概述

Web Components 标准非常重要的一个特性是,它使开发者能够将HTML页面的功能封装为 custom elements(自定义标签),而往常,开发者不得不写一大堆冗长、深层嵌套的标签来实现同样的页面功能。这篇文章将会介绍如何使用HTML的custom elements。Firefox、Chrome和Opera默认就支持 custom elements。Safari目前只支持 autonomous custom elements(自主自定义标签),而 Edge也正在积极实现中。

CustomElementRegistry 接口的实例用来处理 web 文档中的 custom elements — 该对象允许你注册一个 custom element,返回已注册 custom elements 的信息,等等。

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

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

作为示例,我们可以像这样定义一个叫做 word-count 的 custom element:

customElements.define('word-count', WordCount, { extends: 'p' });

这个元素叫做 word-count,它的类对象是 WordCount, 继承自 <p> 元素.

一个 custom element 的类对象可以通过 ES 2015 标准里的类语法生成。所以,WordCount可以写成下面这样:

class WordCount extends HTMLParagraphElement {
constructor() {
// 必须首先调用 super 方法
super(); // 元素的功能代码写在这里 ...
}
}

上面只是一个简单的例子,我们能做的不只这些。在构造函数中,我们可以设定一些生命周期的回调函数,在特定的时间,这些回调函数将会被调用。例如,connectedCallback会在 custom element首次被插入到文档DOM节点上时被调用,而 attributeChangedCallback则会在 custom element增加、删除或者修改某个属性时被调用。

你可以在 使用生命周期回调函数段落中了解更多相关信息。

共有两种 custom elements:

  • Autonomous custom elements 是独立的元素,它不继承其他内建的HTML元素。你可以直接把它们写成HTML标签的形式,来在页面上使用。例如 <popup-info>,或者是document.createElement("popup-info")这样。
  • Customized built-in elements 继承自基本的HTML元素。在创建时,你必须指定所需扩展的元素(正如上面例子所示),使用时,需要先写出基本的元素标签,并通过 is 属性指定custom element的名称。例如<p is="word-count">, 或者 document.createElement("p", { is: "word-count" })

示例

让我们来看几个简单示例,来了解如何创建 custom elements。

Autonomous custom elements

我们来看一下 <popup-info-box> (查看在线示例),一个关于 autonomous custom element的例子。它包含有一个图标和一段文字,并且图标显示在页面上。在这个图标获取焦点时,它会显示一个包含该段文字的信息框,用于展示更多的信息。

为了实现这个功能,首先创建一个JavaScript文件,定义一个叫做PopUpInfo的类,它继承自HTMLElement。Autonomous custom elements 总是继承自HTMLElement

class PopUpInfo extends HTMLElement {
constructor() {
// 必须首先调用 super方法
super(); // 元素的功能代码写在这里 ...
}
}

上述代码片段中,类的构造函数constructor总是先调用super()来建立正确的原型链继承关系。

在构造函数中,我们会定义元素实例所拥有的全部功能。作为示例,我们首先会将shadow root附加到custom element上,然后通过一系列DOM操作创建custom element的内部阴影DOM结构,再将其附加到 shadow root上,最后再将一些CSS附加到 shadow root的style节点上。

// 创建一个 shadow root
var shadow = this.attachShadow({mode: 'open'}); // 创建一个 spans
var wrapper = document.createElement('span');
wrapper.setAttribute('class','wrapper');
var icon = document.createElement('span');
icon.setAttribute('class','icon');
icon.setAttribute('tabindex', 0);
var info = document.createElement('span');
info.setAttribute('class','info'); // 获取text属性上的内容,并添加到一个span标签内
var text = this.getAttribute('text');
info.textContent = text; // 插入 icon
var imgUrl;
if(this.hasAttribute('img')) {
imgUrl = this.getAttribute('img');
} else {
imgUrl = 'img/default.png';
}
var img = document.createElement('img');
img.src = imgUrl;
icon.appendChild(img); // 创建一些 CSS,并应用到 shadow dom上
var style = document.createElement('style'); style.textContent = '.wrapper {' +
// 简洁起见,省略了具体的CSS // 将创建的元素附加到 shadow dom shadow.appendChild(style);
shadow.appendChild(wrapper);
wrapper.appendChild(icon);
wrapper.appendChild(info);

最后,我们使用之前提到的define()方法将 custom element注册到CustomElementRegistry上,在方法的参数里,我们指定了元素的名称,以及定义了元素功能的类。

customElements.define('popup-info', PopUpInfo);

现在我们可以在页面上使用我们定义的custom element了,就像下面这样:

<popup-info img="img/alt.png" text="Your card validation code (CVC)
is an extra security feature — it is the last 3 or 4 numbers on the
back of your card.">

注意: 上方代码不是最新,你可以在这里找到完整的源码

