文章出處

前言

 說起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 class="alert alert-warning alert-dismissible fade in">\
                        <button type="button" class="close" aria-label="Close">\
                          <span aria-hidden="true">&times;</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.html

<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給我驚喜的地方吧!

  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可謂是重中之重,后續繼續研究研究:)
 尊重原創,轉載請注明來自:http://www.cnblogs.com/fsjohnhuang/p/5987853.html ^_^肥仔John

感謝

《從HTML Components的衰落看Web Components的危機》
HTC Reference
Using HTML Components to Implement DHTML Behaviors in Script


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()