What is shadow DOM
<video controls autoplay name="media">
<source id="mp4" src="trailer.mp4" type="video/mp4">
</video>Copy the code
This is the simplest video TAB, with default buttons like volume. There is no trace at all in the source code. So where do these nodes come from? This is called shadow DOM, and the visual control looks like this in the browser:
Note that #shadow-root is grayed out. This is the browser’s way of indicating that content representing other parts of the page in the Shadow DOM does not affect the interior (it can be accessed in a specific way, as discussed later).
Content specifically refers to CSS selectors and javascript code
In short, Shadow DOM is a new specification for HTML that allows developers to encapsulate HTML components (similar to VUE components, which extract separate HTML, CSS, and JS parts).
Why shadow DOM
The name of Bootstrap is familiar to you, and the code is generally as follows:
<ul class="media-list">
<li class="media">
<div class="media-left">
<a href="#">! [] (...). </a> </div> <div class="media-body">
<h4 class="media-heading">Media heading</h4>
</div>
</li>
</ul>Copy the code
It is very simple and easy to use, but you do not have a thorough understanding of these things, often the structure becomes complex, are a pile of templates, modification is a difficult problem, affecting the whole body. The advantage of shadow DOM in this case is that you can write templates like this
<bootstrap-media-list> <a href="#">! [] (...). </a> <h4 class="media-heading">Media heading</h4>
</bootstrap-media-list>Copy the code
Of course, you need some JS and CSS to do this
How to use
Let’s run an example
<div class="widget">Hello, world! </div> <script> var host = document.querySelector('.widget');
var root = host.createShadowRoot();
root.textContent = 'I'm in your div! ';
</script>Copy the code
The host node
shadow host
shadow root
How do I render the content in the host node
Rendering only the content in the shadow root is rarely useful, but having the freedom to render the content in the host node makes the page more flexible. We need the content tag
<div class="pokemon"> </div> <template class="pokemon-template">< h1> A wild <content></content> appeared! </h1> </template> <script> var host = document.querySelector('.pokemon');
var root = host.createShadowRoot();
var template = document.querySelector('.pokemon-template');
root.appendChild(document.importNode(template.content, true));
</script>Copy the code
<content>
The insertion point
.pokemon
select
<div class="host""> <p> Zhuge Liang evolved from great kindness and compassion. </p> <span class="name"> </span> </div> <template class="root-template">< dl> <dt> Name </dt> <dd><content select=".name"></content></dd>
</dl>
<p><content select=""></content></p>
</template>
<script>
var host = document.querySelector('.host');
var root = host.createShadowRoot();
var template = document.querySelector('.root-template');
root.appendChild(template.content);
</script>Copy the code
select
<content select=""></content>
<content></content>
<conent select=""></conent>
<content select="*"></content>
Style rendering and wrapping
Let’s start with a simple example
<style>
button {
font-size: 18px;
font-family: 'Chinese Script'; } </style> <button> <div></div> <script> var host = document.querySelector('div');
var root = host.createShadowRoot();
root.innerHTML =
'<style>button { font-size: 24px; color: blue; } </style>'+
;
</script>Copy the code
shadow DOM
The embodiment of scoping
(:host) selector
The :host is a pseudo-class selector that selects the host node, so we can extend the example above
<style> p { font-size: 12px; } </style> <p> my text </p> <button> my button </button> <template class="shadow-template">
<style>
:host(p) {
color: green;
}
:host(button) {
color: red;
}
:host(*) {
font-size: 24px;
}
</style>
<content select=""></content>
</template>
<script>
var root1 = document.querySelector('p').createShadowRoot();
var root2 = document.querySelector('button').createShadowRoot();
var template = document.querySelector('.shadow-template');
root1.appendChild(document.importNode(template.content, true));
root2.appendChild(document.importNode(template.content, true));
</script>Copy the code
- P tag font size is 12px = shadow styles have less priority than page styles
:host
Any valid selector can be used in the selector,*
Apply to all- Theming can be achieved by mounting different hosts to render different content
The above theme is not complete, and the selection is based only on mount elements, that is.parent > .child
But we can still pass:host-context
implementation.parent < .child
The following<div class="serious"> <p class="serious-widget"> serious-widget </p> </div> <div class="playful"> <p class="playful-widget"> playful-widget </p> </div> <template class="widget-template"> <style> :host-context(.serious) { width: 250px; height: 50px; background: tomato; } :host-context(.playful) { width: 250px; height: 50px; background: deepskyblue; } </style> <content></content> </template> <script> var root1 = document.querySelector('.serious-widget').createShadowRoot(); var root2 = document.querySelector('.playful-widget').createShadowRoot(); var template = document.querySelector('.widget-template'); root1.appendChild(document.importNode(template.content, true)); root2.appendChild(document.importNode(template.content, true)); </script>Copy the code
The above results are pretty good for dynamic component building
Ps: pseudo class, pseudo element selector can also be used directly, the effect and normal node in the same
(:: Content) selector
Keep content and presentation separate when using shadow DOM, meaning that text should come from the page and not be buried in the Shadow DOM template. So we need to render the distributed nodes in the template.
<div class="widget"< p style = "max-width: 100%; clear: both; min-height: 1em; </button> </div> <template class="widget-template">
<style>
::content > button {
color: white;
background: tomato;
border-radius: 10px;
border: none;
padding: 10px;
}
</style>
<content select=""></content>
</template>
<script>
var root = document.querySelector('.widget').createShadowRoot();
var template = document.querySelector('.widget-template');
root.appendChild(document.importNode(template.content, true));
</script>Copy the code
Break scope (::shadow)
We can use ::shadow in the mount node, for example
<style>
.sign-up::shadow #username{
font-size: 20px;
border: 1px solid red;
}
</style>
<div class="sign-up"></div>
<template class="sign-up-template">
<style>
#username{
font-size: 12px;
}
</style>
<div>
<input type="text" id="username" placeholder="Username">
</div>
</template>
<script>
var root = document.querySelector('.sign-up').createShadowRoot();
var template = document.querySelector('.sign-up-template');
root.appendChild(document.importNode(template.content, true));
</script>Copy the code
The downside is we can only penetrate one floor, but we still have one artifact!
Multilayer penetration (/deep/)
<style>
#foo /deep/ button {
color: red;
}
</style>
<div id="foo"></div>
<template>
<div id="bar"></div>
</template>
<script>
var root1 = document.querySelector('#foo').createShadowRoot();
var template = document.querySelector('template');
root1.appendChild(document.importNode(template.content, true));
var root2 = root1.querySelector('#bar').createShadowRoot();
root2.innerHTML = '';
</script>Copy the code
The difference between the javascript
- Data is not block-level and is still mounted in
window
on
- Event redirection (Events that were originally bound to the Shadow DOM node are redirected, so they look like they are bound to the host node)
<input id="normal-text" type="text" value="I'm normal text"> <div id="host"></div> <template> <input id="shadow-text" type="text" value="I'm shadow text"> </template> <script> var root = document.querySelector('#host').createShadowRoot(); var template = document.querySelector('template'); root.appendChild(document.importNode(template.content, true)); document.addEventListener('click'.function(e) { console.log(e.target.id + ' clicked! '); }); </script>Copy the code
You can see that events at the shadow node are brokered by the host node.
Blocking events
Blocked at the root of the shadow node when listening for:
aborterror
select
change
load
reset
reset
resize
scroll
selectstar
<input id="normal-text" type="text" value="I'm normal text"> <div id="host"> <input id="distributed-text" type="text" value="I'm distributed text"> </div> <template> <div><content></content></div> <div> <input id="shadow-text" type="text" value="I'm shadow text"> </div> </template> <script> var root = document.querySelector('#host').createShadowRoot(); var template = document.querySelector('template'); root.appendChild(document.importNode(template.content, true)); document.addEventListener('select'.function(e) { console.log(e.target.id + ' text selected! '); }); </script>Copy the code
The root of the event shadow node is prevented from bubbling up to the Ducoment, so it cannot listen.
Distribution of nodes
Distribution nodes are those that previously projected the content of the host node with the
tag. Distribution nodes do not block as above because this is just a projection of the actual content that is still mounted on the host node.