Search code examples
csscss-selectorsweb-componentpseudo-element

Applying selection style to slotted elements?


I'm trying to apply ::selection style on slotted elements within a web component.

Here is a small code to illustrates the issue:

window.customElements.define('x-widget', class extends HTMLElement {
    constructor() {
        super();

        this.attachShadow({mode: 'open'});

        const template = document.createElement('template');
        template.innerHTML = `
            <div>TEMPLATE</div>
            <div><slot></slot></div>
        `;

        const style = document.createElement('style');
        style.textContent = `
            :host {
                display: content;
                contain: content;
                color: red;
            }
    
            :host::selection, ::selection {
                background: red;
                color: white;
            }
    
            ::slotted(::selection) {
                background: red;
                color: white;
            }
        `;

        this.shadowRoot.appendChild(style);
        this.shadowRoot.appendChild(template.content.cloneNode(true));
    }
});
<x-widget>CONTENT1</x-widget>
<x-widget><div>CONTENT2</div></x-widget>

Here is a demo: https://jsfiddle.net/ov4xmqsr/

The selection style is applied to all texts excepts <div>CONTENT2</div>. Is there a way to use pseudo-element selectors within the component?


Solution

  • You can't, because slotted content is NOT moved to shadowDOM, it remains in ligthDOM.

    You style slotted content in ligthDOM (in this case the main DOM)

    For very detailed answer see: ::slotted CSS selector for nested children in shadowDOM slot

    I added extra CSS to show:

    • Using variables (that penetrate shadowDOM) to declare a colors ones

    • Using a #selectable DIV wrapper selects both custom elements
      See for yourself what the x-widget ::selection selector would do

    Select all text:
    <div id=selectable>
      <x-widget>CONTENT1</x-widget>
      <x-widget><div>CONTENT2</div></x-widget>
    </div>
    <style>
      body {
        --selectionBackground: green; --selectionColor: white;
        font-size: 2em;
      }
      #selectable ::selection {
        background: var(--selectionBackground); color: var(--selectionColor);
        font-weight: bold;
      }
    </style>
    <script>
      window.customElements.define('x-widget', class extends HTMLElement {
        constructor() {
          const template = document.createElement('template');
          template.innerHTML = `<div>TEMPLATE</div><div><slot></slot></div>`;
          const style = document.createElement('style');
          style.textContent = `
                ::selection { /* colors only template text, not slot content */
                    background: var(--selectionBackground);
                    color: var(--selectionColor);
                }
                ::slotted(*) {  /* selectors select HTMLElements! */
                    color: red; /* CONTENT1 is TEXT, NOT an HTMLElement! */
                }`;
          super().attachShadow({mode: 'open'})
                 .append(style, template.content.cloneNode(true));
        }});
    </script>