zoukankan      html  css  js  c++  java
  • shadow dom

    初识shadow dom

    我们先看个input="range"的表现:

    what amazing ! 一个dom能表现出这么多样式嘛?

    无论是初学者和老鸟都是不肯相信的,于是在好奇心的驱使下,我们打开chrome的开发工具设置

     

    结果发现了input节点里面有我们需要的答案

    input节点里面有他表现需要的dom节点,dom节点里面还有些样式,而这些不容易为人知的节点都在一个#shadow-root的节点下面。

     来张图了解下结构

    document

    这个很好理解,就是我们的正常文档 document 。

    shadow host

    对于一个内部有 shadow-dom 的元素而言,它必然需要一个宿主元素,对于上面的例子而言, <input> 标签,就是 shadow-dom 的宿主元素。

    shadow-root

    通过 createShadowRoot(下文会提及) 返回的文档片段被称为 shadow-root 。它和它的后代元素,都将对用户隐藏,但是它们是实际存在的,在 chrome 中,我们可以通常审查元素去查看它们的具体 DOM 实现。

    在 此例子的 input 中,例如滑块,滑块条等都是 shadow-root 的后代。它们工作时会显示在屏幕上,但他们的 DOM 结构对用户是不可见的。

    contents

    就是上述所说的 input 中各子组件的 DOM 的具体实现。

    shadow dom的api

    下文先从例子出发学习api,再由API了解shadow dom的特性,不失为学习的好方法

    helloWord

    1 <div class="widget"></div>  
    2 <script>  
    3     var host = document.querySelector('.widget');
    4     var root = host.createShadowRoot();
    5     root.textContent = 'hello world!';
    6 </script> 

    页面的效果是

    dom结构也和想象中一样:在div.widget>shadow-root>text(hello world)

    shadowHost.createShadowRoot:在shadowHost内创建ShadowRoot节点
    dom操作
    再来个简单的例子
    <body>
       <div class="widget">Hello, world!</div>3   <script>
         var host = document.querySelector('.widget');
         var root = host.createShadowRoot();
     
         var header = document.createElement('h1');
        header.textContent = '影子标题';
     
         var paragraph = document.createElement('p');
         paragraph.textContent = '影子文本';
     
         root.appendChild(header);
         root.appendChild(paragraph);
       </script>
     </body>

    效果和dom结构也和想象中一样,只是div.widget(shadowHost)内的内容不见了,没错,在有shadowDom的情况下ShadowHost内的真实节点并不会渲染。

    从这个例子发现普通的dom操作在shadowDom也是可行的

    template/content

    在之前的例子里,我们用shadow root里面的内容完全替换掉了shdow host里面的内容。但这种奇技淫巧在实际开发中没什么用。真正有用的是我们可以从shdow host中获取内容,并使用shdow root中的结构将这些内容呈现

    <body>
      <div class="widget">
        ShadowRootText
      </div>
    
      <template class="template">
        <h1>ShadowRoot的内容出现了 <content>ShadowRootText</content> 出现了!</h1>
      </template>
    
      <script>
        var host = document.querySelector('.widget');
        var root = host.createShadowRoot();
        var template = document.querySelector('.template');
        root.appendChild(document.importNode(template.content, true));
      </script>
    </body>

    效果如下

    dom结构也是想象中的那样:shadow host里面的内容都插进了shadow root的content节点里面了

    使用 <content> 标签,我们创建了一个insertion point(<content>),其将 div.pokemon 中的文本projects 出来,使之得以在我们的影子节点 <h1> 中展示。

    插入点十分强大,它允许我们在不改变源代码的情况下改变渲染顺序,这也意味着我们可以对要呈现的内容进行选择。

    select 属性

    <body>
      <div class="widget">
        <span class="contury">中国</span>
        <span class="city">广州</span>
        <span class="province">广东</span>
        <p>广州好很塞车</p>
      </div>
    
      <template class="template">
        <dl>
          <dt>国家</dt>
          <dd><content select=".contury"></content></dd>
          <dt>省份</dt>
          <dd><content select=".province"></content></dd>
          <dt>城市</dt>
          <dd><content select=".city"></content></dd>
        </dl>
        <p><content select=""></content></p>
      </template>
    
      <script>
        var host = document.querySelector('.widget');
        var root = host.createShadowRoot();
        var template = document.querySelector('.template');
        root.appendChild(template.content);
      </script>
    </body>

     页面效果

    dom结构

    在这个例子中,content里面能选择shadow root里面的insertion point能通过select属性选择出shadow host里面的内容,

    并且,页面显示顺序是按照shadow root里面的template顺序显示,并不会按照shadow host的数据显示顺序显示,由此可发现

    shadow root 负责渲染的结构、顺序,shadow host负责渲染的数据(接触过angularJS的directive的你是否看完后是否会微微一笑)

    贪心插入点

    <body>
      <div class="widget">
       <p>shadow host text</p>
      </div>
    
      <template class="template">
    
        <p><content ></content>
        <p><content select=""></content>
        <p><content select="*"></content>
      
      </template>
    
      <script>
        var host = document.querySelector('.widget');
        var root = host.createShadowRoot();
        var template = document.querySelector('.template');
        root.appendChild(template.content);
      </script>
    </body>

    template有三个content,三个content都是匹配shadow host的内容的,那么显示的效果是三行文本还是一行呢?

    结果显示是这样的

    dom结构是这样的

     这是因为这个选择器是贪心的,而且元素只能被选择一次。我们一旦把贪心选择器匹配了shadow host的内容,他就会将所有内容都抓取,不给其他 select 选择器留一点内容。

    样式封装

     shadow DOM 有着独立的样式作用域(shadow boundary),shadow boundary的主要好处就是防止主 DOM 中的样式泄露到 shadow DOM 中。

     这就意味着即使你在主文档中有一个针对全部标签的样式选择器,这个样式也不会不经你的允许便影响到 shadow DOM 的元素。

     来个例子:

    <body>  
      <style>
        p {
          font:18/1.5 SimSum;
        }
      </style>
      <p>this is normal text</p>
      <div></div>
    
      <script>
        var host = document.querySelector('div');
        var root = host.createShadowRoot();
        root.innerHTML = '<style>p { font:bold 30px/60px "microsoft yahei";color:red } </style>' +
                         '<p>this is shadow text</p>'
      </script>
    </body>

    显示的效果是这样的

    normal text的样式并不会对shadow text产生影响,一个困扰前端届多年的独立CSS作用域就这么实现了。

    Amazing,不用担心改这个按钮的样式也会影响到其他页面的按钮了,赞~

    宿主样式(:host)

    我经常把shadow host想象成一栋建筑物的外表。这栋建筑物的内部有组件的全部运行方式,外面有一个好的门面。 许多情况下你可能会想给这门面调整一下样式,这就轮到 :host 选择器出场了。

    <body>  
      <style>
        .widget {
          text-align: center;
        }
      </style>
    
      <div class="widget">
        <p>Hello World!</p>
      </div>
    
      <script>
        var host = document.querySelector('.widget');
        var root = host.createShadowRoot();
        root.innerHTML = '<style>' +
                         ':host {' +
                         '  border: 2px dashed red;' +
                         '  text-align: left;' +
                         '  font-size: 28px;' +
                         '} ' +
                         '</style>' +
                         '<content></content>';
      </script>
    </body>  

    显示的效果是下图

    文字28px,还有个小边框,文字是居中对齐的,并没有按照shadow root里面



  • 相关阅读:
    多态
    多继承
    传宗接代——继承
    解决vue项目更新版本后浏览器的缓存问题
    escape()、encodeURI()、encodeURIComponent()三种编码方式的区别
    epoll使用总结
    探讨c/c++的指针
    基于linux的pthread_t封装一个Thread类
    unix高并发编程中遇到的问题和笔记
    面向对象分析与设计 实验七
  • 原文地址:https://www.cnblogs.com/peace1/p/ShadowDom.html
Copyright © 2011-2022 走看看