shadow DOM 影子DOM

shadow DOM有以下特点:

  • shadow DOM与外部html是隔离的,外面用外面的样式,里面用里面的样式。
  • 通过DOM API查找DOM元素时,document只能查到外部的元素,shadow DOM内部元素需要通过shadow root元素来查找。

总结起来就是,shadow DOM可以把一部分html代码隔离起来,与外部完全不会互相干扰。

  • 跟iframe不同的地方在于,我们可以调用外部的方法,这样就不会出现弹窗局限于内部窗口的尴尬情况。

影子dom 就像封装了一个标签组件,大部分样式都默认隐藏了.

Shadow DOM 它是HTML的一个规范,它允许在文档(document)渲染时插入一颗DOM元素子树,但是这个子树不在主DOM树中。
它允许浏览器开发者封装自己的HTML标签、css样式和特定的javascript代码、同时开发人员也可以创建类似 <input>、<video>、<audio>等、这样的自定义的一级标签。创建这些标签内容相关的API,可以被叫做 Web Component。

示例一

<!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>shadow DOM</title>
    <style>
      body {
        background-color: #f3f3f3;
      }
      .text {
        color: red;
      }
    </style>
  </head>
  <body>
    <input type="text" placeholder="请输入内容" />
    <div id="div1"><p class="text">这是外面页面的text类文字</p></div>

    <div id="div2">
      <p class="text">这是原本就在html上的dom元素,3秒后添加到shadow-host里</p>
    </div>
    <div id="shadow-host">
      <p>这是shadow-host下的,与shadow-root平级的兄弟元素,将不会显示</p>
    </div>
  </body>
  <script>
    function addShadow() {
      const shadowHost = document.querySelector("#shadow-host");
      // 通过attachShadow创建一个shadow Root
      const shadow = shadowHost.attachShadow({ mode: "open" });

      const shadowDiv = document.createElement("div");
      shadowDiv.setAttribute("class", "texts");
      shadowDiv.innerText = "shadow DOM内部的text类文字";

      // 为shadow dom创建一个style标签,一开始这个style.isConnected为false,把他添加给shadow Root后 isConnected就为true了
      const style = document.createElement("style");
      let newdom = document.createElement("div");
      newdom.setAttribute("class", "new");
      newdom.innerText = "新添加的shadow dom文本";
      let newdp = document.createElement("div");
      newdp.setAttribute("class", "newdp");
      newdp.innerText = "新添加的子节点shadow dom文本,我还会变色";
      style.textContent = `
        .texts {
          color: green
        }
        .new {
          color: blue
        }
        .newdp{
          color:#ff6700
        }
      `;
      // 为shadow dom添加元素
      shadow.appendChild(style);
      console.log(style.isConnected);

      shadow.appendChild(shadowDiv);

      console.log(document.querySelectorAll(".text"));
      console.log(shadow.querySelectorAll(".text"));

      setTimeout(() => {
        shadow.appendChild(document.querySelector("#div2"));
        shadow.appendChild(newdom);
        newdom.appendChild(newdp);
        setTimeout(() => {
          // 将外部引用的样式添加到 Shadow DOM 上
          const linkElem = document.createElement("link");
          linkElem.setAttribute("rel", "stylesheet");
          linkElem.setAttribute("href", "index.css");
          // 将所创建的元素添加到 Shadow DOM 上
          shadow.appendChild(linkElem);
        }, 1000);
      }, 3000);
    }

    setTimeout(() => {
      addShadow();
    }, 2000);
  </script>
</html>

重点知识js引用外部css样式

     
//css文件跟当前文档在同一目录即可     
// 将外部引用的样式添加到 Shadow DOM 上
const linkElem = document.createElement("link");
linkElem.setAttribute("rel", "stylesheet");
linkElem.setAttribute("href", "index.css");
// 将所创建的元素添加到 Shadow DOM 上
shadow.appendChild(linkElem);

实例二

