成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

WebComponent魔法堂:深究Custom Element 之 標(biāo)準(zhǔn)構(gòu)建

philadelphia / 1342人閱讀

摘要:明確各階段適合的操作用于初始化元素的狀態(tài)和設(shè)置事件監(jiān)聽,或者創(chuàng)建。事件類型轉(zhuǎn)換通過捕獲事件,然后通過發(fā)起事件來對事件類型進行轉(zhuǎn)換,從而觸發(fā)更符合元素特征的事件類型。

前言

?通過《WebComponent魔法堂:深究Custom Element 之 面向痛點編程》,我們明白到其實Custom Element并不是什么新東西,我們甚至可以在IE5.5上定義自己的alert元素。但這種簡單粗暴的自定義元素并不是我們需要的,我們需要的是具有以下特點的自定義元素:

自定義元素可通過原有的方式實例化(,new CustomElement()document.createElement("CUSTOM-ELEMENT"))

可通過原有的方法操作自定義元素實例(如document.body.appendChild,可被CSS樣式所修飾等)

能監(jiān)聽元素的生命周期
?而Google為首提出的H5 Custom Element讓我們可以在原有標(biāo)準(zhǔn)元素的基礎(chǔ)上向瀏覽器注入各種抽象層次更高的自定義元素,并且在元素CRUD操作上與原生API無縫對接,編程體驗更平滑。下面我們一起來通過H5 Custom Element來重新定義alert元素吧!

命名這件“小事”

?在正式擼代碼前我想讓各位最頭痛的事應(yīng)該就是如何命名元素了,下面3個因素將影響我們的命名:

命名沖突。自定義組件如同各種第三方類庫一樣存在命名沖突的問題,那么很自然地會想到引入命名空間來解決,但由于組件的名稱并不涉及組件資源加載的問題,因此我們這里簡化一下——為元素命名添加前綴即可,譬如采用很JAVA的com-cnblogs-fsjohnhuang-alert。

語義化。語義化我們理解就是元素名稱達到望文生義的境界,譬如x-alert一看上去就是知道x是前綴而已跟元素的功能無關(guān),alert才是元素的功能。

足夠的吊:)高大上的名稱總讓人賞心悅目,就像我們項目組之前開玩笑說要把預(yù)警系統(tǒng)改名為"超級無敵全球定位來料品質(zhì)不間斷跟蹤預(yù)警綜合平臺",呵呵!
?除了上述3點外,H5規(guī)范中還有這條規(guī)定:自定義元素必須至少包含一個連字符,即最簡形式也要這樣a-b。而不帶連字符的名稱均留作瀏覽器原生元素使用。換個說法就是名稱帶連字符的元素被識別為有效的自定義元素,而不帶連字符的元素要么被識別為原生元素,要么被識別為無效元素。

const compose = (...fns) => {
  const lastFn = fns.pop()
  fns = fns.reverse()
  return a => fns.reduce((p, fn) => fn(p), lastFn(a))
}
const info = msg => console.log(msg)
const type = o => Object.prototype.toString.call(o)
const printType = compose(info, type)

const newElem = tag => document.createElement(tag)

// 創(chuàng)建有效的自定義元素
const xAlert = newElem("x-alert")
infoType(xAlert) // [object HTMLElement]

// 創(chuàng)建無效的自定義元素
const alert = newElem("alert")
infoType(alert) // [object HTMLUnknownElement]

// 創(chuàng)建有效的原生元素
const div = newElem("div")
infoType(div) // [object HTMLDivElement]

?那如果我偏要用alert來自定義元素呢?瀏覽器自當(dāng)會說一句“悟空,你又調(diào)皮了”

?現(xiàn)在我們已經(jīng)通過命名規(guī)范來有效區(qū)分自定義元素和原生元素,并且通過前綴解決了命名沖突問題。嘿稍等,添加前綴真的是解決命名沖突的好方法嗎?這其實跟通過添加前綴解決id沖突一樣,假如有兩個元素發(fā)生命名沖突時,我們就再把前綴加長直至不再沖突為止,那就有可能出現(xiàn)很JAVA的com-cnblogs-fsjohnhuang-alert的命名,噪音明顯有點多,直接降低語義化的程度,重點還有每次引用該元素時都要敲這么多字符,打字的累看的也累。這一切的根源就是有且僅有一個Scope——Global Scope,因此像解決命名沖突的附加信息則無法通過上下文來隱式的提供,直接導(dǎo)致需要通過前綴的方式來硬加上去。
?前綴的方式我算是認了,但能不能少打?qū)懽帜??像命名空間那樣
木有命名沖突時