译者提示: 在Chrome版本76.0.3809.132(正式版本)(64 位)中测试发现,customElements.define()必须在js文件中调用,且引用此js文件时必须在script标签上添加defer属性,否则this.getAttribute('属性名称')无法获取到值。

Customized built-in elements

现在让我们来看一下另一个有关customized built in element(自定义内置元素)的示例— expanding-list (查看在线示例)。该示例将所有的无序列表转化为一个可收起/展开的菜单。

首先,我们定义一个元素的类,这和之前一样:

class ExpandingList extends HTMLUListElement {
constructor() {
// 必须首先调用 super方法
super(); // 元素的功能代码写在这里 ...
}
}

在这里,我们不会详细解释元素的功能细节,你可以在源码中了解它的工作方式。这里的真正不同点在于元素继承的是HTMLUListElement 接口,而不是HTMLElement。所以它拥有<ul> 元素所有的特性,以及在此基础上我们定义的功能,这是与独立元素(standalone element)不同之处。这也是为什么我们称它为 customized built-in元素,而不是一个autonomous元素。

接下来,和之前一样,我们使用define()方法注册一个元素,但不同的是,我们需要添加一个配置对象,用于指定我们需要继承的元素:

customElements.define('expanding-list', ExpandingList, { extends: "ul" });

在页面上使用 built-in element看起来也会有所不同:

<ul is="expanding-list">

  ...

</ul>

你可以正常使用<ul>标签,也可以通过is属性来指定一个custom element的名称。

注意: 同样的,你可以在这里找到完整的 JavaScript 源码

译者注:在 chrome 66 版本上,该示例无法正确工作,相关问题:

How to create new instance of an extended class of custom elements

使用生命周期回调函数

在custom element的构造函数中,可以指定多个不同的回调函数,它们将会在元素的不同生命时期被调用:

  • connectedCallback:当 custom element首次被插入文档DOM时,被调用。
  • disconnectedCallback:当 custom element从文档DOM中删除时,被调用。
  • adoptedCallback:当 custom element被移动到新的文档时,被调用。
  • attributeChangedCallback: 当 custom element增加、删除、修改自身属性时,被调用。

我们来看一下它们的一下用法示例。下面的代码出自life-cycle-callbacks示例(查看在线示例)。这个简单示例只是生成特定大小、颜色的方块。custom element看起来像下面这样:

<custom-square l="100" c="red"></custom-square>

这里,类的构造函数很简单 — 我们将 shadow DOM附加到元素上,然后将一个<div>元素和<style>元素附加到 shadow root上:

var shadow = this.attachShadow({mode: 'open'});

var div = document.createElement('div');
var style = document.createElement('style');
shadow.appendChild(style);
shadow.appendChild(div);

示例中的关键函数是 updateStyle()—它接受一个元素作为参数,然后获取该元素的shadow root,找到<style>元素,并添加widthheight以及background-color样式。

function updateStyle(elem) {
var shadow = elem.shadowRoot;
var childNodes = shadow.childNodes;
for(var i = 0; i < childNodes.length; i++) {
if(childNodes[i].nodeName === 'STYLE') {
childNodes[i].textContent = 'div {' +
' width: ' + elem.getAttribute('l') + 'px;' +
' height: ' + elem.getAttribute('l') + 'px;' +
' background-color: ' + elem.getAttribute('c');
}
}
}

实际的更新操作是在生命周期的回调函数中处理的,我们在构造函数中设定类这些回调函数。当元素插入到DOM中时,connectedCallback()函数将会执行 — 在该函数中,我们执行updateStyle() 函数来确保方块按照定义来显示;

connectedCallback() {
console.log('Custom square element added to page.');
updateStyle(this);
}

disconnectedCallback()adoptedCallback()回调函数只是简单地将消息发送到控制台,提示我们元素什么时候从DOM中移除、或者什么时候移动到不同的页面:

disconnectedCallback() {
console.log('Custom square element removed from page.');
} adoptedCallback() {
console.log('Custom square element moved to new page.');
}

每当元素的属性变化时,attributeChangedCallback()回调函数会执行。正如它的属性所示,我们可以查看属性的名称、旧值与新值,以此来对元素属性做单独的操作。在当前的示例中,我们只是再次执行了updateStyle()函数,以确保方块的样式在元素属性值变化后得以更新:

attributeChangedCallback(name, oldValue, newValue) {
console.log('Custom square element attributes changed.');
updateStyle(this);
}

需要注意的是,如果需要在元素属性变化后,触发 attributeChangedCallback()回调函数,你必须监听这个属性。这可以通过定义observedAttributes() get函数来实现,observedAttributes()函数体内包含一个 return语句,返回一个数组,包含了需要监听的属性名称:

static get observedAttributes() {return ['w', 'l']; }

