你知道原生HTML组件是什么吗?原生HTML组件的介绍


当前第2页 返回上一页

1

2

3

4

5

class AwesomeElement extends HTMLElement {

  static get observedAttributes() {

    return ['awesome'];

  }

}

disconnectedCallback(): 组件被从 DOM 树中移除,用于进行一些清理操作。

对应 React 中的 Unmounting 阶段:componentWillUnmount()。

adoptedCallback(): 组件实例从一个文档被移动到另一个文档。

这个生命周期是原生组件独有的,React 中没有类似的生命周期。这个生命周期函数也并不常用到,一般在操作多个 document 的时候会遇到,调用 document.adoptNode() 函数转移节点所属 document 时会触发这个生命周期。

在定义了自定义组件后,我们需要将它注册到 HTML 标签列表中,通过 window.customElements.define() 函数即可实现,这个函数接受两个必须参数和一个可选参数。第一个参数是注册的标签名,为了避免和 HTML 自身的标签冲突,Custom Elements 要求用户自定义的组件名必须至少包含一个短杠 -,并且不能以短杠开头,比如 my-element、awesome-button 之类都是可以的。第二个参数是注册的组件的 class,直接将继承的子类类名传入即可,当然也可以直接写一个匿名类:

1

2

3

window.customElements.define('my-element', class extends HTMLElement {

  ...

});

注册之后,我们就可以使用了,可以直接在 html 文档中写对应的标签,比如:<my-element></my-element>,也可以通过 document.createElement('my-element') 来创建,用法与普通标签几乎完全一样。但要注意的是,虽然 html 标准中说部分标签可以不关闭或是自关闭(<br> 或是 <br />),但是只有规定的少数几个标签允许自关闭,所以,在 html 中写 Custom Elements 的节点时必须带上关闭标签。

由于 Custom Elements 是通过 JavaScript 来定义的,而一般 js 文件都是通过 <script> 标签外联的,所以 html 文档中的 Custom Elements 在 JavaScript 未执行时是处于一个默认的状态,浏览器默认会将其内容直接显示出来。为了避免这样的情况发生,Custom Elements 在被注册后都会有一个 :defined CSS 伪类而在注册前没有,所以我们可以通过 CSS 选择器在 Custom Elements 注册前将其隐藏起来,比如:

1

2

3

my-element:not(:defined) {

  display: none;

}

或者 Custom Elements 也提供了一个函数来检测指定的组件是否已经被注册:customElements.whenDefined(),这个函数接受一个组件名参数,并返回一个 Promise,当 Promise 被 resolve 时,就表示组件被注册了。

这样,我们就可以放心的在加载 Custom Elements 的 JavaScript 的 <script> 标签上使用 async 属性来延迟加载了(当然,如果是使用 ES6 Modules 形式的话默认的加载行为就会和 defer 类似)。

1246191307-5bc7f1ac6b9fe_articlex.png

Custom Elements + Shadow DOM

使用 Custom Elements 来创建组件时,通常会与 Shadow DOM 进行结合,利用 Shadow DOM 的隔离性,就可以创造独立的组件。

通常在 Custom Elements 的 constructor() 构造函数中去创建 Shadow DOM,并对 Shadow DOM 中的节点添加事件监听、对特定事件触发原生 Events 对象。

正常编写 html 文档时,我们可能会给 Custom Elements 添加一些子节点,像这样:<my-element><h1>Title</h1><p>Content</p></my-element>,而我们创建的 Shadow DOM 又拥有其自己的结构,怎样将这些子节点放置到 Shadow DOM 中正确的位置上呢?

在 React 中,这些子节点被放置在 props 的 children 中,我们可以在 render() 时选择将它放在哪里。而在 Shadow DOM 中有一个特殊的标签:<slot>,这个标签的用处就如同其字面意思,在 Shadow DOM 上放置一个“插槽”,然后 Custom Elements 的子节点就会自动放置到这个“插槽”中了。

有时我们需要更加精确地控制子节点在 Shadow DOM 中的位置,而默认情况下,所有子节点都会被放置在同一个 <slot> 标签下,即便是你写了多个 <slot>。那怎样更精确地对子节点进行控制呢?

默认情况下,<slot>Fallback</slot> 这样的是默认的 <slot>,只有第一个默认的 <slot> 会有效,将所有子节点全部放进去,如果没有可用的子节点,将会显示默认的 Fallback 内容(Fallback 可以是一棵子 DOM 树)。

<slot> 标签有一个 name 属性,当你提供 name 后,它将变为一个“有名字的 <slot>”,这样的 <slot> 可以存在多个,只要名字各不相同。此时他们会自动匹配 Custom Elements 下带 slot 属性并且 slot 属性与自身 name 相同的子节点,像这样

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

<template id="list">

  <div>

    <h1>Others</h1>

    <slot></slot>

  </div>

  <div>

    <h1>Animals</h1>

    <slot name="animal"></slot>

  </div>

  <div>

    <h1>Fruits</h1>

    <slot name="fruit"></slot>

  </div>

