Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
196 views
in Technique[技术] by (71.8m points)

shadow dom - slot selector limit in web component

slot is good to make a reusable web component, however, it has a limit so far. What I faced is the style problem. You just can't define the style inside a component, even you know what the inject content's structure would be.

Details found from my post in github here

I write a component, and try to inject content through slot from the outside and try to add style to the specific content in the component's shadow root. Demo

HTML file

  <my-navbar>
    <ul>
      <li>link1</li>
      <li>link2</li>
      <li>link3</li>
      </ul>
  </my-navbar>

JS file

customElements.define('my-navbar', class extends HTMLElement {
  constructor () {
    super();
    const sr = this.attachShadow({ mode: 'open' });
    sr.innerHTML = `
      <style>
      /*worked*/
      ::slotted(ul)
      {
        color:green;
      }
      /*
      Suppose I know the outside content is "ul li", and I directly define the 
      style after they injected into component's slot. However, it just doesn't 
      work because the slotted selector is just a compound selector. It can only 
      affect the first layer 'ul'. It can't affect the child dom 'li' */
      ::slotted(ul li)
      {
        color:red;
      }
      </style>
      <slot></slot>
    `;
  }
});

However, it just doesn't work directly because you just can't use a complex selector for ::slot(simple_selector) Reason

I found an indirect solution, and that's to re-append the outside content into the slots inside the component's shadow root. Demo

HTML file

  <my-navbar>
    <!--a dom defined a slot property-->
    <ul slot='t'>
      <li>link1</li>
      <li>link2</li>
      <li>link3</li>
      </ul>
    <!--A dom not define slot property-->
    <span>1234</span>
  </my-navbar>

JS file

customElements.define('my-navbar', class extends HTMLElement {
  constructor () {
    super();
    const sr = this.attachShadow({ mode: 'open' });
    sr.innerHTML = `
      <style>
      ul li
      {
        color:red;
      }
      </style>
      <slot name='t'></slot>
      <slot ></slot>
    `;
    // Do something later...
    setTimeout(this.appendOutsideSlotContentIntoInsideSlot.bind(this), 1000)
  }

    appendOutsideSlotContentIntoInsideSlot()
  {

    // Insert outside dom element which has define slot property into the specify slot inside the shadow root
    debugger;
     for (let objIndex=0;objIndex<this.children.length;) 
     {
        var obj=this.children[objIndex];
        if(obj.slot){
          var slot=this.shadowRoot.querySelector('slot[name='+obj.slot+']');
          if(slot)
          {
              slot.appendChild(obj);
              continue;
           }
       }
       objIndex++;
    }


    // Insert the rest dom which has not define slot property values into the anonymous slot
    var defaultSlot=Array.prototype.slice.call(this.shadowRoot.querySelectorAll('slot')).filter(function(el){ return !el.name})[0];

    debugger;
    if(defaultSlot)
      {
         while (this.children.length>0) 
        {
          defaultSlot.appendChild(this.children[0])

        }

      }
  }
});

Well, it works for the contents which have defined the slot property, but doesn't work again with content that has no slot property.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

With the exception of a few inheritable rules the contents of the slot are not supposed to be directly affected by your component's shadow CSS. They are designed to allow the CSS outside of your component to be in control.

That is by design.

This is similar to the protection given to elements within the shadow DOM not being affected by external CSS.

Read the section Styling distributed nodes found here: https://developers.google.com/web/fundamentals/web-components/shadowdom#stylinglightdom

You are only allowed to change CSS rules for the top level elements inside the slot. And you are even limited to what you can do to that. All child elements are controlled by the CSS outside of the shadow DOM.

In the example below you will see that we can change the color and background color of the top level elements, or the <ul> tags:

customElements.define('my-navbar', class extends HTMLElement {
  constructor () {
    super();
    const sr = this.attachShadow({ mode: 'open' });
    sr.innerHTML = `
      <style>
      ::slotted(ul)
      {
        color: blue;
      }
      
      ::slotted(.bold) {
        font-weight: bold;
        background-color: #222;
        color: #FFF;
      }
      
      ::slotted(.italic) {
        font-style: italic;
        background-color: #AAA;
        color: #000;
      }

      ::slotted(*)
      {
        color: red;
      }
      </style>
      <slot></slot>
    `;
  }
});
<my-navbar>
  <ul class="bold">
    <li>link1</li>
    <li class="italic">link2</li>
    <li>link3</li>
  </ul>
  <ul class="italic">
    <li>link1</li>
    <li class="bold">link2</li>
    <li>link3</li>
  </ul>
</my-navbar>

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...