Web Components详解-Shadow DOM插槽

前言

插槽实际上也属于组件通信的一种方式,但是由于其强大的api和实用性,我将其单独拆开来介绍。

定义

Slot(插槽)是Web Components中一个重要的特性,它允许在组件内部定义占位符,以便父组件可以向其中插入内容。换句话说就是将子组件或者标签传入父组件中,最终达到在父组件外部实现子组件的效果

基本用法

slot属于Shadow DOM的一部分,在原生html中并不支持插槽的写法,所以我们必须将标签放在Shadow DOM中。

插槽标签的写法

html 复制代码
<slot name="标签slot属性值"></slot>

需要传入的标签必须在对应的自定义标签中定义

html 复制代码
<my-custom-element>
    <div slot="标签slot属性值">标签</div>
</my-custom-element>

完整示例参考下面的代码

html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ShadowDOM</title>
</head>

<body>
    <my-custom-element>
        <header slot="header">header</header>
        <main slot="content">
            <span>content</span>
        </main>
        <footer slot="footer">footer</footer>
    </my-custom-element>
    <div id="slots">
        <slot name="header"></slot>
        <slot name="content"></slot>
        <slot name="footer"></slot>
    </div>

    <script>
        const elemName = "my-custom-element"
        const ele = document.querySelector(elemName)
        const slots = document.querySelector("#slots")
        class MyCustomElement extends HTMLElement {
            constructor() {
                super()
                this.attachShadow({ mode: 'open' });
                this.shadowRoot.appendChild(slots)// 插槽必须在shadowDOM中
            }
        }
        customElements.define(elemName, MyCustomElement)
    </script>
</body>

</html>

具名插槽

具名插槽实际上就是上面的用法,在自定义标签中使用<div slot="标签slot属性值">标签</div>以及在影子DOM中使用<slot name="标签slot属性值"></slot>的形式达到效果

DOM的结构如下

匿名插槽

匿名插槽又叫默认插槽,当有slot标签不设置name属性,并且在自定义标签中存在未设置slot属性的其他标签,即具名插槽的name属性以及slot属性均未设置,此时第一个slot标签就会承载自定义标签中的全部匿名标签,参考下面的代码

html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ShadowDOM</title>
</head>

<body>
    <my-custom-element>
        <header>header</header>
        <main>
            <span>content</span>
        </main>
        <footer>footer</footer>
    </my-custom-element>
    <div id="slots">
        <!-- my-custom-element中的匿名标签都会放在第一个slot标签中 -->
        <slot></slot>
        <slot></slot>
        <slot></slot>
    </div>

    <script>
        const elemName = "my-custom-element"
        const ele = document.querySelector(elemName)
        const slots = document.querySelector("#slots")
        class MyCustomElement extends HTMLElement {
            constructor() {
                super()
                this.attachShadow({ mode: 'open' });
                this.shadowRoot.appendChild(slots)
            }
        }
        customElements.define(elemName, MyCustomElement)
    </script>
</body>

</html>

在页面中的DOM结构如下,三个标签都被放在了第一个slot

后备插槽

当我们使用图片标签图片却加载失败时往往会给图片增加一个alt文字提醒,或使用默认图片。类似的插槽也有这种效果。当我们使用具名插槽并且找不到对应的标签时,可以在slot标签中增加标签以便默认状态展示,比如

html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ShadowDOM</title>
</head>

<body>
    <button>显示后备插槽</button>
    <my-custom-element>
        <div id="content" slot="content">具名插槽</div>
    </my-custom-element>
    <div id="slots">
        <slot name="content">
            <div style="color: lightcoral;">后备插槽</div>
        </slot>
    </div>

    <script src="./main.js"></script>
    <script>
        const content = document.querySelector("#content")
        document.querySelector("button").addEventListener("click", () => {
            content.remove()// 当自定义标签my-custom-element中没有标签时,则显示后备插槽标签
        })
    </script>
</body>

</html>

当我们将自定义标签中对应的插槽删掉时,插槽元素就会显示后备插槽标签

插槽更新

当我们插入,修改,移除插槽时会触发slotchange事件钩子,类似于用于监听DOM更新的MutationObserver,来看看下面的代码