</template>

 

<my-list>

  <div slot="animal">Cat</div>

  <div slot="fruit">Apple</div>

  <div slot="fruit">Banana</div>

  <div slot="other">flower</div>

  <div>pencil</div>

  <div slot="animal">Dog</div>

  <div slot="fruit">peach</div>

  <div>red</div>

</my-list>

1

2

3

4

5

6

7

8

9

class MyList extends HTMLElement {

  constructor() {

    super();

    const root = this.attachShadow({ mode: 'open' });

    const template = document.getElementById('list');

    root.appendChild(document.importNode(template.content, true));

  }

}

customElements.define('my-list', MyList);

这样就可以得到如图所示的结构,#shadow-root (open) 表示这是一个开放的 Shadow DOM,下面的节点是直接从 template 中 clone 过来的,浏览器自动在三个 <slot> 标签下放置了几个灰色的 <div> 节点,实际上这些灰色的 <div> 节点表示的是到其真实节点的“引用”,鼠标移动到他们上会显示一个 reveal 链接,点击这个链接即可跳转至其真实节点。

1523399701-5bc7f1ce6b75a_articlex.png

这里我们可以看到,虽然 <my-list> 下的子节点是乱序放置的,但是只要是给定了 slot 属性,就会被放置到正确的 <slot> 标签下。注意观察其中有一个 <div slot="other">flower</div>,这个节点由于指定了 slot="other",但是却找不到匹配的 <slot> 标签,所以它不会被显示在结果中。

在为 Custom Elements 下的 Shadow DOM 设置样式的时候,我们可以直接在 Shadow DOM 下放置 <style> 标签,也可以放置 <link rel="stylesheet">,Shadow DOM 下的样式都是局部的,所以不用担心会影响到 Shadow DOM 的外部。并且由于这些样式仅影响局部,所以对性能也有很大的提升。

在 Shadow DOM 内部的样式中,也有一些特定的选择器,比如 :host 选择器,代表着 ShadowRoot,这类似于普通 DOM 中的 :root,并且它可以与其他伪类组合使用,比如当鼠标在组件上时::host(:hover),当组件拥有某个 class 时::host(.awesome),当组件拥有 disabled 属性时::host([disabled])……但是 :host 是拥有继承属性的,所以如果在 Custom Elements 外部定义了某些样式,将会覆盖 :host 中的样式,这样就可以轻松地实现各式各样的“主题风格”了。

为了实现自定义主题,我们还可以使用 Shadow DOM 提供的 :host-context() 选择器,这个选择器允许检查 Shadow DOM 的任何祖先节点是否包含指定选择器。比如如果在最外层 DOM 的 <html> 或 <body> 上有一个 class:.night,则 Shadow DOM 内就可以使用 :host-context(.night) 来指定一个夜晚的主题。这样可以实现主题样式的继承。

还有一种样式的定义方式是利用 CSS 变量。我们在 Shadow DOM 中使用变量来指定样式,比如:background-color: var(--bg-colour, #0F0);,这样就可以在 Shadow DOM 外面指定 --bg-colour 变量来设置样式了,如果没有指定变量,将使用默认的样式颜色 #0F0。

有时我们需要在 Shadow DOM 内部使用完全自定义的样式,比如字体样式、字体大小,如果任由其继承可能导致布局错乱,而每次在组件外面指定样式又略显麻烦,并且也破坏了组件的封装性。所以,Shadow DOM 提供了一个 all 属性,只要指定 :host{ all: initial; } 就可以重置所有继承的属性。

Demo

Web Components 的 Demo 在网上已经有很多了,这是我 2 年前初次接触 ES6 与 Web Components 的时候写的一个 Demo:https://github.com/jinliming2/Calendar-js,一个日历,当时还是 v0 的规范,并且在 Firefox 下还存在会导致 Firefox 崩溃的 Bug(感觉是 Firefox 在实现 Shadow DOM 时的 Bug)。目前这个 Demo 已经不能在 Firefox 下运行了,因为 Firefox 已经删除了 v0 规范,开始实行 v1 标准了,所以近期我可能会重构一下这个 Demo。

以上就是你知道原生HTML组件是什么吗?原生HTML组件的介绍的详细内容,更多文章请关注木庄网络博客

返回前面的内容

相关阅读 >>

如何理解javascript的对象

html5实现背景音乐的自动播放

javascript参数类型转换有哪些方法

javascript怎么输出绝对值

html5不常用标签可以怎么使用?

一纸搞懂js系列(1)之编译原理,作用域,作用域链,变量提升,暂时性死区

javascript的用途有哪些

javascript怎么判断正数还是负数

webgl怎样操作json与echarts图表

javascript怎么移除属性

更多相关阅读请进入《前端》频道 >>




打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...

    正在狠努力加载,请稍候...