美文网首页
浅谈 shadow dom 中的 template 和 slot

浅谈 shadow dom 中的 template 和 slot

作者: 楼兰之风 | 来源:发表于2016-09-20 11:29 被阅读1522次

    最近一个内部系统可视化设计器的布局在用户升级 chrome 到 53 版本之后,里面的坑位增加按钮消失了,经过排查发现,坑位的标签使用了slot,而坑位中的按钮是通过 css 的 empty 伪类实现的,即坑位为空时,出现增加按钮,但现在为什么突然无效了呢?

    slot的名字换成其他标签名,发现样式顿时生效,于是猜测是slot作为特殊标签被 chrome 设置了特定的行为,经过查阅资料发现slotshadow dom 中的内置标签。

    现在我们来了解下 shadow dom 以及与其相关的两个特殊标签 templateslot

    何为shadow dom

    shadow dom,顾名思义,影子节点,它是 web components 规范的一个子集,主要为了解决dom对象的封装,通常的dom,其js行为和css样式极易受到别的代码的干扰,但shadow dom规定了只有其宿主才可定义其表现,外部的api是无法获取到shadow dom中的东西。

    由于shadow dom是影子元素,因此其必须捆绑一个宿主元素,宿主元素事实上成为了“傀儡”,宿主元素的内容将被隐藏,而shadow dom的内容将展示出来,以下是一个例子:

    html:
    <div id="con">
        没什么卵用的文字
    </div>
    
    js:
    var host = document.querySelector('#con');
    var root = host.attachShadow({mode:'open'});//为宿主附加一个影子元素
    
    root.innerHTML = "我来自shadow dom";//为影响元素附上内容,shadow dom的api和普通dom的大致相同
    
    

    最终效果:

    我来自shadow dom
    

    可以看到,宿主的内容确实被掩盖了,然而通过chrome的devtools,可以看到宿主的原内容以及背后的shadow dom:

    shadow dom中的template

    前面说了,shadow dom可以实现dom的隔离,比如样式的封装,那么如何实现呢?shadow规定了一种名为template的标签,这种标签类似我们经常用的<script type='tpl'>,它不会被解析为dom树的一部分,template的内容可以被塞入到shadow dom中并且反复利用,在template中可以设置style,但只对这个template中的元素有效,看下示例:

    html:
    <style>
    span {
      background-color:blue;/*设置页面所有span背景为蓝色,然而对shadow dom没什么卵用*/
    }
    </style>
    <div id="con">
        没什么卵用的文字
      </div>
       <template id="tpl">
         <style>
           span {
             color:red;
           }
         </style>
         <span>hello world</span>
       </template>
    
    js:
    var host = document.querySelector('#con');
    var root = host.attachShadow({mode:'open'});
    
    var con = document.getElementById("tpl").content.cloneNode(true);
    
    root.appendChild(con);
    
    

    效果截图:

    <span style='color:red'>hello world</span>

    可以看到,template的内容被塞入到宿主,并且其文案被设置为红色,而body 中对 span 设置为蓝色背景却没有生效;另外这里要注意document.getElementById("tpl").content中的content属性,它是template标签的特有属性,你可以通过嗅探该属性来判断浏览器是否支持shadow dom和template标签。

    shadow dom的slot标签

    由于shadow dom的内容会掩盖宿主的内容,那么现在问题来了,我就是想把宿主的内容显示出来怎么办?

    最新的shadow dom草案支持了一个叫slot标签的东西,slot是一个插槽,一个坑位,可以在template中定义坑位,然后宿主中的内容可以标记属于哪一个坑位,这样一个萝卜一个坑,宿主的内容就会被正确地插入到template所标记的位置去,还是来看一个例子:

    html:
    <div id="con">
        没什么卵用的文字
        <span slot="main1">
          坑位1
        </span>
        <span slot="main2">
          坑位2
        </span>
        没什么卵用的文字 </div>
      <template id="tpl">tpl begin
        <slot name="main1">
        </slot>
        <slot name="main2">
        </slot>
        tpl end
          </template>
    
    js:
    var host = document.querySelector('#con');
    var root = host.attachShadow({mode:'open'});
    
    var con = document.getElementById("tpl").content.cloneNode(true);
    
    root.appendChild(con);
    

    最终的效果是:

    tpl begin 坑位1 坑位2 tpl end

    可以看到,宿主中的两个span分别插入到了其标记的slot坑位中。在slot出现之前,仍然可以实现类似的功能,只不过标签名叫content

    结语

    现在我们来解释一下文章开头出现的问题的原因,由于 slot标签仅仅是一个占位符而已,其最终会被宿主标记了该位置的内容替换(注意是替换,而不是插入),因此没必要对slot标签设置样式,这就是为啥chrome 53忽略其样式的原因,无独有偶,最新版的ios 10默认浏览器也会隐藏slot,因为slot中并不需要显示任何东西。

    借鉴vue的思想,我们在编译阶段把slot编译成div,从而解决了这个问题,顺便提醒一下,shadow dom是非常新的技术,目前还处在试验阶段,而且标准还不确定,请不要在生产环境使用。

    相关文章

      网友评论

          本文标题:浅谈 shadow dom 中的 template 和 slot

          本文链接:https://www.haomeiwen.com/subject/xeqoettx.html