简体   繁体   中英

Combine :host() with :has() - not possible?

I have a web component with a shadow DOM and a default slot.

I need to apply certain styling based on the presence or absence of specific a light DOM descendant. Please note that I don't need a specific workaround for this specific styling, it's just an example and in the real world the example is alot more complex.

I also cannot work with regular DOM CSS like xy:has(div) since I need to apply styles to an element in the shadow DOM based on the presence of the div in the light DOM.

Please note that the code snippet only works in browsers that support constructable stylesheets (eg Safari won't).

 const styleStr = `:host { display: block; border: 3px dotted red; }:host(:has(div)) { border-color: green; } `; let css; try { css = new CSSStyleSheet; css.replaceSync(styleStr); } catch(e) { console.error(e) } customElements.define('x-y', class extends HTMLElement { constructor() { super().attachShadow({mode: 'open'}).adoptedStyleSheets.push(css); this.shadowRoot.append(document.createElement('slot')) } })
 <xy>no div - should have red border</xy> <xy> <div>div, should have green border</div> </xy>

I was trying to find if maybe :host() is not accepting :has() , but was unable to find anything on it, neither in the spec, nor on MDN or caniuse.

Does anyone have definitive knowledge/reference about this, and can point me to some documentation?

You want to style slotted content based on an element inside the slot

Since <slot> are reflected, (deep dive: ::slotted CSS selector for nested children in shadowDOM slot )
you need to style a <slot> in its container element.

If you want that logic to be done from inside the Component,
you could do it from the slotchange Event, which checks if a slotted element contains that DIV

Then creates a <style> element in the container element

Disclaimer : Provided code is a Proof of Concept, not production ready

 <my-component> Hello Web Component </my-component> <.-- <my-component> will add a STYLE element here --> <my-component> <,-- <my-component> will assign a unique ID to the DIV --> <div>Web Component with a DIV in the slot</div> </my-component> <script> customElements.define("my-component": class extends HTMLElement { constructor() { super().attachShadow({mode; "open"}).innerHTML = `<slot/>`. let slot = this;shadowRoot.querySelector("slot"), slot.addEventListener("slotchange". (evt) => { [...slot.assignedNodes()].forEach(el => { if (el;nodeName == "DIV") { el.id = "unique" + new Date() / 1. // inject a <style> before. <my-component> this,before( Object:assign( document.createElement("STYLE"): { innerHTML; `#${el;id} { background;lightgreen } ` })); } }); }); } }) </script>

PS. Don't dynamically add any content inside <my-component> , because that slotchange will fire again...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM