• Skip to primary navigation
  • Skip to main content
  • Skip to primary sidebar
  • Skip to footer
Codemotion Magazine

Codemotion Magazine

We code the future. Together

  • Magazine
  • Dev Hub
    • Community Manager
    • CTO
    • DevOps Engineer
    • Backend Developer
    • Frontend Developer
    • Web Developer
    • Mobile Developer
    • Game Developer
    • Machine Learning Developer
    • Blockchain Developer
    • Designer – CXO
    • Big Data Analyst
    • Security Manager
    • Cloud Manager
  • Articles
    • Stories
    • Events
  • Sign In
Home » Dev Hub » Frontend Developer » The DOM and its shadow
Frontend Developer

The DOM and its shadow

Learn how to manipulate elements of a web page by guaranteeing proper encapsulation. How? By using the Shadow DOM.

Last update December 5, 2019 by Andrea Chiarelli

The DOM and its shadow

Nowadays, most frontend web developers build their UI with well-known libraries and frameworks such as React, Angular, Vue, and so on. Maybe many developers have almost forgotten how to dynamically create HTML elements by accessing the Document Object Model (DOM). In fact, almost all these cool libraries abstract the DOM and provide their own model to produce HTML elements. Some even virtualize the DOM to optimize access.

Why then should a modern frontend web developer keep the DOM in mind?  After all, it sounds like low-level stuff that UI libraries handle efficiently.

That’s certainly how it looks, and for the most part, how it is, but the DOM always remains present.  Any UI model eventually maps your code to the DOM, retaining its congenital defects in the process.

The problem with the DOM

Most UI libraries have made us accustomed to thinking of the UI as a puzzle of components. This architecture is really awesome and promotes reuse, one of the most avidly-pursued principles of software development. On the other hand, the DOM has a major flaw that hinders composition and reuse: it is globally accessible.

It doesn’t matter where your UI element comes from – React, Angular, plain JavaScript… When your element is appended to the DOM, it can be accessed from any JavaScript code or CSS rules, and its internal structure can be changed.

The DOM is like a global variable. And all we know how bad global variables are.

As a result, when you add your UI component to the DOM, the UI may lose its alleged reusability due to the DOM’s lack of encapsulation.

Let’s explore this point with an example.

Say you have created an awesome banner to invite your JavaScript community to your next great meetup. You want to distribute this banner to other websites so that they can all spread the word. You can build this banner in many ways, with pure HTML or using the UI library you prefer. It doesn’t really matter – regardless of how you build, the output will be a few elements on the DOM.

For our purposes here, assume that this is the JavaScript code that creates your banner:

function createBanner(elementId) {
const banner = document.getElementById(elementId);
banner.innerHTML = `
<style>
#box {
-webkit-box-shadow: 5px 5px 15px 5px #000000;
box-shadow: 5px 5px 15px 5px #000000;
font-family: Verdana, Geneva, sans-serif;
padding: 10px;
text-align: center;
width: 80%;
}
h3 {
font-size: 25px;
color: #5F36FF;
}
#description {
font-weight: bold;
font-size: 18px;
color: #6B6B6B;
}
#finalMessage {
font-weight: bold;
color: #6B2E63;
text-decoration: underline solid rgb(68, 68, 68);
}
</style>
<div id="box">
<h3>Join our awesome JavaScript meetup</h3>
<p id="description">You will learn the latest amazing way to forget JavaScript.<p>
<p id="finalMessage">Don't miss it!</p>
</div>
`;
}

We’ve used plain JavaScript here: a simple function that takes the identifier of an element in the DOM and attaches some markup to it. If this code is stored in a file named banner.js, you can use it within an HTML page in the following way:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="banner.js"></script>
<script>
window.onload = function() {
createBanner("banner")
};
</script>
</head>
<body>
<div id="banner"></div>
</body>
</html>

This page will be rendered as shown in the following image:

Now, say that one of your ‘friend’ websites uses a CSS rule like the following:

h3 {
text-transform: uppercase;
background: #0A59FA;
}

As a result of applying this rule, your banner will look like this:

Not so good – this is not how you wanted your banner to look.

Even worse, imagine that somewhere in one of those other websites there is a JavaScript code like this:

document.getElementById("description").innerText = "I don't like JavaScript!";

Your banner will now look like this:

Eventually, you will decide that your banner is not safe. Its inner structure can be manipulated like any other element in the DOM.

How can you protect your work from accidental or intentional changes from the external environment? How can you guarantee the encapsulation of your work?

The Shadow DOM

Traditionally, the DOM encapsulation problem has been tackled by using iframes. Put all the code for your banner into an iframe and it is protected. That solution solves one problem, but it introduces others. In fact, there is a simple and elegant solution for the encapsulation problem, and its name is Shadow DOM. It is a set of standard APIs that enable the encapsulation of a portion of the DOM inside the DOM of an HTML page. This allows you to create a sort of private DOM for an HTML element. In other words, it enables a local DOM in contrast with the global DOM, just like global and local variables. The trick is very simple. Look at this snippet of code:

