Light DOM: Familiar but Vulnerable World
📅
Lesson Objective: Review the basics of the DOM, understand its limitations, and prepare to study Shadow DOM as a solution to these problems.
What is the "Light DOM"?
When we talk about the "Light DOM", we mean the standard, regular DOM tree of the document that we work with every day. It's the very hierarchy of elements that we create in HTML and manipulate using JavaScript.
A simple example of the Light DOM:
<div class="user-card">
<h2>Anna Petrova</h2>
<p>Frontend Developer</p>
<button>Details</button>
</div>
Here, all elements reside in the global document space—this is the "Light DOM".
Strengths of the Light DOM
- Simplicity — requires no additional APIs to work with
- Transparency — the entire structure is visible and accessible
- Compatibility — works everywhere, always
- Ease of Debugging — all elements are immediately visible in the inspector
Problems of the Light DOM
But this simplicity has a downside. Let's look at the main problems with a practical example.
1. Global Styles (CSS Collisions)
Imagine we are creating a button component:
<button class="btn">Click me</button>
<style>
.btn {
background: blue;
padding: 10px 20px;
border-radius: 5px;
}
</style>
But what if on the same page, another developer (or even you yourself) uses the same class?
<style>
/* Somewhere else */
.btn {
background: red; /* Conflict! */
margin: 20px;
}
</style>
Our button unexpectedly turns red. This is a CSS collision—one of the most common problems in web development.
2. Vulnerability of the DOM Structure
What if someone accidentally (or intentionally) changes the structure of our component?
<div class="user-card">
<h2>Anna Petrova</h2>
<p>Frontend Developer</p>
<button>Details</button>
<!-- Someone added an unwanted element -->
<div>Advertisement</div>
</div>
Our component is "broken" from the outside.
3. JavaScript Conflicts
JavaScript also operates in the global space:
const buttons = document.querySelectorAll('button');
// This function affects ALL buttons on the page
If we want to find our specific buttons, we have to use complex selectors and hope they don't break.
4. Scaling Issues
Imagine you created a beautiful "accordion" component. It works great on one page. But as the project grows:
- New developers don't know how to use the component correctly
- Styles start to conflict
- The structure is accidentally changed
- A "specificity war" arises in CSS
Solutions... That Became Problems
Developers came up with various approaches to solve these problems:
Naming Methodologies (BEM, OOCSS)
.user-card__title--large { ... }
.user-card__button--disabled { ... }
Pros: Reduces collisions Cons: Long names, manual work, does not guarantee 100% protection
CSS-in-JS and CSS Modules
import styles from './Button.module.css';
function Button() {
return <button className={styles.primary}>Click</button>;
}
Pros: Automatic style isolation Cons: Require a bundler, harder to debug
Frameworks (React, Vue, Angular)
They offer their own component systems with encapsulation.
Pros: Solve many isolation problems Cons: Bind you to a specific technology, have a learning curve
But What About a Native Approach?
All these solutions work, but they are built on top of the browser. And yet, browsers themselves have been solving these exact same problems for their built-in elements for decades!
Think about it: why does <select> look and work the same on all websites? Why don't its styles conflict with ours? Why can't we accidentally "break" its structure?
The answer: browsers use the Shadow DOM to encapsulate their components.
Summing Up
The Light DOM is the foundation of the web, but it has serious limitations:
- ❌ Global styles (CSS collisions)
- ❌ Vulnerable element structure
- ❌ JavaScript conflicts
- ❌ Scaling problems
Existing solutions help, but they are either imperfect or tie us to specific technologies.
In the next lesson, we will learn how browsers solve these problems using the built-in Shadow DOM, and how we can use this same mechanism to create our own components.