751
技術社區[雲棲]
WebComponent魔法堂:深究Custom Element 之 從過去看現在
前言
說起Custom Element那必然會想起那個相似而又以失敗告終的HTML Component。HTML Component是在IE5開始引入的新技術,用於對原生元素作功能"增強",雖然僅僅被IE所支持,雖然IE10也開始放棄它了,雖然掌握了也用不上,但還是不影響我們以研究的心態去了解它的:)
把玩HTML Component
HTML Component簡稱HTC,它由定義和應用兩部分組成。定義部分寫在.htc
文件中(MIME為text/x-component),由HTC獨有標簽、JScript和全局對象(element
,window
等)組成;而應用部分則寫在html文件中,通過CSS的behavior規則附加到特定的元素上。
定義部分
**HTC獨有標簽**
_PUBLIC:COMPONENT
, 根節點._
_PUBLIC:PROPERTY
, 定義元素公開自定義屬性/特性._
屬性
NAME
,html文件中使用的屬性名
INTERNALNAME
,htc文件內使用的屬性名,默認與NAME
一致
VALUE
,屬性默認值
PUT
,setter對應的函數名
GET
,getter對應的函數名
_PUBLIC:EVENT
, 定義元素公開自定義事件._
屬性
NAME
,公開事件名稱,如onheadingchange
ID
,htc內使用的事件名稱,如ohc.然後通過ohc.fire(createEventObject())
來觸發事件
_PUBLIC:ATTACH
,訂閱事件_
屬性
EVENT
,訂閱的事件名稱,如onheadingchange
ONEVENT
,事件處理函數體,如headingchangehandler()
FOR
,事件發生的宿主(element
,document
,window
,默認是element
)
_PUBLIC:METHOD
, 定義元素公開方法_
屬性
NAME
,html文件中使用的方法名
INTERNALNAME
,htc文件內使用的方法名,默認與NAME
一致。在JScript中實現具體的方法體
_PUBLIC:DEFAULTS
,設置HTC默認配置_
**HTC生命周期事件**ondocumentready
, 添加到DOM tree時觸發,在oncontentready後觸發oncontentready
, 添加到DOM tree時觸發ondetach
, 脫離DOM tree時觸發, 刷新頁麵時也會觸發oncontentsave
, 當複製(ctrl-c)element內容時觸發
**HTC全局對象**element
, 所附加到的元素實例runtimeStyle
,所附加到的元素實例的style屬性document
,html的文檔對象
**HTC全局函數**createEventObject()
,創建事件對象attachEvent(evtName, handler)
, 訂閱事件.注意:一般不建議使用attachEvent來訂閱事件,采用<PUBLIC:ATTACH>
來訂閱事件,它會自動幫我們執行detach操作,避免內存泄露.detachEvent(evtName[, handler])
, 取消訂閱事件
應用部分
引入.htc
1.基本打開方式
<style>
css-selector{
behavior: url(file.htc);
}
</style>
2.打開多個
<style>
css-selector{
behavior: url(file1.htc) url(file2.htc);
}
</style>
可以看到是通過css-selector匹配元素然後將htc附加到元素上,感覺是不是跟AngularJS通過屬性E指定附加元素的方式差不多呢!
3.自定義元素
<html xmlns:x>
<head>
<style>
x\:alert{
behavior: url(x-alert.htc);
}
</style>
</head>
<body>
<x:alert></x:alert>
</body>
</html>
自定義元素則有些麻煩,就是要為自定義元素指定命名空間x:alert
,然後在html節點上列出命名空間xmlns:x
。(可多個命名空間並存<html xmlns:x xmlns:y>
)
下麵我們來嚐試定義一個x:alert
元素來看看具體怎麼玩吧!
自定義x:alert
元素
x-alert.htc
<PUBLIC:COMPONENT>
<PUBLIC:ATTACH EVENT="oncontentready" ONEVENT="onattach()"></PUBLIC:ATTACH>
<PUBLIC:ATTACH EVENT="ondetach" ONEVENT="ondetach()"></PUBLIC:ATTACH>
<PUBLIC:METHOD NAME="close"></PUBLIC:METHOD>
<PUBLIC:METHOD NAME="show"></PUBLIC:METHOD>
<PUBLIC:PROPERTY NAME="heading" PUT="putHeading" SET="setHeading"></PUBLIC:PROPERTY>
<PUBLIC:EVENT NAME="onheadingchange" ID="ohc"></PUBLIC:EVENT>
<PUBLIC:ATTACH EVENT="onclick" ONEVENT="onclick()"></PUBLIC:ATTACH>
<script language="JScript">
/*
* private region
*/
function toArray(arrayLike, sIdx, eIdx){
return Array.prototype.slice.call(arrayLike, sIdx || 0, eIdx || arrayLike.length)
}
function curry(fn /*, ...args*/){
var len = fn.length
, args = toArray(arguments, 1)
return len <= args.length
? fn.apply(null, args.slice(0, len))
: function next(args){
return function(){
var tmpArgs = args.concat(toArray(arguments))
return len <= tmpArgs.length ? fn.apply(null, tmpArgs.slice(0, len)) : next(tmpArgs)
}
}(args)
}
function compile(tpl, ctx){
var k
for (k in ctx){
tpl = tpl.replace(RegExp('\$\{' + k + '\}'), ctx[k]
}
return tpl
}
// 元素內部結構
var tpl = '<div >\
<button type="button" aria-label="Close">\
<span aria-hidden="true">×</span>\
</button>\
<div >${raw}</div>\
</div>'
var getHtml = curry(compile, tpl)
/*
* leftcycle region
*/
var inited = 0, oHtml = ''
function onattach(){
if (inited) return
oHtml = element.innerHTML
var ctx = {
raw: heading + oHtml
}
var html = genHtml(ctx)
element.innerHTML = html
runtimeStyle.display = 'block'
runtimeStyle.border = 'solid 1px red'
}
function ondetach(){}
/*
* public method region
*/
function show(){
runtimeStyle.display = 'block'
}
function close(){
runtimeStyle.display = 'none'
}
/*
* public property region
*/
var heading = ''
function putHeading(val){
if (heading !== val){
setTimeout(function(){
var evt = createEventObject()
evt.propertyName = 'heading'
ohc.fire(evt)
}, 0)
}
heading = val
}
function getHeading(){
return heading
}
/*
* attach event region
*/
function onclick(){
if (/^\s*close\s*$/.test(event.srcElement.className)){
close()
}
}
</script>
</PUBLIC:COMPONENT>
引用x:alert
index.html
<html xmlns:x>
<head>
<title></title>
<style>
x\:alert{
behavior: url(x-alert.htc);
}
</style>
</head>
<body>
<x:alert heading="Hello world!"></x:alert>
<script language="JScript">
var a = document.getElementById('a')
a.onheadingchange = function(){
alert(event.propertyName + ':' + a.heading)
}
// a.show()
// a.close()
// document.body.appendChilid(document.createElement('x:alert'))
</script>
</body>
</html>
感受
在寫HTC時我有種寫C的感覺,先通過HTC獨有標簽聲明事件、屬性、方法等,然後通過JScript來提供具體實現,其實寫Angular2時也有這樣的感覺,不過這裏的感覺更強烈一些。
這裏先列出開發時HTC給我驚喜的地方吧!
1. htc文件內的JScript代碼作用域為htc文件本身,並不汙染html文件的腳本上下文;
2. 帶屬性訪問器的自定義屬性大大提高我們對自定義屬性的可控性;
然後就是槽點了
1. htc行為與元素綁定分離,好處是靈活,缺點是非自包含,每次引入都要應用者自己綁定一次太囉嗦了。我覺得Angular通過屬性E綁定元素既靈活又實現自包含才是正路啊!
2. API有bug。如ondocumentready事件說好了是html文檔加載完就會觸發,按理隻會觸發一下,可實際上它總會在oncontentready事件後觸發,還有fireEvent的API根本就沒有,隻能說繼承了IE一如既往的各種坑。
3. 通過runtimeStyle來設置inline style,從而會丟失繼承、層疊等CSS特性的好處;
4. 自定義元素內部結構會受到外部JS、CSS的影響,並不是一個真正閉合的元素。
總結
很抱歉本文的內容十分對不住標題所述,更全麵的觀點請查看徐飛老師的《從HTML Components的衰落看Web Components的危機》。假如單獨看Custom Element,其實它跟HTML Component無異,都沒有完整的解決自定義元素/組件的問題,但WebComponent除了Custom Element,還有另外3個好夥伴(Shadow DOM,template,html imports)來一起為自定義元素提供完整的解決方案,其中Shadow DOM可謂是重中之重,後續繼續研究研究:)
尊重原創,轉載請注明來自:https://www.cnblogs.com/fsjohnhuang/p/5987853.html ^_^肥仔John
感謝
《從HTML Components的衰落看Web Components的危機》
HTC Reference
Using HTML Components to Implement DHTML Behaviors in Script
最後更新:2017-05-05 11:31:39
上一篇:
打造高效前端工作環境-tmuxinator
下一篇:
前端魔法堂:解秘FOUC
Serverless 場景下的日誌收集和分析實踐
後PC時代較勁蘋果安卓 Windows RT深入評測
物聯網技術下的智能照明發展分析
傳未來iPad將物體“拉出”屏幕
2017微信紅包增強版:含牛牛+PC蛋蛋+接龍+掃雷等棋牌源碼下載
動態設置鏈接的Title的js方法
HTAP數據庫 PostgreSQL 場景與性能測試之 11 - (OLTP) 字符串搜索 - 後綴查詢
集成代碼生成器 java 微信 自定義菜單 java微信接口開發 公眾平台 SSM redis shiro 多數據源
我認為Win 8安裝過程界麵應該是這樣
刪除docker-register的鏡像& none無效鏡像講解