classMyElementextendsHTMLElement{
staticgetobservedAttributes(){
return['disabled'];
}
constructor(){
constshadowRoot=this.attachShadow({mode:'open'});
shadowRoot.innerHTML=`
<style>
.disabled{
opacity:0.4;
}
</style>
<divid="container"></div>
`;
this.container=this.shadowRoot('#container');
}
attributeChangedCallback(attr,oldVal,newVal){
if(attr==='disabled'){
if(this.disabled){
this.container.classList.add('disabled');
}
else{
this.container.classList.remove('disabled')
}
}
}
}
以后无论何时disabled的attribute被改变时,this.container上面的名为disabled的class都会显示或隐藏,它是ShadowDOM的内在元素。接下去让我们看一下。
ShadowDOM
使用ShadowDOM,自定义元素的HTML和CSS完全封装在模块内。这意味着元素将以单个的HTML标签出现在文档的DOM树种。其外部的构架将会放在#shadow-root。
实际上一些原生的HTML元素也使用了ShadowDOM。例如你再一个网页中有一个<video>元素,它将要成为一个单独的标签展现,但它也将显示播放和暂停视频的控件,当你在浏览器开发软件中查看video标签,是看不到这种控件。
这种控件实际上就是video元素的ShadowDOM的一个别,因此默认状况下是隐藏的。要在Chrome中显示ShadowDOM,进入开发者软件中的Preferences中,选中ShowuseragentShadowDOM。当你在开发者软件中重新查看video元素时,你就可以看见该元素的ShadowDOM了。
ShadowDOM还提供了局部作用域的CSS。所有的CSS都只应用于模块本身。元素将只继承最小数量从模块内部定义的CSS,甚至可以不从内部继承任何CSS。不过你可以暴露很多CSS属性,以便用户对模块进行样式修改。这可以缓解许多CSS问题,同时依然允许自定义模块样式。定义一个Shadowroot:
constshadowRoot=this.attachShadow({mode:'open'});
shadowRoot.innerHTML=`<p>Helloworld</p>`;
这定义了一个带mode:open的Shadowroot,这意味着可以再开发者工具找到它并与之交互,配置暴露出的CSS属性,抛出的事件。同样也可以定义mode:closed,会受到与之相反的体现。
你可以使用使用HTML字符串添加到innerHtml的property属性中,或者使用一个<template>去给Shadowroot添加HTML。一个HTML的template基本是惰性的HTML片段,你可以定义了提前使用。在实际插入DOM前,它是不可见也不可解读的。这意味着定义在外部的任何资源都能够获得,任何外部定义的CSS和Java只有当它被插入DOM中时,才会被执行。当模块的HTML根据其状况出现设置时,例如你可以定义多个<template>元素,然后按照模块的状况去插入某些元素,这样可以轻松的设置模块的HTML部分,并不应该设置单个DOM节点。
当Shadowroot被创建以后,你可以使用document对象的所有DOM方法,例如this.shadowRoot.querySelector去查找元素。组件的所有颜色都被定义在style标签内,如果你想使用一个常规的<link>标签,你也可以获得内部风格。除此之外,还可以使用:host选择器对模块本身进行风格设定。例如,自定义元素默认使用display:inline,所以即使你想要将模块展示为款元素,你可以这么做:
:host{
display:block;
}
这还允许你进行上下文的风格化。例如你想要借助disabled的attribute来改变组件的背景是否为红色:
:host([disabled]){
opacity:0.5;
}

