Slot

slot插槽,占位. 参考vue

mdn: https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/slot

参考vue:https://cn.vuejs.org/guide/components/slots.html

下面示例,自定名为"my-card"自定义组件,可以自定义标题和内容.

<body>
    <div id="app">
        <my-card></my-card>
        <my-card>自定义内容</my-card>
        <my-card>
            <span slot="title">自定义标题</span>
            自定义内容
        </my-card>
    </div>
  
  
    <!-- 组件模板 -->
    <template id="card-template">
        <style>
            :host{
                display: block;
                border: 1px solid #ccc;
                margin: 10px;
            }
           
            .title,.content{
                margin: 0;
                padding: 5px;
            }
            .title{
                background-color: #ccc;
            }
        </style>
        <div >
            <!-- 标题插槽 -->
            <header class="title"><slot name="title">默认标题</slot></header>
            <!-- 内容插槽 -->
            <div class="content">
                <slot>默认内容</slot>
            </div>
        </div>
    </template>
    <script>
        customElements.define(
                'my-card',
                class extends HTMLElement {
                    constructor() {
                        super();
                        this.attachShadow({mode: 'open'});
                        const template=document.querySelector('#card-template');
                        this.shadowRoot.appendChild(template.content.cloneNode(true));
                    }
                }
        )
    </script>
</body>

image-20231020012153392

结构

image-20231020113000811

与slot关联的元素为slotable Element

组件中定义slot

  • 匿名slot
 <slot>默认内容</slot>
  • 有名slot

    <slot name="title"></slot>
  • 默认值

    <slot name="title">默认标题</slot>
  • 共用多个

    <slot name="title">默认标题1</slot>
    <slot name="title">默认标题2</slot>

注意: 如果组件中没有定义任何slot或匿名slot,插入到组件的内容将不显示.

<tempalte>
 </template>

<my-card>
  <p>不显示</p>
</my-card>

可以利用这个特性,临时存储一下内容,然后通过修改slot来显示.

slot传参

可以传任意标签内容

  • 匿名slot

    <my-card>
      <p>任意内容</p>
    </my-card>
  • 具名slot

    <my-card>
      <h3 slot="title">标题</h3>
      <p>任意内容</p>
    </my-card>
  • 纯文本内容

    <my-card>
      <slot slot="title">标题</slot>
      <p>任意内容</p>
     </my-card>
  • 同时传多个

    <my-card>
      <h3 slot="title">标题1</h3>
      <h4 slot="title">标题2</h4>
      <p>任意内容</p>
    </my-card>

样式

::slotted() 选择器,slot中有内容. 用于区分已插入和未插入.

注意: ::slotted(*) 不能选择纯文本内容,最好用标签包起来.

<template>
  <style>
    ::slotted(*){
      color:red;
    }
    ::slotted(h2){
      color:blue;
    }
  </style>
  <header><h2 name="title">默认标题</h2></header>
</template>

组件中获取slot内容

通过assignedElements 获取分配给slot内容.

 <script>
        customElements.define(
                'my-card',
                class extends HTMLElement {
                    constructor() {
                        super();
                        this.attachShadow({mode: 'open'});
                        const template=document.querySelector('#card-template');
                        this.shadowRoot.appendChild(template.content.cloneNode(true));
                        const slotElements = this.shadowRoot.querySelectorAll('slot');
          
                        for(let slotElement of slotElements){
                            const assignedElements = slotElement.assignedElements({ flatten: true });
                            // 可以有多个
                            console.log(assignedElements[0]?.innerHTML);
                        }
                    }
                }
        )
</script>

事件

slotchange 事件可以监听slot的内容变化.

注意: slotchange 事件不能取消. 只能监听slot的添加删除,属性的变化.

<div id="app">
        <my-card id="my-card">
            xxx
        </my-card>

        <button id="btn">添加内容</button>
    </div>
    <!-- 组件模板 -->
    <template id="card-template">
        <style>
            :host{
                display: block;
                border: 1px solid #ccc;
                margin: 10px;
            }
           
            .title,.content{
                margin: 0;
                padding: 5px;
            }
            .title{
                background-color: #ccc;
            }
            ::slotted(*){
                color:red;
            }
            ::slotted(h2){
                color:blue;
            }
        </style>
        <div >

            <!-- 内容插槽 -->
            <div class="content">
                <slot>默认内容</slot>
            </div>
        </div>
    </template>  
<script>
        customElements.define(
                'my-card',
                class extends HTMLElement {
                    constructor() {
                        super();
                        this.attachShadow({mode: 'open'});
                        const template=document.querySelector('#card-template');
                        this.shadowRoot.appendChild(template.content.cloneNode(true));
                        const slot = this.shadowRoot.querySelector('slot');
                        // 事件
                        slot.addEventListener("slotchange",function(e){
                            console.log(this.assignedNodes());
                        })

                    }
                }
        )
    </script>
    <script>
        const btn=document.querySelector('#btn');
        const card=document.querySelector('#my-card');
        // 添加内容
        btn.addEventListener('click',function(){
           card.innerHTML="";
           const p=document.createElement("p");
           p.innerText="内容"+Math.random();
   
           card.appendChild(p);
        })
    </script>
</body>

组件中的slotable Element变化,都会触发slotchange事件.

  btn.addEventListener('click',function(){
    
    // 1.添加slot属性
    // card.querySelector("p").setAttribute("slot","content");
    // 2.修改slot
    // card.querySelector("[slot='title']").setAttribute("slot","content");
    // 3. 删除slot 
    //card.querySelector("[slot='title']").removeAttribute("slot");
    // 4. 删除原元素
    // card.querySelector("[slot='title']").remove();
    // 5. 设置innertHTML
    // card.innerHTML="";
    // 6. 删除slot本身
    // card.shadowRoot.querySelector("slot[name='title']").remove();
  })

动态创建slot

// 动态创建slot
slot=document.createElement('slot');
slot.addEventListener("slotchange",function(e){
    console.log(this.assignedNodes());
})
card.shadowRoot.appendChild(slot);

原作者:阿金
本文地址:https://hi-arkin.com/archives/web-components-4.html

标签: template web components slot

(本篇完)

评论