摘要:前言最近加入到新項目組負責(zé)前端技術(shù)預(yù)研和選型,一直偏向于以為代表的技術(shù)線,于是查閱各類資料想說服老大向這方面靠,最后得到的結(jié)果是資料是英語無所謂,最重要是上符合要求,技術(shù)的事你說了算。但當(dāng)我們需要動態(tài)實例化元素時,命令式則是最佳的選擇。
前言
?最近加入到新項目組負責(zé)前端技術(shù)預(yù)研和選型,一直偏向于以Polymer為代表的WebComponent技術(shù)線,于是查閱各類資料想說服老大向這方面靠,最后得到的結(jié)果是:"資料99%是英語無所謂,最重要是UI/UX上符合要求,技術(shù)的事你說了算。",于是我只好乖乖地去學(xué)UI/UX設(shè)計的事,木有設(shè)計師撐腰的前端是苦逼的:(嘈吐一地后,還是擠點時間總結(jié)一下WebComponent的內(nèi)容吧,為以后作培訓(xùn)材料作點準備。
浮在水面上的痛 組件噪音太多了!?在使用Bootstrap的Modal組件時,我們不免要Ctrl+c然后Ctrl+v下面一堆代碼
?一個不留神誤刪了一個結(jié)束標簽,或拼錯了某個class或?qū)傩阅蔷捅吡耍藭r一個語法高亮、提供語法檢查的編輯器是如此重要啊!但是我其實只想配置個Modal而已。
?由于元素信息由標簽標識符,元素特性和樹層級結(jié)構(gòu)組成,所以排除噪音后提取的核心配置信息應(yīng)該如下(YAML語法描述):
dialog: modal: true children: header: title: Modal title closable: true body: children: p: textContent: One fine body… footer children: button: type: close textContent: Close button: type: submit textContent: Save changes
轉(zhuǎn)換成HTML就是
而像Alert甚至可以極致到這樣
是不是很簡單啊?
可惜瀏覽器木有提供
既然瀏覽器木有提供,那我們自己手寫一個吧!
復(fù)盤找問題?雖然表面上實現(xiàn)了需求,但存在2個明顯的缺陷
不完整的元素實例化方式
原生元素有2種實例化方式
聲明式
命令式
// 元素實例化 const input = new HTMLInputElement() // 或者 document.createElement("INPUT") input.type = "text" // 添加到DOM樹 document.querySelector("#mount-node").appendChild(input)
?由于聲明式注重What to do,而命令式注重How to do,并且我們操作的是DOM,所以采用聲明式的HTML標簽比命令式的JavaScript會來得簡潔平滑。但當(dāng)我們需要動態(tài)實例化元素時,命令式則是最佳的選擇。于是我們勉強可以這樣
// 元素實例化 const myAlert = new Alert() // 添加到DOM樹 document.querySelector("#mount-node").appendChild(myAlert.el) /* 由于Alert無法正常實現(xiàn)HTMLElement和Node接口,因此無法實現(xiàn) document.querySelector("#mount-node").appendChild(myAlert) myAlert和myAlert.el的差別在于前者的myAlert是元素本身,而后者則是元素句柄,其實沒有明確哪種更好,只是原生方法都是支持操作元素本身,一下來個不一致的句柄不蒙才怪了 */
?即使你能忍受上述的代碼,那通過innerHTML實現(xiàn)半聲明式的動態(tài)元素實例化,那又怎么玩呢?是再手動調(diào)用一下registerElement("alert", el => new Alert(el))嗎?
?更別想通過document.createElement來創(chuàng)建自定義元素了。
有生命無周期
?元素的生命從實例化那刻開始,然后經(jīng)歷如添加到DOM樹、從DOM樹移除等階段,而想要更全面有效地管理元素的話,那么捕獲各階段并完成相應(yīng)的處理則是唯一有效的途徑了。
?當(dāng)定義一個新元素時,有3件事件是必須考慮的:
元素自閉合: 元素自身信息的自包含,并且不受外部上下文環(huán)境的影響;
元素的生命周期: 通過監(jiān)控元素的生命周期,從而實現(xiàn)不同階段完成不同任務(wù)的目錄;
元素間的數(shù)據(jù)交換: 采用property in, event out的方式與外部上下文環(huán)境通信,從而與其他元素進行通信。
?元素自閉合貌似無望了,下面我們試試監(jiān)聽元素的生命周期吧!
?通過constructor我們能監(jiān)聽元素的創(chuàng)建階段,但后續(xù)的各個階段呢?可幸的是可以通過MutationObserver監(jiān)聽document.body來實現(xiàn):)
最終得到的如下版本:
"use strict" class Alert{ constructor(el = document.createElement("ALERT")){ this.el = el this.el.fireConnected = () => { this.connectedCallback && this.connectedCallback() } this.el.fireDisconnected = () => { this.disconnectedCallback && this.disconnectedCallback() } this.el.fireAttributeChanged = (attrName, oldVal, newVal) => { this.attributeChangedCallback && this.attributeChangedCallback(attrName, oldVal, newVal) } const raw = el.innerHTML el.dataset.resolved = "" el.innerHTML = `總結(jié)${raw}` el.querySelector("button.close").addEventListener("click", _ => this.close()) } close(){ this.el.style.display = "none" } show(){ this.el.style.display = "block" } connectedCallback(){ console.log("connectedCallback") } disconnectedCallback(){ console.log("disconnectedCallback") } attributeChangedCallback(attrName, oldVal, newVal){ console.log("attributeChangedCallback") } } function registerElement(tagName, ctorFactory){ [...document.querySelectorAll(`${tagName}:not([data-resolved])`)].forEach(ctorFactory) } function registerElements(ctorFactories){ for(let k in ctorFactories){ registerElement(k, ctorFactories[k]) } } const observer = new MutationObserver(records => { records.forEach(record => { if (record.addedNodes.length && record.target.hasAttribute("data-resolved")){ // connected record.target.fireConnected() } else if (record.removedNodes.length){ // disconnected const node = [...record.removedNodes].find(node => node.hasAttribute("data-resolved")) node && node.fireDisconnected() } else if ("attributes" === record.type && record.target.hasAttribute("data-resolved")){ // attribute changed record.target.fireAttributeChanged(record.attributeName, record.oldValue, record.target.getAttribute(record.attributeName)) } }) }) observer.observe(document.body, {attributes: true, childList: true, subtree: true}) registerElement("alert", el => new Alert(el))
?千辛萬苦擼了個基本不可用的自定義元素模式,但通過代碼我們進一步了解到對于自定義元素我們需要以下基本特性:
自定義元素可通過原有的方式實例化(
可通過原有的方法操作自定義元素實例(如document.body.appendChild等)
能監(jiān)聽元素的生命周期
下一篇《WebComponent魔法堂:深究Custom Element 之 標準構(gòu)建》中,我們將一同探究H5標準中Custom Element API,并利用它來實現(xiàn)滿足上述特性的自定義元素:)
?尊重原創(chuàng),轉(zhuǎn)載請注明來自: http://www.cnblogs.com/fsjohn... ^_^肥仔John
感謝Custom ELement
Custom ELement v1
MutationObserver
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/80535.html
摘要:明確各階段適合的操作用于初始化元素的狀態(tài)和設(shè)置事件監(jiān)聽,或者創(chuàng)建。事件類型轉(zhuǎn)換通過捕獲事件,然后通過發(fā)起事件來對事件類型進行轉(zhuǎn)換,從而觸發(fā)更符合元素特征的事件類型。 前言 ?通過《WebComponent魔法堂:深究Custom Element 之 面向痛點編程》,我們明白到其實Custom Element并不是什么新東西,我們甚至可以在IE5.5上定義自己的alert元素。但這種簡單...
摘要:而同步和異步則是描述另一個方面。異步將數(shù)據(jù)從內(nèi)核空間拷貝到用戶空間的操作由系統(tǒng)自動處理,然后通知應(yīng)用程序直接使用數(shù)據(jù)即可。 前言 ?上周5在公司作了關(guān)于JS異步編程模型的技術(shù)分享,可能是內(nèi)容太干的緣故吧,最后從大家的表情看出這條粉腸到底在說啥?的結(jié)果:(下面是PPT的講義,具體的PPT和示例代碼在https://github.com/fsjohnhuan...上,有興趣就上去看看吧! ...
摘要:魔法堂重新認識和魔法堂你一定誤解過的魔法堂就這個樣魔法堂說說那個被埋沒的志向深入細節(jié)后會發(fā)現(xiàn)中定位模式之間,和之間存在千絲萬縷的關(guān)系,必須以俯瞰的角度捋一下。當(dāng)采用時,屬性的實際值會被重置為。由于和則需要通過來引入來提供盒子定位微調(diào)的功能。 前言 ?對于Box Model和Positioning Scheme中3種定位模式的細節(jié),已經(jīng)通過以下幾篇文章記錄了我對其的理解和思考。?《CSS...
摘要:與的映射關(guān)系為。與根對應(yīng)的對應(yīng)的層疊上下文,是其他的祖先,的范圍覆蓋整條。注意的默認值為,自動賦值為。對于,它會將賦予給對應(yīng)的,而則不會。 一、前言 ?假如只是開發(fā)簡單的彈窗效果,懂得通過z-index來調(diào)整元素間的層疊關(guān)系就夠了。但要將多個彈窗間層疊關(guān)系給處理好,那么充分理解z-index背后的原理及兼容性問題就是必要的知識...
摘要:不耽誤表單提交數(shù)據(jù)雖然我們無法看到的元素,但當(dāng)表單提交時依然會將隱藏的元素的值提交上去。讓元素在見面上不可視,但保留元素原來占有的位置。不過由于各瀏覽器實現(xiàn)效果均有出入,因此一般不會使用這個值。繼承父元素的值。 前言 ?還記得面試時被問起請說說display:none和visibility:hidden的區(qū)別嗎?是不是回答完display:none不占用原來的位置,而visibilit...
閱讀 1411·2021-10-08 10:04
閱讀 733·2021-09-07 09:58
閱讀 2912·2019-08-30 15:55
閱讀 2424·2019-08-29 17:21
閱讀 2126·2019-08-28 18:04
閱讀 3075·2019-08-28 17:57
閱讀 715·2019-08-26 11:46
閱讀 2228·2019-08-23 17:20