html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ShadowDOM</title>
</head>

<body>
    <my-custom-element-change>
        <div id="content" slot="content">插槽</div>
        <div id="content2" slot="content2">插槽2</div>
    </my-custom-element-change>
    <div id="slots">
        <slot name="content"></slot>
    </div>
    <script src="./main.js"></script>
    <script>
        const slot_box = `<div id="box1">slot</div>`
        const slot_content = slots?.querySelector('[name="content"]')
        slot_content.addEventListener("slotchange", console.log);
        customElements.define('my-custom-element-change', class extends MyCustomElement { });// 初始化触发slotchange
        setTimeout(() => slot_content.name = "content2", 1000)// 替换slot绑定的元素,触发slotchange
        setTimeout(() => slot_content.remove(), 2000)// 删除插槽触发slotchange
    </script>
</body>

</html>

上面的代码主要是一个简单的slotchange回调演示,创建自定义元素后,slot会初始化触发slotchange,1秒后修改slot内容触发slotchange,最后2秒后删除slot再次触发回调

插槽api

  • assignedSlot:它是标签的一个属性,通常在slot的目标标签使用,用于获取目标标签绑定的slot标签
  • assignedNodes:assignedNodes是slot上的函数,使用该方法可以返回所有分配的节点,包括文本节点和元素节点
  • assignedElements:assignedElements是slot上的函数,它会返回分配的元素节点
html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ShadowDOM</title>
</head>

<body>
    <my-custom-element>
        text1
        <header slot="header">header</header>
        text2
        <main slot="content">
            content
            <span>content1</span>
            <span>content2</span>
            <span>
                <div>
                    <span>content3</span>
                </div>
            </span>
        </main>
        <footer slot="footer">footer</footer>
    </my-custom-element>
    <div id="slots">
        <slot name="header">header</slot>
        <slot name="content">content</slot>
        <slot name="footer">footer</slot>
    </div>

    <script src="./main.js"></script>
    <script>
        const elems = document.querySelectorAll('[slot]')
        const slotElems = slots.querySelectorAll('[name]')
        elems.forEach(it => console.log(it.assignedSlot))
        slotElems.forEach(slot => {
            const nodes = slot.assignedNodes();
            const elements = slot.assignedElements();
            nodes.forEach(console.log);
            elements.forEach(console.log);
        })
    </script>
</body>

</html>

总结

插槽是Web Components中的一个重要特性,用于在自定义组件内部定义占位符,使得父组件可以向其中插入内容,从而实现了组件之间的高度灵活的通信和组合。通过合理使用具名插槽、匿名插槽以及后备插槽,开发者可以实现高度定制化的组件组合。同时,插槽的事件和 API 提供了对插槽内容的监测和操作,为构建更加动态的用户界面增添了便利性。

以上就是文章全部内容了,如果觉得文章不错的话,还望三连支持一下,感谢!

相关代码

myCode: 基于js的一些小案例或者项目 - Gitee.com

参考文章

Shadow DOM 插槽,组成

相关推荐
时光少年2 分钟前
Android 视频分屏性能优化——GLContext共享
前端
23.14 分钟前
【Java】NIO 中的多路复用(Selector / Channel)机制
java·面试·nio
IT_陈寒20 分钟前
JavaScript开发者必知的5个性能杀手,你踩了几个坑?
前端·人工智能·后端
跟着珅聪学java23 分钟前
Electron 精美菜单设计
运维·前端·数据库
日光倾24 分钟前
【Vue.js 入门笔记】闭包和对象引用
前端·vue.js·笔记
EstherNi26 分钟前
左右两侧定位的效果,vue3
javascript·vue.js
一只程序熊30 分钟前
UniappX 未找到 “video“ 组件,已自动当做 “view“ 组件处理。请确保代码正确,或重新生成自定义基座后再试。
前端
林小帅32 分钟前
【笔记】xxx 技术分享文档模板
前端
雾岛心情37 分钟前
【HTML&CSS】HTML为文字添加格式和内容
前端·css·html
心.c1 小时前
如何在项目中减少 XSS 攻击
前端·xss