很早咱们就能够在 HTML 文档中写 <custome-element></custom-element>
这样的自定义名称标签。可是浏览器对于不认识的标签一概当成一个普通的行内元素处理,没有相关语义。虽然咱们能用 JavaScript 代码给它添加一些功能,可是并无生命周期相关的函数供咱们作一些初始化和销毁的处理。 javascript
经过浏览器提供的 Custom elements api 咱们能定义一个自定义元素,而且告知 HTML 解析器如何正确地构造一个元素,以及在该元素的属性变化时执行相应的处理。html
好比咱们想要像 <date-string ln="zh"></data-string>
这样使用一个显示日期字符串的标签,而且在 ln 属性为 zh 时显示中文格式,en 时显示英文格式。java
首先咱们定义一个类 DateString 派生自 HTMLElement。chrome
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 中。api
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-* app
上面代码那样设置属性很繁琐,咱们能够作一个属性映射,以指望 dateStr.ln = 'zh' 这样赋值dom
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 元素作升级处理,调用相应的生命周期函数。 ui
须要注意的是,只有插入到文档中的元素才会升级:
<!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 版本,扩展内置元素依然在开发中。参见连接
自定义元素的构造函数必须遵循以下限制
自定义元素的命名限制以下
<h-?></h-?>
这样也是能够的.不容许下面这些名称
若是名称出现不容许的字符, customElements.define 会报错。
自定义元素能够定义特殊生命周期钩子,以便在其存续的特定时间内运行代码。
响应回调是同步的。若是有人对您的元素调用 el.setAttribute(...),浏览器将当即调用 attributeChangedCallback()。 同理,从 DOM 中移除元素(例如用户调用 el.remove())后,您会当即收到 disconnectedCallback()。