在我们的例子中,该段代码处于构造函数的上方。

注意: 在这里查看完整的 JavaScript源码

——以上内容节选自MDN Web Docs

window 属性:自定义元素(custom elements)的更多相关文章

  1. 自定义元素(custom elements)

    记录下自定义html自定义元素的相关心得: 浏览器将自定义元素保留在 DOM 之中,但不会任何语义.除此之外,自定义元素与标准元素都一致 事实上,浏览器提供了一个HTMLUnknownElement, ...

  2. 使用 custom element 创建自定义元素

    很早我们就可以在 HTML 文档中写 <custome-element></custom-element> 这样的自定义名称标签.但是浏览器对于不认识的标签一律当成一个普通的行 ...

  3. angularJS 自定义元素和属性

    创造自定义元素和属性的方法是:directive('string',function(){ return{}; }); ①函数接收两个参数:一个字符串(指令的名字),一个函数: ②回调函数必须返回一个 ...

  4. 使用custom elements和Shadow DOM自定义标签

    具体的api我就不写 官网上面多  如果不知道这个的话也可以搜索一下 目前感觉这个还是相当好用的直接上码. <!DOCTYPE html> <html lang="en&q ...

  5. 自定义元素 – 在 HTML 中定义新元素

    本文翻译自 Custom Elements: defining new elements in HTML,在保证技术要点表达准确的前提下,行文风格有少量改编和瞎搞. 原译文地址 本文目录 引言 用时髦 ...

  6. HTML 自定义元素教程

    组件是 Web 开发的方向,现在的热点是 JavaScript 组件,但是 HTML 组件未来可能更有希望. 本文就介绍 HTML 组件的基础知识:自定义元素(custom elements). 文章 ...

  7. KnockoutJS 3.X API 第六章 组件(4) 自定义元素

    自定义元素提供了一种将组件注入视图的方便方法. 本节目录 介绍 例子 传递参数 父组件和子组件之间的通信 传递监控属性的表达式 将标记传递到组件中 控制自定义元素标记名称 注册自定义元素 备注1:将自 ...

  8. Web Components之Custom Elements

    什么是Web Component? Web Components 包含了多种不同的技术.你可以把Web Components当做是用一系列的Web技术创建的.可重用的用户界面组件的统称.Web Com ...

  9. 自定义元素–为你的HTML代码定义新元素

    注意:这篇文章介绍的 API 尚未完全标准化,并且仍在变动中,在项目中使用这些实验性 API 时请务必谨慎. 引言 现在的 web 严重缺乏表达能力.你只要瞧一眼“现代”的 web 应用,比如 GMa ...

随机推荐

  1. spring多模块之间的调用

    https://blog.csdn.net/tomcat_2014/article/details/50206197?locationNum=5

  2. Java基础——消息队列

    1.消息队列的适用场景:商品秒杀.系统解耦.日志记录等 2.使用Queue实现消息对列 双端队列(Deque)是 Queue 的子类也是 Queue 的补充类,头部和尾部都支持元素插入和获取阻塞队列指 ...

  3. Spring 配置文件配置事务

    一.引入事务的头文件 xmlns:tx="http://www.springframework.org/schema/tx" http://www.springframework. ...

  4. Spring与Junit测试整合

    一.引入spring测试包:text包 二.@RunWith:指定spring对junit提供的一个运行器 @ContextConfiguration:  locations指定spring配置文件位 ...

  5. [论文理解] Good Semi-supervised Learning That Requires a Bad GAN

    Good Semi-supervised Learning That Requires a Bad GAN 恢复博客更新,最近没那么忙了,记录一下学习. Intro 本文是一篇稍微偏理论的半监督学习的 ...

  6. 002 01 Android 零基础入门 01 Java基础语法 01 Java初识 02 Java简介

    002 01 Android 零基础入门 01 Java基础语法 01 Java初识 02 Java简介 学习Java的基础语法 Java是一门编程语言,学习的逻辑其实和现实世界的语言是一样的,需要了 ...

  7. Tensorflow学习笔记No.4.2

    使用CNN卷积神经网络(2) 使用Tensorflow搭建简单的CNN卷积神经网络对fashion_mnist数据集进行分类 不了解是那么是CNN卷积神经网络的小伙伴可以参考上一篇博客(Tensorf ...

  8. servercat IOS Linux监控 SSH客户端

    servercat IOS Linux监控 SSH客户端 iOS 平台上新出的一个挺有趣的服务器监控 + SSH 客户端. 监控服务器状态,内存.CPU.网络 还能对Docker容器进行监控 价格:¥ ...

  9. vue去掉地址栏#号

    mode:'history' 将这代码放入router.js里面

  10. Fedora version history --- kernel version

    Fedora version history https://en.wikipedia.org/wiki/Fedora_version_history     Version (Code name)[ ...