<!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>shadow DOM</title>
    <style>
      .text {
        color: red;
      }
    </style>
  </head>
  <body>
    <div id="div1"><p class="text">这是外面页面的text类文字</p></div>

    <div id="div2">
      <p class="text">这是原本就在html上的dom元素,3秒后添加到shadow-host里</p>
    </div>
    <div id="shadow-host">
      <p>我一秒以后消失</p>
    </div>
  </body>
  <script>
    //结论就是html的样式即使和shadow内部样式重名了也不会相互影响
    function addShadow() {
      const shadowHost = document.querySelector("#shadow-host");
      // 通过attachShadow创建一个shadow Root
      const shadow = shadowHost.attachShadow({ mode: "open" });

      const shadowDiv = document.createElement("div");
      shadowDiv.setAttribute("class", "text");
      shadowDiv.innerText = "shadow DOM内部的text类文字";
      let style = document.createElement("style");
      shadow.appendChild(shadowDiv);
      style.textContent = `
      .text{
        color:blue
      }
      `;
      shadow.appendChild(style);
      console.log(document.querySelectorAll(".text"));
      console.log(shadow.querySelectorAll(".text"));

      setTimeout(() => {
        shadow.appendChild(document.querySelector("#div2"));
      }, 3000);
    }
    setTimeout(() => {
      addShadow();
    }, 1000);
  </script>
</html>

mode

可以使用 Element.attachShadow() 方法来将一个 shadow root 附加到任何一个元素上。它接受一个配置对象作为参数,该对象有一个 mode 属性,值可以是 open 或者 closed

open 表示可以通过页面内的 JavaScript 方法来获取 Shadow DOM,例如使用 Element.shadowRoot 属性:

自定义元素

customElements.define()方法用来注册一个 custom element,该方法接受以下参数:

  • 表示所创建的元素名称的符合 DOMString 标准的字符串。注意,custom element 的名称不能是单个单词,且其中必须要有短横线
  • 用于定义元素行为的  。
  • 可选参数,一个包含 extends 属性的配置对象,是可选参数。它指定了所创建的元素继承自哪个内置元素,可以继承任何内置元素。

发送

otherWindow.postMessage(message, targetOrigin, [transfer]);
  • otherWindow

    其他窗口的一个引用,比如iframe的contentWindow属性、执行window.open返回的窗口对象、或者是命名过或数值索引的window.frames

  • message

    将要发送到其他 window的数据。它将会被结构化克隆算法序列化。这意味着你可以不受什么限制的将数据对象安全的传送给目标窗口而无需自己序列化。

  • targetOrigin

    通过窗口的origin属性来指定哪些窗口能接收到消息事件,其值可以是字符串”“(表示无限制)或者一个URI。在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。这个机制用来控制消息可以发送到哪些窗口;例如,当用postMessage传送密码时,这个参数就显得尤为重要,必须保证它的值与这条包含密码的信息的预期接受者的origin属性完全一致,来防止密码被恶意的第三方截获。**如果你明确的知道消息应该发送到哪个窗口,那么请始终提供一个有确切值的targetOrigin,而不是\。不提供确切的目标将导致数据泄露到任何对数据感兴趣的恶意站点。**

  • transfer 可选

    是一串和message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。

接收

接收时的message 的属性有:

  • data

    从其他 window 中传递过来的对象。

  • origin

    调用 postMessage 时消息发送方窗口的 origin . 这个字符串由 协议、“://“、域名、“ : 端口号”拼接而成。例如 “https://example.org (隐含端口 443)”、“http://example.net (隐含端口 80)”、“http://example.com:8080”。请注意,这个origin不能保证是该窗口的当前或未来origin,因为postMessage被调用后可能被导航到不同的位置。

  • source

    对发送消息的窗口对象的引用; 您可以使用此来在具有不同origin的两个窗口之间建立双向通信。

示例三

<!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>shadow DOM</title>
  <style>
    .text {
      color: red;
    }
  </style>
</head>

<body>
  <z-div text="我是原信息"></z-div>
  <button onclick="sendMsg()">发送事件</button>
  <script defer>
    class ZDiv extends HTMLElement {
      constructor() {
        // 必须首先调用 super 方法
        super();
        const shadow = this.attachShadow({
          mode: "open"
        });
          
        const shadowDiv = document.createElement("div");
        shadowDiv.innerText = this.getAttribute("text");
        shadow.appendChild(shadowDiv);

        window.addEventListener(
          "message",
          function (event) {
            console.log(event);
            shadowDiv.innerText =
              event.origin + " 发来信息:\n " + JSON.stringify(event.data);
          },
          false
        );
      }
    }
    customElements.define("z-div", ZDiv);

    function sendMsg() {
      window.postMessage({
        time: new Date().toLocaleString(),
        url:"www.baidu.com"
      });
      }
  </script>

</html>

 

posted @ 2022-02-23 16:52  波仔、  阅读(357)  评论(0编辑  收藏  举报