很早咱們就能夠在 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()。