#!usr/bin/env python
# -*- coding: utf-8 -*-
from django.http import HttpResponse

def index(request):
  return HttpResponse("Hello World!")

存在命名沖突時

#!usr/bin/env python
# -*- coding: utf-8 -*-
import django.db.models
import peewee

type(django.db.models.CharField)
type(peewee.CharField)

前綴也能有選擇的省略就好了!

把玩Custome Element v0

?對元素命名吐嘈一地后,是時候把玩API了。

從頭到腳定義新元素
/** x-alert元素定義 **/
const xAlertProto = Object.create(HTMLElement.prototype, {
  /* 元素生命周期的事件 */
  // 實例化時觸發(fā)
  createdCallback: {
    value: function(){
      console.log("invoked createCallback!")

      const raw = this.innerHTML
      this.innerHTML = `
${raw}
` this.querySelector("button.close").addEventListener("click", _ => this.close()) } }, // 元素添加到DOM樹時觸發(fā) attachedCallback: { value: function(){ console.log("invoked attachedCallback!") } }, // 元素DOM樹上移除時觸發(fā) detachedCallback: { value: function(){ console.log("invoked detachedCallback!") } }, // 元素的attribute發(fā)生變化時觸發(fā) attributeChangedCallback: { value: function(attrName, oldVal, newVal){ console.log(`attributeChangedCallback-change ${attrName} from ${oldVal} to ${newVal}`) } }, /* 定義元素的公有方法和屬性 */ // 重寫textContent屬性 textContent: { get: function(){ return this.querySelector(".content").textContent }, set: function(val){ this.querySelector(".content").textContent = val } }, close: { value: function(){ this.style.display = "none" } }, show: { value: function(){ this.style.display = "block" } } }) // 向瀏覽器注冊自定義元素 const XAlert = document.registerElement("x-alert", { prototype: xAlertProto }) /** 操作 **/ // 實例化 const xAlert1 = new XAlert() // invoked createCallback! const xAlert2 = document.createElement("x-alert") // invoked createCallback! // 添加到DOM樹 document.body.appendChild(xAlert1) // invoked attachedCallback! // 從DOM樹中移除 xAlert1.remove() // invoked detachedCallback! // 僅作為DIV的子元素,而不是DOM樹成員不會觸發(fā)attachedCallback和detachedCallback函數(shù) const d = document.createElement("div") d.appendChild(xAlert1) xAlert1.remove() // 訪問元素實例方法和屬性 xAlert1.textContent = 1 console.log(xAlert1.textContent) // 1 xAlert1.close() // 修改元素實例特性 xAlert1.setAttribute("d", 1) // attributeChangedCallback-change d from null to 1 xAlert1.removeAttribute("d") // attributeChangedCallback-change d from 1 to null // setAttributeNode和removeAttributeNode方法也會觸發(fā)attributeChangedCallback

?上面通過定義x-alert元素展現(xiàn)了Custom Element的所有API,其實就是繼承HTMLElement接口,然后選擇性地實現(xiàn)4個生命周期回調(diào)方法,而在createdCallback中書寫自定義元素內(nèi)容展開的邏輯。另外可以定義元素公開屬性和方法。最后通過document.registerElement方法告知瀏覽器我們定義了全新的元素,你要好好對它哦!
?那現(xiàn)在的問題在于假如這個HTML Markup出現(xiàn)在document.registerElement調(diào)用之前,那會出現(xiàn)什么情況呢?這時的x-alert元素處于unresolved狀態(tài),并且可以通過CSS Selector :unresolved來捕獲,當(dāng)執(zhí)行document.registerElement后,x-alert元素則處于resolved狀態(tài)。于是可針對兩種狀態(tài)作樣式調(diào)整,告知用戶處于unresolved狀態(tài)的元素暫不可用,敬請期待。

漸進增強原生元素

?有時候我們只是想在現(xiàn)有元素的基礎(chǔ)上作些功能增強,倘若又要從頭做起那也太折騰了,幸好Custom Element規(guī)范早已為我們想好了。下面我們來對input元素作增強

const xInputProto = Object.create(HTMLInputElement.prototype, {
  createdCallback: {
    value: function(){ this.value = "x-input" }
  },
  isEmail: {
    value: function(){
      const val = this.value
      return /[0-9a-zA-Z]+@S+.S+/.test(val)
    }
  }
})
document.registerElement("x-input", {
  prototype: xInputProto,
  extends: "input"
})

// 操作
const xInput1 = document.createElement("input", "x-input") // 
console.log(xInput1.value) // x-input
console.log(xInput1.isEmail()) // false
Custom Element v1 —— 換個裝而已啦

?Custom Element API現(xiàn)在已經(jīng)升級到v1版本了,其實就是提供一個專門的window.customElements作為入口來統(tǒng)一管理和操作自定義元素,并且以對ES6 class更友善的方式定義元素,其中的步驟和概念并沒有什么變化。下面我們采用Custom Element v1的API重寫上面兩個示例

從頭定義

class XAlert extends HTMLElement{
  // 相當(dāng)于v0中的createdCallback,但要注意的是v0中的createdCallback僅元素處于resolved狀態(tài)時才觸發(fā),而v1中的constructor就是即使元素處于undefined狀態(tài)也會觸發(fā),因此盡量將操作延遲到connectedCallback里執(zhí)行
  constructor(){
    super() // 必須調(diào)用父類的構(gòu)造函數(shù)

    const raw = this.innerHTML
    this.innerHTML = `
${raw}
` this.querySelector("button.close").addEventListener("click", _ => this.close()) } // 相當(dāng)于v0中的attachedCallback connectedCallback(){ console.log("invoked connectedCallback!") } // 相當(dāng)于v0中的detachedCallback disconnectedCallback(){ console.log("invoked disconnectedCallback!") } // 相當(dāng)于v0中的attributeChangedCallback,但新增一個可選的observedAttributes屬性來約束所監(jiān)聽的屬性數(shù)目 attributeChangedCallback(attrName, oldVal, newVal){ console.log(`attributeChangedCallback-change ${attrName} from ${oldVal} to ${newVal}`) } // 缺省時表示attributeChangedCallback將監(jiān)聽所有屬性變化,若返回數(shù)組則僅監(jiān)聽數(shù)組中的屬性變化 static get observedAttributes(){ return ["disabled"] } // 新增事件回調(diào),就是通過document.adoptNode方法修改元素ownerDocument屬性時觸發(fā) adoptedCallback(){ console.log("invoked adoptedCallback!") } get textContent(){ return this.querySelector(".content").textContent } set textContent(val){ this.querySelector(".content").textContent = val } close(){ this.style.display = "none" } show(){ this.style.display = "block" } } customElements.define("x-alert", XAlert)

漸進增強

class XInput extends HTMLInputElement{
  constructor(){
    super()

    this.value = "x-input"
  }
  isEmail(){
    const val = this.value
    return /[0-9a-zA-Z]+@S+.S+/.test(val)
  }
}
customElements.define("x-input", XInput, {extends: "input"})

// 實例化方式
document.createElement("input", {is: "x-input"})
new XInput()

?除此之外之前的unresolved狀態(tài)改成defined和undefined狀態(tài),CSS對應(yīng)的選擇器為:defined:not(:defined)。
?還有就是新增一個customeElements.whenDefined({String} tagName):Promise方法,讓我們能監(jiān)聽自定義元素從undefined轉(zhuǎn)換為defined的事件。


  Twitter
  Facebook
  G+


// Fetch all the children of  that are not defined yet.
let undefinedButtons = buttons.querySelectorAll(":not(:defined)");

let promises = [...undefinedButtons].map(socialButton => {
  return customElements.whenDefined(socialButton.localName);
));

// Wait for all the social-buttons to be upgraded.
Promise.all(promises).then(() => {
  // All social-button children are ready.
});
從頭定義一個剛好可用的元素不容易?。?/b>

?到這里我想大家已經(jīng)對Custom Element API有所認識了,下面我們嘗試自定義一個完整的元素吧。不過再實操前,我們先看看一個剛好可用的元素應(yīng)該注意哪些細節(jié)。

明確各階段適合的操作

1.constructor
?用于初始化元素的狀態(tài)和設(shè)置事件監(jiān)聽,或者創(chuàng)建Shadow Dom。
2.connectedCallback
?資源獲取和元素渲染等操作適合在這里執(zhí)行,但該方法可被調(diào)用多次,因此對于只執(zhí)行一次的操作要自帶檢測方案。
3.disconnectedCallback
?適合作資源清理等工作(如移除事件監(jiān)聽)

更細的細節(jié)

1.constructor中的細節(jié)
1.1. 第一句必須調(diào)用super()保證父類實例創(chuàng)建
1.2. return語句要么沒有,要么就只能是returnreturn this
1.3. 不能調(diào)用document.writedocument.open方法
1.4. 不要訪問元素的特性(attribute)和子元素,因為元素可能處于undefined狀態(tài)并沒有特性和子元素可訪問
1.5. 不要設(shè)置元素的特性和子元素,因為即使元素處于defined狀態(tài),通過document.createElementnew方式創(chuàng)建元素實例時,本應(yīng)該是沒有特性和子元素的
2.打造focusable元素 by tabindex特性
?默認情況下自定義元素是無法獲取焦點的,因此需要顯式添加tabindex特性來讓其focusable。另外還要注意的是若元素disabledtrue時,必須移除tabindex讓元素unfocusable。
3.ARIA特性
?通過ARIA特性讓其他閱讀器等其他訪問工具可以識別我們的自定義元素。
4.事件類型轉(zhuǎn)換
?通過addEventListener捕獲事件,然后通過dispathEvent發(fā)起事件來對事件類型進行轉(zhuǎn)換,從而觸發(fā)更符合元素特征的事件類型。

下面我們來擼個x-btn

class XBtn extends HTMLElement{
  static get observedAttributes(){ return ["disabled"] }
  constructor(){
    super()

    this.addEventListener("keydown", e => {
      if (!~[13, 32].indexOf(e.keyCode)) return  

      this.dispatchEvent(new MouseEvent("click", {
        cancelable: true,
        bubbles: true
      }))
    })

    this.addEventListener("click", e => {
      if (this.disabled){
        e.stopPropagation()
        e.preventDefault()
      }
    })
  }
  connectedCallback(){
    this.setAttribute("tabindex", 0)
    this.setAttribute("role", "button")
  }
  get disabled(){
    return this.hasAttribute("disabled")
  }
  set disabled(val){
    if (val){
      this.setAttribute("disabled","")
    }
    else{
      this.removeAttribute("disabled")
    }
  }
  attributeChangedCallback(attrName, oldVal, newVal){
    this.setAttribute("aria-disabled", !!this.disabled)
    if (this.disabled){
      this.removeAttribute("tabindex")
    }
    else{
      this.setAttribute("tabindex", "0")
    }
  }
}
customElements.define("x-btn", XBtn)
如何開始使用Custom Element v1?

?Chrome54默認支持Custom Element v1,Chrome53則須要修改啟動參數(shù)chrome --enable-blink-features=CustomElementsV1。其他瀏覽器可使用webcomponets.js這個polyfill。

題外話一番

?關(guān)于Custom Element我們就說到這里吧,不過我在此提一個有點怪但又確實應(yīng)該被注意到的細節(jié)問題,那就是自定義元素是不是一定要采用來聲明呢?能否采用的方式呢?
?答案是不行的,由于自定義元素屬于Normal Element,因此必須采用這種開始標(biāo)簽和閉合標(biāo)簽來聲明。那么什么是Normal Element呢?
其實元素分為以下5類:

Void elements
?格式為,包含以下元素area,base,br,col,embed,hr,img,keygen,link,meta,param,source,track,wbr

Raw text elements
?格式為,包含以下元素script,style

escapable raw text elements
?格式為,包含以下元素textarea,title

Foreign elements
?格式為,MathML和SVG命名空間下的元素

Normal elements
?格式為,除上述4種元素外的其他元素。某些條件下可以省略結(jié)束標(biāo)簽,因為瀏覽器會自動為我們補全,但結(jié)果往往會很吊軌,所以還是自己寫完整比較安全。

總結(jié)

?當(dāng)頭一回聽到Custom Element時我是那么的興奮不已,猶如找到根救命稻草似的。但如同其他新技術(shù)的出現(xiàn)一樣,利弊同行,如何判斷和擇優(yōu)利用是讓人頭痛的事情,也許前人的經(jīng)驗?zāi)芙o我指明方向吧!下篇《WebComponent魔法堂:深究Custom Element 之 從過去看現(xiàn)在》,我們將穿越回18年前看看先驅(qū)HTML Component的黑歷史,然后再次審視WebComponent吧!
?尊重原創(chuàng),轉(zhuǎn)載請注明來自:http://www.cnblogs.com/fsjohn... ^_^肥仔John

感謝

How to Create Custom HTML Elements
A vocabulary and associated APIs for HTML and XHTML
Custom Elements v1
custom-elements-customized-builtin-example

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/87951.html

相關(guān)文章

  • WebComponent魔法:深究Custom Element 面向痛點編程

    摘要:前言最近加入到新項目組負責(zé)前端技術(shù)預(yù)研和選型,一直偏向于以為代表的技術(shù)線,于是查閱各類資料想說服老大向這方面靠,最后得到的結(jié)果是資料是英語無所謂,最重要是上符合要求,技術(shù)的事你說了算。但當(dāng)我們需要動態(tài)實例化元素時,命令式則是最佳的選擇。 前言 ?最近加入到新項目組負責(zé)前端技術(shù)預(yù)研和選型,一直偏向于以Polymer為代表的WebComponent技術(shù)線,于是查閱各類資料想說服老大向這方面...

    flyer_dev 評論0 收藏0
  • JS魔法:那些困擾你的DOM集合類型

    摘要:五的子類對象會返回一個集合對象,集合內(nèi)存儲類型的元素。七的子類初看很有可能以為集合元素就是單選表單元素,其實可以存儲任意類型的表單元素。八的子類開始,將返回子類的對象,其行為特征和一致。但在前,我們應(yīng)該先了解清楚的類型的特征。 一、前言                            大家先看看下面的js,猜猜結(jié)果會怎樣吧! 可選答案: ①. 獲取id屬性值為id的節(jié)點元素 ②...

    468122151 評論0 收藏0
  • CSS魔法:小結(jié)一下Box Model與Positioning Scheme

    摘要:魔法堂重新認識和魔法堂你一定誤解過的魔法堂就這個樣魔法堂說說那個被埋沒的志向深入細節(jié)后會發(fā)現(xiàn)中定位模式之間,和之間存在千絲萬縷的關(guān)系,必須以俯瞰的角度捋一下。當(dāng)采用時,屬性的實際值會被重置為。由于和則需要通過來引入來提供盒子定位微調(diào)的功能。 前言 ?對于Box Model和Positioning Scheme中3種定位模式的細節(jié),已經(jīng)通過以下幾篇文章記錄了我對其的理解和思考。?《CSS...

    szysky 評論0 收藏0
  • CSS魔法:"那不是bug,是你不懂我!" by inline-block

    摘要:那不是,是我不懂而已。的用途之一西文是以空格來分隔單詞的,而漢字間則無需空格分隔,但為了統(tǒng)一西文東亞和的排版,于是抽象出一個名為的概念用于分隔詞義單元,則作為的值域,而定義域就是語言信息。 前言 每當(dāng)來個需要既要水平排版又要設(shè)置固定高寬時,我就會想起display:inline-block,還有為了支持IE5.5/6/7的hack*display:inline;*zoom:1;。然后發(fā)...

    cucumber 評論0 收藏0
  • CSS魔法:你真的理解z-index嗎?

    摘要:與的映射關(guān)系為。與根對應(yīng)的對應(yīng)的層疊上下文,是其他的祖先,的范圍覆蓋整條。注意的默認值為,自動賦值為。對于,它會將賦予給對應(yīng)的,而則不會。 一、前言                               ?假如只是開發(fā)簡單的彈窗效果,懂得通過z-index來調(diào)整元素間的層疊關(guān)系就夠了。但要將多個彈窗間層疊關(guān)系給處理好,那么充分理解z-index背后的原理及兼容性問題就是必要的知識...

    andycall 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<