前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >WebComponent魔法堂:深究Custom Element 之 从过去看现在

WebComponent魔法堂:深究Custom Element 之 从过去看现在

作者头像
^_^肥仔John
发布2018-01-18 17:43:05
1.1K0
发布2018-01-18 17:43:05
举报

前言

 说起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.基本打开方式

代码语言:javascript
复制
<style>
  css-selector{
    behavior: url(file.htc);
  }
</style>

2.打开多个

代码语言:javascript
复制
<style>
  css-selector{
    behavior: url(file1.htc) url(file2.htc);
  }
</style>

 可以看到是通过css-selector匹配元素然后将htc附加到元素上,感觉是不是跟AngularJS通过属性E指定附加元素的方式差不多呢! 3.自定义元素

代码语言:javascript
复制
<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

代码语言:javascript
复制
<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

代码语言:javascript
复制
<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可谓是重中之重,后续继续研究研究:)

感谢

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016-10-22 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 把玩HTML Component
    • 定义部分
      • 应用部分
        • 自定义x:alert元素
          • 引用x:alert
            • 感受
            • 总结
            • 感谢
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档