问题
Here is a fiddle demonstration of the custom element: https://jsfiddle.net/c4bLo097/5/
Here is the code from the fiddle:
JavaScript:
window.customElements.define('test-element', class TestElement extends HTMLElement {
constructor() {
super()
let contents = `
<style>
:host {
display: block;
}
:host([hidden]) {
display: none;
}
</style>`
// convert string to nodes
let template = document.createElement('template')
template.innerHTML = contents
// create shadow
this.attachShadow({mode: 'open'})
// insert nodes
this.shadowRoot.appendChild(template.content.cloneNode(true))
}
})
HTML:
<test-element>
This element should have a natural height.
<div style="height: 300px;"></div>
I should be able to see this text on a green background.
</test-element>
CSS:
test-element {
width: 200px;
background: green;
}
If you inspect the <custom-element>
with your devloper tools, you should see that the shadow is there. But my element will not render its height correctly.
Here is an example fiddle of what I'm trying to achieve: https://jsfiddle.net/9483s1qb/2/
Thank you
回答1:
When you add shadowDOM the content of your element becomes "lightDOM"..
It is no longer displayed in the main DOM and not part of your shadowDOM.
As Supersharp explained in 2017:
The Light DOM is simply the plain old DOM tree inside a HTML element.
The term is only used in context of Web Components (Custom Elements WITH Shadow DOM)
I suppose the normal DOM was redefined as Light in contrast with Shadow.The WHATWG specs call it the shadowroot host's node tree, or light tree:
The Shadow DOM is the added DOM that recovers, masks, or replaces the normal DOM,
as explained in the article from Google.
You have to transfer lightDOM content to shadowDOM yourself:
with SLOTs:
https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_templates_and_slotsor with code
customElements.define('my-element', class extends HTMLElement {
constructor() {
super()
//create shadowDOM (thus creating lightDOM) and append Template
this.attachShadow({mode: 'open'})
.append(document.getElementById(this.nodeName).content.cloneNode(true))
}
connectedCallback(){
//append content from lightDOM
this.shadowRoot.append(...this.querySelectorAll('DIV'));
}
})
my-element{
border: 1px dashed blue;
}
<template id="MY-ELEMENT">
<style>
:host {
display: block;
font-size:20px;
}
h4{
background:yellow;
margin: .5em 0;
}
div{
background:lightcoral;
}
</style>
<h4><slot name="title"></slot></h4>
</template>
<my-element>
<!-- begin lightDOM because my-element has shadowDOM -->
<span slot="title">whole SPAN is slotted</span>
<div>I am appended</div>
<div>appended too</div>
<p>I remain (invisible) in lightDOM</p>
<!-- end lightDOM -->
</my-element>
<my-element>
<!-- begin lightDOM because my-element has shadowDOM -->
<span slot="title">slotted too</span>
<div>appended again</div>
I remain (invisible) in lightDOM
<!-- end lightDOM -->
</my-element>
Snippet Notes:
The template is cloned
the append in the
connectedCallback
moves the content,
if you want to leave the original in lightDOM (eg. use it as a datastore)
you have to clone it like done with thetemplate
.the
slot="title"
moves the wholespan
(including the span!) to its slot in shadowDOMDo try yourself:
In the JSFiddle playground: https://jsfiddle.net/CustomElementsExamples/bzvLcxfe/What happens with an unnamed:
<slot></slot>
?What happens if you change the
<span>
to a<div>
Be Aware!! Browsers behave different!
You might require a setTimeout
in the connectedCallback
when the (main) DOM has not been instantiated yet:
connectedCallback() {
let savedHTML = this.outerHTML;
//append content from lightDOM
const append = (selector) =>
this.shadowRoot.append(...this.querySelectorAll(selector), savedHTML);
if (this.outerHTML.includes("timeout"))
setTimeout(() => append('DIV'))
else
append('DIV');
}
JSFiddle playground: https://jsfiddle.net/CustomElementsExamples/bzvLcxfe/
in the first yellow Element DIVs are appended without a
setTimeout
in the second yellow Element DIVs are appended with a
setTimeout
the
outerHTML
(known in theconnectedCallback
) is appended in both Elements.
Result in (Chromium) Chrome\Edge\Opera:
- lightDOM is not available,
setTimeout
required to delay code till Event loop has finished, and lightDOM is available.
Result in (Gecko) FireFox:
- lightDOM is available, no
setTimeout
required
Result in Safari:
- I don't know (yet) please let me know
Notes:
This happens (in Chromium Browsers) when the
customElements.define
is run before your DOM is ready. If you change the JSFiddle and execute the<script>
block after the DOM elements are created, all is fine.
(but most of the time you load libraries ASAP to prevent FOUCsrequestAnimationFrame
in theconnectedCallback
has the same behaviour as asetTimeout
. More details: https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoopyour DOM might also be ready when you load your elements async
<script src=elements.js async ></script>
来源:https://stackoverflow.com/questions/60838990/why-does-my-shadow-dom-break-my-custom-element