当下的前端界,各种MV*框架大行其道,而在这之前则是jQuery一家独大。jQuery是操作dom的利器,并且将dom封装成了JQ对象从而增加了一些新的api及特性。而MV*们则更加的“粗暴”,直接屏蔽了底层的dom操作,直接通过框架将数据映射到模板上。不仅如此,MV*们更是为中大型项目的组织构建提供了良好的基础——模块抽离和代码复用。
无论是vue等还是各家的小程序,都提供了template
和 slot
,其主要的作用是将一部分逻辑相似的代码进行复用,同时也允许在复用的时候提供一些定制化配置。
抛掉这些,我们的原生html是否提供了类似的功能?
shadowDOM的介绍
Web components的一个重要特性是封装——可以将html标签结构、css样式和行为隐藏起来,并从页面上的其他代码中分离开来,这样不同的功能不会混在一起,代码看起来也会更加干净整洁。其中,shadowDOM接口是关键所在,它可以将一个隐藏的、独立的DOM添加到一个元素上。
shadowDOM, 顾名思义,影子节点。影子节点需要依附在一个宿主节点上,并且也会将宿主节点中的内容给替换掉(注意这里是 替换 而不是 添加)。同时在影子节点内部是一个独立的作用域,并不会受外部的样式影响。
shadowDOM的用法
<head>
.....
<style>
p {
color: yellowgreen;
}
</style>
</head>
<body>
<div id="host" >
这里是shadowROM的宿主
</div>
<p>这里是shadowRoot外面的内容</p>
</body>复制代码
创建一个template
,同时给这个template
中的p
标签设定新的样式
<template id="shadowContent" >
<style>
p {
color: red;
}
</style>
<p>这里是template中的内容</p>
</template>复制代码
host.attachShadow({mode: 'open'})
可以给host
节点依附一个shadowDOM,并且返回这个有的shadowDOM节点。
document.querySelector('#shadowContent').content
中的 content
是 template
的特有属性
最后将克隆的节点添加到shadowDOM下。
<script>
let host = document.querySelector('#host')
let shadowRoot = host.attachShadow({mode: 'open'})
let shadowContent = document.querySelector('#shadowContent').content.cloneNode(true)
shadowRoot.appendChild(shadowContent)
</script>复制代码
最终的效果:
在上图中,我们可以看到template
模板,是作为一个document-fragment
存在的。这表明它是存在于内存中而不是主dom树上,它并不会因为template
中的内容变化而引起文档的reflow。
配合slot使用
直接上代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>测试shadowDOM</title>
<style>
p {
color: yellowgreen;
}
</style>
</head>
<body>
<div id="host" >
<p slot="slot1" >我是要放到占位符里的内容</p>
这里是shadowROM的宿主
</div>
<p>这里是shadowRoot外面的内容</p>
<!-- 以下为模板相关 -->
<template id="shadowContent" >
<style>
p {
color: red;
}
</style>
<!-- 以下是一个占位符 -->
<slot name="slot1" ></slot>
<p>这里是template中的内容</p>
</template>
<script>
let host = document.querySelector('#host')
let shadowRoot = host.attachShadow({mode: 'open'})
let shadowContent = document.querySelector('#shadowContent').content.cloneNode(true)
shadowRoot.appendChild(shadowContent)
</script>
</body>
</html>复制代码
效果:
值得注意的是:slot
的样式是文档中的样式,而不是template
中的样式
结尾
其实shadowDOM
已经被大家所广泛使用,最常见的就是一些视频播放网站的定制的video标签就是利用了shadowDOM
。
最后,新手上路,如有争议,欢迎讨论。