默认状况下,自定义元素从周围的CSS中继承一些属性,例如色彩和图标等,如果你想清空组件的初始状况以及将模块内的所有CSS都设定为默认的初始值,你可以使用:
:host{
all:initial;
}
特别重要,需要留意的一点是,从内部定义在模块本身的风格优先于使用:host在ShadowDOM中定义的风格。如果你这么做
my-element{
display:inline-block;
}
它将会被覆盖
:host{
display:block;
}
不需要从内部去颠覆自定义元素的风格。如果你期望客户可以修改模块的个别颜色,你可以暴露CSS变量去超过这个效果。例如你想让客户可以选用模块的背景色彩,可以暴露一个叫—background-color的CSS变量。假设今天有一个ShadowDOM的根节点是<div>
#container{
background-color:var(--background-color);
}
目前用户可以在模块的内部设定它的背景色彩
my-element{
--background-color:#ff0000;
}
你还可以在模块内修改一个默认值,以防用户没有修改
:host{
--background-color:#ffffff;
}
#container{
background-color:var(--background-color);
}
其实你还可以让客户设定任何的CSS变量,前提是这种变量的命名要以—开头。
通过提供局部的CSS、HTML,ShadowDOM解决了全部CSS可能带给的一些弊端,这样难题往往造成不断地添加颜色表,其中包括了愈发越多的选择器和覆盖。ShadowDOM似的标记和颜色捆绑到自己的模块内,而不应该任何工具和命名约定。你再也不用害怕新的class或id会与现有的任何一个冲突。
除此之外,还可以借助CSS变量设定web模块的外部形状,还可以将HTML注入到WebComponents中。
通过slots组成
组合是借助ShadowDOM树与用户提供的标记组合在一起的过程。这是借助<slot>元素完成的,该元素基本是ShadowDOM的占位符,用来呈现用户提供的标记。用户提供的标记又可以作为lightDOM。合成会将lightDOM和ShadowDOM合并作为一个新的DOM树。
比如,你可以创建一个<iamge-gallery>组件,并提供标准的img标签成为模块要展现的内容:
<image-gallery>
<imgsrc="foo.jpg"slot="image">
<imgsrc="bar.jpg"slot="image">
</image-gallery>
组件目前即将获得两个提供的图像,并且使用slots将他们渲染到模块的ShadowDOM中。注意到slot=”image”的attribute,这告诉了模块需要要在ShadowDOM的哪些位置渲染他们。例如这种
<divid="container">
<divclass="images">
<slotname="image"></slot>
</div>
</div>
当lightDOM中的节点被分发到ShadowDOM中时,得到的DOM树看上去是这么的:
<divid="container">
<divclass="images">
<slotname="image">
<imgsrc="foo.jpg"slot="image">
<imgsrc="bar.jpg"slot="image">
</slot>
</div>
</div>
就像你看见的,任何客户提供的具备slot属性的元素,都将在slot元素中展现。而slot元素具备name属性,其值与slot属性的值对应。<select>元素的工作方与此完全相似,你可以在开发这软件中查看(查看修改在上面)
它接受用户提供的option元素,并将他们展现到下拉菜单中。带有name属性的slot被称为具名slot,但是这个属性不是需要的。它仅用于必须将内容展现在特定位置时使用。当一个或多个slot没有name属性时,将根据用户提供内容的顺序在其中展现。当用户提供的内容超过slot时,slot可以提供默认的展示。
看一下<image-gallery>的ShadowDOM:
<divid="container">
<divclass="images">
<slot></slot>
<slot></slot>
<slot>
<strong>Noimagehere!</strong><--fallbackcontent-->
</slot>
</div>
</div>
即使你再只给两个image的话,最后的结果如下:
<divid="container">
<divclass="images">
<slot>
<imgsrc="foo.jpg">
</slot>
<slot>
<imgsrc="bar.jpg">
</slot>
<slot>
<strong>Noimagehere!</strong>
</slot>
</div>
</div>
通过slot在ShadowDOM中展现的元素被称为分发节点。这些部件被插入前的形状也即将被用于人们插入后。在ShadowDOM中,分发节点可以借助::sloted()来获得额外的样式
::slotted(img){
float:left;
}
::sloted()可以接受任何有效的CSS选择器,但它没法选择老牌节点,例如::slotedd(sectionimg)的状况,将不会作用于thiscontent
<image-gallery>
<sectionslot="image">
<imgsrc="foo.jpg">
</section>
</image-gallery>在Java中使用slots
你可以借助Java与slots进行交互去检测哪个结点被分发到什么slot,哪些slot被插入了元素,以及slotchange事件。
要找出这些元素终于被分发给对应的slots可以使用slot.assignedNodes()假如你还想查看slot的默认内容,你可以使用slot.assignedNodes({flatten:true})要找出这些slot被分发的元素,可以使用element.assignedSlot当slot内的节点出现改变,即添加或删除节点时,将会出发slotchange事件。要切记的是,只有当slot节点自身改变才能触发webcomponents可以删除吗,而这种slot节点的子节点并不会触发。
本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/sanxing/article-120724-2.html
人家做生意上来先给你个大金戒指