从原生web组件到框架组件源码(二)

innerHTML outerHTML textContent innerText 区别

<div id="test">
  <span>sdsdsdsd <span>555</span></span>
  bbbb
</div>
  • innerHTML

​ 从对象的起始位置到终止位置的全部内容,包括Html标签

<span>sdsdsdsd <span>555</span></span>bbbb

  • innerText

    从起始位置到终点位置的内容,去掉HTML内容,并且如果里面有多个标签或者迭代子代都是去除标签的

    sdsdsdsd 555 bbb

  • outerHTML

    除了包含innerHTML的全部内容外,还包含对象标签本身

  • textContent

    跟innerText 返回的结果一样

重要

根据W3C标签,尽量用innerHTML,textContent,而少用innerText,outerHTML

动态访问DOM

方法 描述
document.createElement(``tag) 创建并返回一个HTML标签tag
element.appendChild(``child) 添加的元素child里面element
element.insertAdjacentHTML(``pos,``html) 将代码插入htmlelement
element.insertAdjacentElement(``pos,``node) 将元素node插入element

正常情况下我们使用的时候

class AppElement extends HTMLElement {

  name = this.getAttribute("name") || "Desconocido";

  connectedCallback() {
    const element = document.createElement("div");
    element.className = "element";
    this.appendChild(element);

    const innerElement = document.createElement("div");
    innerElement.textContent = this.name;
    element.appendChild(innerElement);
  }
}
数据类型 特定类型 标签 描述
HTMLElement HTMLDivElement <div> 看不见的分隔层(块中)。
HTMLElement HTMLSpanElement <span> 不可见的分隔层(在线)。
HTMLElement HTMLImageElement <img> 图片。
HTMLElement HTMLAudioElement <audio> 音频容器。

inserAdjacentHTML

element.insertAdjacentHTML(position, text);

position

插入内容的位置

  • 'beforebegin':元素自身的前面。
  • 'afterbegin':插入元素内部的第一个子节点之前。
  • 'beforeend':插入元素内部的最后一个子节点之后。
  • 'afterend':元素自身的后面。

text

  • 解析为html,并插入到DOM树上,字符串类型
// 原为 <div id="one">one</div> 
var d1 = document.getElementById('one'); 
d1.insertAdjacentHTML('afterend', '<div id="two">two</div>');

// 此时,新结构变成:
// <div id="one">one</div><div id="two">two</div>

insertAdjacentElement(position, element);

个人觉得这个用的比较多

position

DOMString 表示相对于该元素的位置;必须是以下字符串之一:

  • 'beforebegin': 在该元素本身的前面.
  • 'afterbegin':只在该元素当中, 在该元素第一个子孩子前面.
  • 'beforeend':只在该元素当中, 在该元素最后一个子孩子后面.
  • 'afterend': 在该元素本身的后面.
<div id="one">one</div>
<script>
  let span = document.createElement('span');
  span.textContent='3333';
  let one=document.querySelector('#one');
  one.insertAdjacentElement('beforebegin',span)
</script>

content.cloneNode(deep)

deep为 true 的时候,就是创建一个深层克隆,为false就是浅拷贝

我们要目标这个适用于自定义元素

// 创建一个自定义组件
const template = document.createElement("template");
template.innerHTML = `
  <div class="element">
    <div class="name"></div>
  </div>`;

// 讲自定义组件拷贝到AppElement 里面
class AppElement extends HTMLElement {

  name = this.getAttribute("name") || "Desconocido";

  connectedCallback() {
    const markup = template.content.cloneNode((true));
    markup.querySelector(".name").textContent = this.name;
    this.appendChild(markup);
  }
}

shadow DOM

影子DOM

在javascript不同框架出现后,他们设计了Virstual DOM: 页面DOM的内存中副本,可以直接管理更改,以便后面转换为文档的真实DOM,目的是加快优化页面的DOM更改

例如:react 引入虚拟DOM,已检测更改(树之间的差异), 更新受影响的节点,然后再将其传递给真实的dom

语法

const div = document.createElement("div");
const shadow = div.attachShadow({ mode: "open" });
div.shadowRoot === shadow; // true

mode 定义了shadow DOM的封装模式,创建shadow Dom元素将具有一个属性,通过.shadowRoot 访问

封装css

我们知道css具有全局特性,Shadow DOM 中css不会影响文档的css,也不会使全局css传递到shaDow DOM 的css,就是具有沙箱形式

  <style>
    h1{
      color:red;
    }
  </style>
