使用 custom element 创建自定义元素
很早我们就可以在 HTML 文档中写 <custome-element></custom-element> 这样的自定义名称标签。但是浏览器对于不认识的标签一律当成一个普通的行内元素处理,没有相关语义。虽然我们能用 JavaScript 代码给它添加一些功能,但是并没有生命周期相关的函数供我们做一些初始化和销毁的处理。
通过浏览器提供的 Custom elements api 我们能定义一个自定义元素,并且告知 HTML 解析器如何正确地构造一个元素,以及在该元素的属性变化时执行相应的处理。
定义新元素
比如我们想要像 <date-string ln="zh"></data-string> 这样使用一个显示日期字符串的标签,并且在 ln 属性为 zh 时显示中文格式,en 时显示英文格式。
首先我们定义一个类 DateString 派生自 HTMLElement。
class DateString extends HTMLElement {
constructor() {
super()
return
}
// 返回需要监听的属性,当属性值改变的时候会调用 attributeChangedCallback 这个方法
static get observedAttributes () {
return ['ln']
}
attributeChangedCallback (name, oldValue, newValue) {
this.updateRendering (newValue)
}
// 元素插入到文档中时调用
connectedCallback() {
const ln = this.getAttribute('ln')
this.updateRendering(ln)
}
// 元素从文档中移除时调用
disconnectedCallback () {
window.clearInterval(this.interval)
}
updateRendering (ln = 'zh') {
// 一个比较好的实践就是在渲染时,检查元素的 ownerDocument.defaultView, 如果不存在则什么都不干
if (!this.ownerDocument.defaultView) {
return
}
if (this.interval) {
window.clearInterval(this.interval)
}
this.interval = setInterval(() => {
if (ln === 'zh') {
this.innerHTML = new Date().toLocaleString()
} else {
this.innerHTML = new Date().toString()
}
}, 1000)
}
}
然后调用 customElements.define() 注册这个自定义元素,设置属性并插入到 body 中。
customElements.define("date-string", DateString)
const dateStr = document.createElement('date-string')
dateStr.setAttribute('ln', 'zh')
document.body.appendChild(dateStr)
也可以用直接调用构造函数创建元素
const dateStr = new DateString()
dateStr.setAttribute('ln', 'zh')
document.body.appendChild(dateStr)
自定义元素可以使用符合规范的任意属性名,下面说的派生内置元素类型要自定义属性,则要用 data-*
上面代码那样设置属性很繁琐,我们可以做一个属性映射,以期望 dateStr.ln = 'zh' 这样赋值
class DateString extends HTMLElement {
...
get ln () {
return this.getAttribute('ln')
}
set ln (value) {
this.setAttribute('ln', value)
}
}
元素升级
除了像上面那样用 JavaScript 代码创建元素并添加到 body 下面,也可以直接在 HTML 写自定义元素
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<date-string ln="zh"></date-string>
<script>
class DateString extends HTMLElement {
...
}
customElements.define("date-string", DateString)
</script>
</body>
</html>
上面的代码依然正常工作。首先浏览器正常解析文档,遇到 <date-string> 标签时,把它当做一个普通的行内元素对待,实际上是 HTMElement 类型(如果标签的名称中没有中划线,<unknow>,那么则是 HTMLUnknownElement 类型实例)。当 <script> 标签中的代码执行后,注册了名为 date-string 的自定义元素,浏览器再对文档中的 data-string 元素做升级处理,调用相应的生命周期函数。
需要注意的是,只有插入到文档中的元素才会升级:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<date-string ln="zh" id="dateStr"></date-string>
<script>
const dateStr = document.getElementById('dateStr')
const other = document.createElement('date-string')
console.log(dateStr instanceof HTMLElement) // true
console.log(other instanceof HTMLElement) // true
class DateString extends HTMLElement {}
customElements.define("date-string", DateString)
console.log(dateStr instanceof DateString) // true
console.log(other instanceof DateString) // false
// 插入到文档中后,other 元素升级为自定义元素类型 DateString
document.body.appendChild(other)
console.log(other instanceof DateString) // true
</script>
</body>
</html>
派生内置元素类型
除了从 HTMLElement 派生自定义元素,我们还可以从 HTMLButtonElement, HTMLDivElement 等内置元素类型派生自定义元素。这么做的好处是,可以保留内置元素的语义化功能。比如,HTMLButtonElement 有 active 状态,通过按 tab 键可以使 button 元素获得焦点,然后按回车键相当于点击 button 元素。现在我们从 HTMLButtonElement 派生一个自定义的按钮,并在点击的时候改变背景颜色。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<button is="colored-button">colored</button>
<script>
class ColoredButton extends HTMLButtonElement {
constructor () {
super();
this.addEventListener('click', () => {
let ox = Math.floor(Math.random()*255).toString(16)
this.style.background = `#${ox.repeat(3)}`
})
}
}
customElements.define('colored-button', ColoredButton, {
extends: 'button'
})
</script>
</body>
</html>
这个按钮在行为上与内置的 button 一样, 可以获取焦点,提交表单,也有禁用属性等。
派生内置元素与自定义元素略微不同,调用 customElements.define 时要传入第三个参数表明是从那个元素派生,这里使用的名称是 'button' 即标签名,因为浏览器是靠识别标签名来提供语义和默认行为,基于这一点,使用的时候也是用的原本的标签名 button,然后再给一个 is 属性指定自定义元素的名称。
通过 document.createElement 创建元素时也有不同
const coloredButton = document.createElement('button', {
is: 'colored-button'
})
也可以直接调用构造函数创建
const coloredButton = new ColoredButton
console.log(coloredButton.localName) // => 'button'
console.log(coloredButton.getAttribute(is)) // => 'colored-button'
注:直到 chrome 61 版本,扩展内置元素依然在开发中。参见链接
构造函数的一些限制
自定义元素的构造函数必须遵循如下限制
- 构造函数中不能调用 document.write() 和 document.open()
- 不能访问元素的属性和子元素,因为在元素未升级的情况下(元素还未插入文档中),不存在属性和子元素
- 初始化工作要尽量推迟到 connectedCallback 中,尤其是涉及获取资源或渲染的工作。但是 connectedCallback 可能会调用多次(每次插入到文档中时都会调用),一次性的初始化工作需要自己设置防护措施。
命名限制
自定义元素的命名限制如下
- 必须以小写字母开头
- 必须有至少一个中划线
- 允许小写字母,中划线,下划线,点号,数字
- 允许部分 Unicode 字符,所以
<h-?></h-?>这样也是可以的. 不允许下面这些名称
- annotation-xml
- color-profile
- font-face
- font-face-src
- font-face-uri
- font-face-format
- font-face-name
- missing-glyph
如果名称出现不允许的字符, customElements.define 会报错。
生命周期函数
自定义元素可以定义特殊生命周期钩子,以便在其存续的特定时间内运行代码。
- constructor 创建或升级元素的一个实例。用于初始化状态、设置事件侦听器。
- connectedCallback 元素每次插入到 DOM 时都会调用。可用于获取资源或渲染。
- disconnectedCallback 元素每次从 DOM 中移除时都会调用。用于运行清理代码。
- attributeChangedCallback 属性添加、移除、更新或替换。仅对 observedAttributes 中返回的属性有效。
- adoptedCallback 自定义元素被移入新的 document 时调用。
响应回调是同步的。如果有人对您的元素调用 el.setAttribute(...),浏览器将立即调用 attributeChangedCallback()。 同理,从 DOM 中移除元素(例如用户调用 el.remove())后,您会立即收到 disconnectedCallback()。
使用 custom element 创建自定义元素的更多相关文章
- Creating Custom Shadows ——创建自定义shadow
Custom shadows are a Robolectric feature that allows you to make targeted changes in the way Android ...
- 自定义元素(custom elements)
记录下自定义html自定义元素的相关心得: 浏览器将自定义元素保留在 DOM 之中,但不会任何语义.除此之外,自定义元素与标准元素都一致 事实上,浏览器提供了一个HTMLUnknownElement, ...
- WebComponent魔法堂:深究Custom Element 之 面向痛点编程
前言 最近加入到新项目组负责前端技术预研和选型,一直偏向于以Polymer为代表的WebComponent技术线,于是查阅各类资料想说服老大向这方面靠,最后得到的结果是:"资料99%是英语 ...
- 前端组件化Polymer入门教程(4)——自定义元素
除了上一篇说到的创建自定义元素方法以外,还可以通过原生JS来创建,当你需要动态的创建元素时可以通过这种方式. template.html <link rel="import" ...
- window 属性:自定义元素(custom elements)
概述 Web Components 标准非常重要的一个特性是,它使开发者能够将HTML页面的功能封装为 custom elements(自定义标签),而往常,开发者不得不写一大堆冗长.深层嵌套的标 ...
- angular custom Element 自定义web component
angular 自定义web组件: 首先创建一个名为myCustom的组件. 引入app.module: ... import {customComponent} from ' ./myCustom. ...
- GHOST CMS - 创建自定义主页 Creating a custom home page
创建自定义主页 Creating a custom home page 为你的网站创建一个自定义的主页是一个让你从人群中脱颖而出的好方法,并把你自己独特的印记存放在你的网上.本教程向您展示了如何在Gh ...
- 元素(element)创建
一.元素创建的三种方式-------元素创建是为了提高用户的体验 1.第一种 document.write("标签代码及内容") <input type="butt ...
- WebComponent魔法堂:深究Custom Element 之 从过去看现在
前言 说起Custom Element那必然会想起那个相似而又以失败告终的HTML Component.HTML Component是在IE5开始引入的新技术,用于对原生元素作功能"增强& ...
随机推荐
- Ret2shellcode
利用原理 ret2shellcode,即控制程序执行 shellcode 代码.一般来说,shellcode 需要我们自己填充.这其实是另外一种典型的利用方法,即此时我们需要自己去填充一些可执行的代码 ...
- poj-3657 Haybale Guessing(二分答案+并查集)
http://poj.org/problem?id=3657 下方有中文版,不想看英文的可直接点这里看中文版题目 Description The cows, who always have an in ...
- 六、linux-mysql的mysql字符集问题
一.什么是字符集? 字符集是用来定义mysql数据字符串的存储方式,而校对规则则是定义比较字符串的方式.mysql字符集包含字符集和校对规则. 二.字符集的选择 常见的字符集中,中英混合环境建议用UT ...
- RadixSort(基数排序)原理及C++代码实现
基数排序是一种思想很值得学习的排序方法. 它突破了正常的排序思维:先排高位,如果高位相同再排次高位,直至最低.它的思想是利用稳定排序从低位开始排,最后再排最高位. 另外它用来划分的位不一定是一位一位的 ...
- Create Collection Type with Class Type Constraints
Create Collection Type with Class Type Constraints: new TypeToken<ArrayList<ClassType>>( ...
- Zabbix 监控sqlserver
转:Zabbix 监控sqlserver 一:Zabbix监控sqlserver 方法一: 1.思路整理 1.在zabbix server上安装Freetds.unixODBC.unixODBC-de ...
- 系统学习javaweb1----HTML语言1
自我感受:HTML语言没想到也有这么大的学问,竟然能通过超链接标签直接访问百度,这可让我大吃一惊,我也得反思一下自己,上学期的java纯是混过来的,没有系统的学习过,感觉能通过期末考试都是侥幸,接下来 ...
- python后端面试第三部分:数据储存与缓存相关--长期维护
1. 列举常见的关系型数据库和非关系型都有哪些?2. MySQL常见数据库引擎及比较?3. 简述数据三大范式?4. 什么是事务?MySQL如何支持事务?5. 简述数据库设计中一对多和多对多的应用场景? ...
- 【待填坑】LG_4996_咕咕咕
正解思路和[AHOI]的中国象棋非常相似,同样是利用状态不一定一定要表示出来,利用组合数学递推节省枚举时间.
- SpringMVC学习笔记二:参数接受
该项目用来介绍SpringMVC对参数接受的方法: 项目目录树:在前一个项目上修改添加 新添加了Student类和Group类,用来测试整体参数接受 Student.java package com. ...