DZone
Web Dev Zone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
  • Refcardz
  • Trend Reports
  • Webinars
  • Zones
  • |
    • Agile
    • AI
    • Big Data
    • Cloud
    • Database
    • DevOps
    • Integration
    • IoT
    • Java
    • Microservices
    • Open Source
    • Performance
    • Security
    • Web Dev
DZone > Web Dev Zone > Using the Shadow DOM to Isolate Styles on a DOM You Don't Own

Using the Shadow DOM to Isolate Styles on a DOM You Don't Own

Shadow DOM is perfect for when you need to embed a widget in a DOM you don't own or have access to. Here's how to use it.

Riley Napier user avatar by
Riley Napier
·
Dec. 21, 21 · Web Dev Zone · Tutorial
Like (4)
Save
Tweet
7.41K Views

Join the DZone community and get the full member experience.

Join For Free

There have been many times in my career of building web apps and frontend experiences that I have needed to embed a widget in a DOM that I did not build or have access to —  for example, embedding e-commerce widgets in a CMS or building Chrome extensions to augment a given website. One of the biggest frustrations I've had is the "style collision." In my development environment, all the widgets look great, but the second I add the widget to a customer’s page… everything is broken! 

Why Does This Happen?

The difficulty with embedding widgets in a DOM that you don't own is every DOM is going to reference different default fonts and colors. It’s normal for a CSS stylesheet to look like:

CSS
 
body {
   font-family: my-awesome-font;
   font-size: 16px;
   color: #eee;
   line-height: 16px;
   box-sizing: border-box;
}

When I embed my widget into the body of this customer’s page, it will inherit the above styles. While sometimes this is okay, many times it will break the beautiful widget I designed because I designed the widget with a different font size or padding.

Classic Solutions

Historically, we have had two solutions to this problem:  

  1. Use an iFrame.
  2. Be very explicit with your styles.

While both solutions can work, they both have rather frustrating aspects you will have to deal with. Below I'll go over a few examples of what I’ve done in the past and then cover what this blog post is all about: the fancy, new, futuristic way to make composite user interfaces, or the Shadow DOM. <insert sinister laugh here>

Working with iFrames

With an iFrame, I have no control over the element’s size, so the consumer of my widget will have to accurately carve out space in their DOM for my iFrame. If my widget is dynamic in size, this is going to cause all sorts of problems with scrolling and positioning.

The second issue we find with iFrames is the communication between the iFrame and the parent. While I can now use CustomEvents, I will need to build out an event system for both the parent and the iFrame context. This can be frustrating if the client already has a built-in SDK.  It’s basically building a mini SDK for the SDK for iFrame communication.  

Finally, and maybe the most simplistic issue, is my consumer can’t tweak ANY of the styles in my iFrame. This can lead to inconsistent user interfaces and is just a bad experience all around.

While iFrames will work, they are outdated, difficult to communicate with, and, if your widget is dynamic in size or you need any kind of customization, **good luck.**

CSS Specificity

The more common approach I have taken is to just be super specific with my CSS — so namespace everything! This can be tedious and will most likely need to be tweaked for each new client integrating your components. The QA process for pushing out an update to the widget is going to be difficult too.  There are so many ways clients can use CSS and have it break your integration.

So what can I do?

Enter the Shadow DOM

The Shadow DOM is an API for DOM encapsulation, and we all know how important encapsulation is.

Shadow DOM allows hidden DOM trees to be attached to elements in the regular DOM tree — this Shadow DOM tree starts with a shadow root underneath which can be attached to any elements you want in the same way as the normal DOM.

The most basic approach to creating a shadow is to attach it to any DOM element:

JavaScript
 
const shadow = element.attachShadow({mode: 'open' || ‘closed’});

The mode 'open' or 'closed' allows you to specify whether or not the page’s JavaScript can interact with the Shadow DOM.  Open means it can interact and closed means it cannot.

After I’ve created my shadow element, I can append to it just like any normal DOM node.

JavaScript
 
const shadow = element.attachShadow({mode: 'open' || ‘closed’}); 
const styleNode = document.createElement(“style”);
style.textContent = `
    background: blue;
    font-size: 18px;
`;

shadow.appendChild(styleNode);


const contentNode = document.createElement(“div”);
contentNode.textContent = `Hello World`;
shadow.appendChild(contentNode);

The above code will create a shadow node, append a node style to the Shadow DOM, and append a div saying Hello World. The style will now be isolated, only affecting the shadow tree and not contaminating the parent. Success!

However, the above example is very verbose and simple and is showing us just the bare metal implementation of the Shadow DOM. It’s only scratching the surface of what the Shadow DOM can do. It’s not all that complicated and it’s pretty well supported right now.

Shadow DOM V1

Shadow DOM with React

I’d like to take a quick moment to highlight a really useful package that I’ve used in the past and really gave me the feeling of “WOW, I might actually be able to use this in production.”

React Shadow makes working with the shadow DOM with React easy as pie!  The example I used above with 'react-shadow' will look like this:

JavaScript
 
import root from 'react-shadow';

export default () =>  (
    <root.div>
        <div>Hello World</div>
        <style type="text/css">
            background: blue;
            font-size: 18px;
        </style>
    </root.div>
);

Now, if that isn’t magic, I don’t know what else is. So, take a step with me into the future. Let’s not be afraid of our Shadow DOM and let’s make beautiful composite user experiences together!

IFrame (video format)

Published at DZone with permission of Riley Napier. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Cypress: The Future of Test Automation! Advantages and Disadvantages
  • Functional vs. Non-Functional Requirements: The Full Guide, Definitions, and Technical Examples
  • Debugging Java Collections Framework Issues in Production
  • 9 Strategies to Improve Your Software Development Process

Comments

Web Dev Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • MVB Program
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends:

DZone.com is powered by 

AnswerHub logo