提及Custom Element那必然會想起那個類似而又以失敗了結的HTML Component。HTML Component是在IE5開始引入的新技術,用於對原生元素做功能"加強",雖然僅僅被IE所支持,雖然IE10也開始放棄它了,雖然掌握了也用不上,但仍是不影響咱們以研究的心態去了解它的:)css
HTML Component簡稱HTC,它由定義和應用兩部分組成。定義部分寫在.htc
文件中(MIME爲text/x-component),由HTC獨有標籤、JScript和全局對象(element
,window
等)組成;而應用部分則寫在html文件中,經過CSS的behavior規則附加到特定的元素上。html
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])
, 取消訂閱事件git
引入.htc
1.基本打開方式github
<style> css-selector{ behavior: url(file.htc); } </style>
2.打開多個app
<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
元素來看看具體怎麼玩吧!url
x:alert
元素x-alert.htcspa
<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 class="alert alert-warning alert-dismissible fade in">\ <button type="button" class="close" aria-label="Close">\ <span aria-hidden="true">×</span>\ </button>\ <div class="content">${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.htmlprototype
<html xmlns:x> <head> <title></title> <style> x\:alert{ behavior: url(x-alert.htc); } </style> </head> <body> <x:alert id="a" 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給我驚喜的地方吧!code
htc文件內的JScript代碼做用域爲htc文件自己,並不污染html文件的腳本上下文;
帶屬性訪問器的自定義屬性大大提升咱們對自定義屬性的可控性;
而後就是槽點了
htc行爲與元素綁定分離,好處是靈活,缺點是非自包含,每次引入都要應用者本身綁定一次太囉嗦了。我以爲Angular經過屬性E綁定元素既靈活又實現自包含纔是正路啊!
API有bug。如ondocumentready事件說好了是html文檔加載完就會觸發,按理只會觸發一下,可實際上它總會在oncontentready事件後觸發,還有fireEvent的API根本就沒有,只能說繼承了IE一如既往的各類坑。
經過runtimeStyle來設置inline style,從而會丟失繼承、層疊等CSS特性的好處;
自定義元素內部結構會受到外部JS、CSS的影響,並非一個真正閉合的元素。
很抱歉本文的內容十分對不住標題所述,更全面的觀點請查看徐飛老師的《從HTML Components的衰落看Web Components的危機》。假如單獨看Custom Element,其實它跟HTML Component無異,都沒有完整的解決自定義元素/組件的問題,但WebComponent除了Custom Element,還有另外3個好夥伴(Shadow DOM,template,html imports)來一塊兒爲自定義元素提供完整的解決方案,其中Shadow DOM可謂是重中之重,後續繼續研究研究:)
尊重原創,轉載請註明來自:http://www.cnblogs.com/fsjohn... ^_^肥仔John
《從HTML Components的衰落看Web Components的危機》
HTC Reference
Using HTML Components to Implement DHTML Behaviors in Script