<app-element></app-element>
<span>33233</span>
<script>
  customElements.define("app-element", class App extends HTMLElement {
    constructor() {
      super();
      this.attachShadow({ mode: "open" });
    }
    connectedCallback() {
      this.shadowRoot.innerHTML = `
      <style>
        span {
          background: steelblue;
          padding: 5px;
          color: white;
        }
      </style>
      <div>
        <span>CSS</span>
        <h1>3333</h1>
      </div>
    `;
    }
  });
</script>
<h1>h1</h1>

我们发现,css被完全隔离开了

自定义元素

<app-element></app-element>

<script>
  customElements.define("app-element", class extends HTMLElement {
    connectedCallback() {
      this.innerHTML = "<div>Hello, friend!</div>";
    }
  });
</script>

在自定义元素里面插入 shadow DOM

<app-element>
  <div class="container">Contenido previo del elemento</div>
</app-element>

<script>
  customElements.define("app-element", class extends HTMLElement {
    constructor() {
      super();
      this.attachShadow({ mode: "open" });
    }

    connectedCallback() {
      this.shadowRoot.innerHTML = "<div>Hello, friend!</div>";
    }
  });
</script>

我们发现创建页面上的Shadow DOM 后,页面上.container 的dom被隐藏了

换句话说当我们附加shadow DOM 时,它将隐藏Light DOM,但是尽管Light DOM 被隐藏了,但是可以通过浏览器查询到代码

<app-element>
  #shadow-root (open)
    <div>Hello, friend!</div>
  <div class="container">Contenido previo del elemento</div>
</app-element>

我们this.shadowRoot.innerHTML用于修改Shadow DOM。如果使用,我们将this.innerHTML只修改Light DOM

插槽

<slot> 插槽中插入我们放置的Light DOM

connectedCallback() {
      this.shadowRoot.innerHTML = "<div>Hello, friend! <slot>默认插槽</slot></div>";
    }

我们发现默认插槽会被填充.container 的内容,当<app-element> 里面为空的话,会填充默认的插槽

命名插槽

<app-element>
  <h2 slot="name">Manz</h2>
  <span slot="role">Developer</span>
  <p slot="description">I hate Internet Explorer.</p>
</app-element>

<script>
  customElements.define("app-element", class extends HTMLElement {
    constructor() {
      super();
      this.attachShadow({ mode: "open" });
    }

    connectedCallback() {
      this.shadowRoot.innerHTML = `
        <div class="card">
          <slot name="name"></slot>
          <slot name="description"></slot>
          <slot name="role"></slot>
        </div>
      `;
    }
  });
</script>

通过在外部定义css,修改插槽中的css,通过我们给出<slot></slot>给出一个默认信息

::sloted 伪类

    connectedCallback() {
      this.shadowRoot.innerHTML = `
        <style>
          ::slotted(h2) { color: blue; }  
        </style>
        <div class="card">
          <slot name="name"></slot>
          <slot name="description"></slot>
          <slot name="role"></slot>
        </div>
      `;
    }

会优先考虑全局css,但是如果有!important 会考虑优先级进行替换

正常情况下,全局的css大于自身的

  ::slotted(h2) { color: blue; }  

插槽的事件检测

事件 描述
slotchange 当它检测到插槽元素关联已更改时,将触发该事件。

们将像处理任何事件一样使用.addEventListener()该事件,使用有<slot>问题的事件来监听它并检测它何时被触发,并执行关联的功能

const slot = this.shadowRoot.querySelector("slot");
slot.addEventListener("slotchange", () => console.log("¡El slot ha cambiado!"));

例子,能监控到变化

element.setAttribute('slot', slotName);
// element.assignedSlot = $slot
element.removeAttribute('slot');
// element.assignedSlot = null

提地写了一个完整的demo,方便理解

<app-element>
  <button onClick="add()">++</button>
  <h1>xxxx</h1>
</app-element>

js部分

  class AppElement extends HTMLElement {
    constructor() {
      super();
      this.attachShadow({mode: "open"});
    }
    slot;
    connectedCallback() {
      // 添加到页面上
      this.shadowRoot.innerHTML = `<slot></slot>`;
      // 查找到slot,并且监控属性的变化
      this.slot = document.querySelector('app-element').shadowRoot.querySelector('slot');
      this.slot.addEventListener('slotchange',e=>{
        console.log('触发');
      })
    }
  }

  customElements.define("app-element", AppElement)
// 点击的时候修改属性
  function add() {
    let slot = document.querySelector('app-element').shadowRoot.querySelector('slot');
    slot.setAttribute('name','333')
  }

决定自己的高度的是你的态度,而不是你的才能

记得我们是终身初学者和学习者

总有一天我也能成为大佬

原文地址:https://www.cnblogs.com/fangdongdemao/p/13915202.html