function createBanner(elementId) {
const banner = document.getElementById(elementId);

banner.attachShadow({mode: "open"});

banner.shadowRoot.innerHTML = `
<style>
// The same stuff as before...
</div>
`;
}

We just invoked the attachShadow() method of the element to attach the Shadow DOM. This method is available for most HTML elements. It attaches a node that represents the root of the local DOM to the banner element. This root node can be accessed through the shadowRoot property. The content that was previously directly appended to the banner element is now appended to the shadowRoot property.

Open and closed Shadow DOM

As you’ve seen, a very little change to your code makes your banner impenetrable. Well actually, not quite.

You noticed that we passed a parameter to the attachShadow() method.

banner.attachShadow({mode: "open"});

This is a literal object with the mode property set to the open value. This object allows you to specify the creation mode of your Shadow DOM and it is mandatory. In the example above, we asked for an open Shadow DOM. This means that the elements that build up our banner will be protected against accidental interference from CSS rules or DOM manipulation, but they remain accessible from the outside world. In fact, you can access the elements through the shadowRoot property, as shown below:

const bannerRoot = document.getElementById("banner").shadowRoot;
bannerRoot.getElementById("description")
.innerText = "I don't like JavaScript!";

So, the internal parts of your banner are still accessible, but it has to be done intentionally. By attaching the Shadow DOM with the open mode you chose for this to be possible. If on the other hand you don’t want access to be possible, if you want the internal workings of your banner to be invisible to the rest of the world, attach a Shadow DOM with the closed mode:

banner.attachShadow({mode: "closed"});

Now, your banner will be impregnable!

Conclusion

As we saw inthis article, the DOM most developers know is not the best structure for hosting a composable UI. Its open accessibility exposes your UI elements to possible undesirable conflicts. This factor clashes with the principle of composability and reusability we all want for our UI elements. We have explored an example of such possible conflict, but we have also discovered that a simple and standard solution is available. The lack of encapsulation of the classic DOM can be overcome by using the Shadow DOM. A few JavaScript statements can save your work from external interference and make your UI elements really reusable.

Tagged as:HTML JavaScript

How Search Engines Work
Previous Post
Config management and cost optimisation for serverless computing
Next Post

Primary Sidebar

Subscribe to our newsletter

I consent to the processing of personal data in order to receive information on upcoming events, commercial offers or job offers from Codemotion.
THANK YOU!

Whitepaper & Checklist: How to Organise an Online Tech Conference

To help community managers and companies like ours overcome the Covid-19 emergency we have decided to share our experience organizing our first large virtual conference. Learn how to organise your first online event thanks to our success story – and mistakes!

DOWNLOAD

Latest

we love founders

Thinking Like a Founder – meet Chad Arimura

CTO

Move Over DevOps, It’s Time for DesignOps and the Role of UX Engineer

Designer - CXO

developer

The State of AI in 2021

Machine Learning Developer

Machine Learning on the Network Edge

The Rise of Machine Learning at the Network Edge

Machine Learning Developer

robot programming

Are You Ready for the FaaS Wars?

Backend Developer

Related articles

  • Front-end development with Angular & NgRx
  • Build your pipeline project with… style!
  • Reactive Forms in Angular
  • Nexi Dev Training Program: an attendee’s experience
  • Welcome to the world of micro-apps
  • Editorial web layouts: how Gutenberg would have used CSS (perhaps)

Subscribe to our newsletter

I consent to the processing of personal data in order to receive information on upcoming events, commercial offers or job offers from Codemotion.
THANK YOU!

Footer

  • Learning
  • Magazine
  • Community
  • Events
  • Kids
  • How to use our platform
  • About Codemotion Magazine
  • Contact us
  • Become a contributor
  • How to become a CTO
  • How to run a meetup
  • Tools for virtual conferences

Follow us

  • Facebook
  • Twitter
  • LinkedIn
  • Instagram
  • YouTube
  • RSS

DOWNLOAD APP

CONFERENCE CHECK-IN

© Copyright Codemotion srl Via Marsala, 29/H, 00185 Roma P.IVA 12392791005 | Privacy policy

  • Learning
  • Magazine
  • Community
  • Events
  • Kids
  • How to use our platform
  • About Codemotion Magazine
  • Contact us
  • Become a contributor
  • How to become a CTO
  • How to run a meetup
  • Tools for virtual conferences

Follow us

  • Facebook
  • Twitter
  • LinkedIn
  • Instagram
  • YouTube
  • RSS

DOWNLOAD APP

CONFERENCE CHECK-IN