自定义标签在IE6-8的困境

2015/07/20 · HTML5 ·
IE,
自定义标签

原文出处:
司徒正美   

或许未来前端组件化之路都是自定义标签,但这东西早在20年前,JSTL已在搞了。现在Web
Component还只有webkit支持。但一个组件库,还需要一个特殊的标识它们是一块的。不过这个XML已经帮我们搞定了,使用scopeName,如”<xxx:dialog>”。在我继续往下想如何处理如何为这个标签绑定数据,与其他组件通信,管理生命周期,等等大事之前,我还有一个不得不面对的问题,就是如何兼容IE6-8!

比如以下一个页面:

图片 1

在chrome, firefox, IE11, IE11的IE6兼容模式分别如下:

图片 2
图片 3
图片 4
图片 5

我们会发现IE6下实际是多出许多标签,它是把闭标签也变成一个独立的元素节点

图片 6

这个AA:DIV标签被开膛破肚,里面子节点全部暴出来,成为其兄弟节点了。因此想兼容它,就要费点劲。有个两个情况需要考虑,1是用户已经将它写在页面上,情况同上;2是用户是将它放在字符串模版中,这个用正则搞定。不过正则要是碰上复杂的属性名,还是会晕掉。因此我还是打算使用原生的HTML
parser。换言之,字符串,我还是会将它变成节点。这么办呢?!我想了许多办法,后来还是使用VML的命名空间法搞定!

我们将上面的页面改复杂点,再看看效果!

图片 7
图片 8

可以看到其套嵌关系现在完全正确,并且标签名不会大写化,也不会生成多余节点!

好了,我们再判定一下是否为自定义标签,或者准确地说,这个节点是否我们组件库中定义的自定义标签。某些情况下,一个页面可以存在多套组件库,包括avalon的,ploymer的,或者是直接用Web
Component写的。

avalon的组件库将使用命名空间,这样就好区别开。在IE6-9中,判定element.scopeName是否为aa(这是组件库的命名空间,你可以改个更高大上的名字),在其他浏览器判定此元素的localName是否以aa:开头就行了!

JavaScript

function isWidget(el, uiName){ return el.scopeName ? el.scopeName ===
uiName: el.localName.indexOf(uiName+”:”) === 0 }

1
2
3
function isWidget(el, uiName){
  return   el.scopeName ? el.scopeName === uiName: el.localName.indexOf(uiName+":") === 0
}

这个难题解决后,我们就可以开搞基于自定义标签的UI库了!

1 赞 1 收藏
评论

图片 9

      这样使用变量i已经被重复定义了,所以需要把变量i定义在if之前,即:

tp.type = tp.type || {};
tp.type.isArray = function(ele) {
    return "[object Array]" === Object.prototype.toString.call(ele);
};
tp.type.isFunction = function(ele) {
    return "[object Function]" === Object.prototype.toString.call(ele);
};
tp.type.isObject = function(ele) {
    return ("function" === typeof ele) || !!(ele && "object" === typeof ele);
};
tp.type.isString = function(ele) {
    return "[object String]" === Object.prototype.toString.call(ele);
};
tp.type.isNumber = function(ele) {
    return "[object Number]" === Object.prototype.toString.call(ele) && isFinite(ele);
};
tp.type.isBoolean = function(ele) {
    return "boolean" === typeof ele;
};
tp.type.isElement = function(ele) {
    return ele && ele.nodeType == 1;
};
tp.type.isUndefined = function(ele) {
    return "undefined" === typeof ele;
};

       
 首先,类型判定,由于JS是弱类型语言,而有时候是需要判定它的类型的,当然也可以使用typeof
去判定,暂时我是这么做的:

   
 define的第一个参数是该组件的名字(需要唯一,所以我还是按照命名空间的方式写的),第二个参数是这个组件所依赖的组件,第三个参数是回调函数,也就是当依赖的组件下载完成之后,回调执行,而tp.modules.add就可以将这个模块加载到整个库中,这样的话才能使用tp.use调用。

  

           我认为:#aa
input这种实际上就是通过document.getElementById查询之后然后查询它的子孙节点中的所有满足tagName为input的元素;而#aaa
>
input这种就是查询它的孩子节点中是否有这种满足条件的元素。现在整个流程比较简单了,对于一个复杂查询,首先进行一个简单查询,然后按照查询的结果集合,进行一次遍历,对每个节点查询它的孩子节点或子孙节点,将所有满足条件的放入到另外一个数组,如果该数组为空,那么直接返回空数组,否则,继续进行下一次查询(依旧查询孩子节点或子孙节点)。

          事件之后,当然就是DOM了,感觉每个库在这个上面都做了很多工作。

        然后就是浏览器判定,我是这么写的:

     
我之前写了一篇日志来实现AMD,当然,效率低下,反正大家看看就行了http://my.oschina.net/mingtingling/blog/113815

tp.event.preventDefault = function(event) {
        if(event.preventDefault) {
            event.preventDefault();
        } else {
            event.returnValue = false;
        }
    };
    tp.event.stopPropagation = function(event) {
        if(event.stopPropagation) {
            event.stopPropagation();
        } else {
            event.cancelBubble = true;
        }
    };

     
 好吧,貌似又超时了,先就这样吧,感觉每次写这种日志都会耗费不少时间。

       
到寒假的时候,决定自己的毕设不使用现在成熟的JS库,反而自己来写一个不完善的库,这样学习的更多,当然,也比较费时间。

var _getElementsByClassName = null;
        if(document.getElementsByClassName) {
                _getElementsByClassName = function(str) {
                    var fetchedEles = document.getElementsByClassName(str),
                        eles = [];

                    for(var i = 0, len = fetchedEles.length; i < len; i++) {
                        eles.push(fetchedEles[i]);
                    }
                    return eles;
                };
        } else {
            _getElementsByClassName = function(str,openClassScan) {
                var eles = [],
                    allElements = document.getElementsByTagName("*"),
                    i;
                if(openClassScan) {
                    for (i = 0; i< allElements.length; i++ ) {
                        if (tp.dom.containClass(allElements[i],str)) {
                            eles.push(allElements[i]);
                        }
                    }
                } else {
                    for (i = 0; i< allElements.length; i++ ) {
                        if (str === allElements[i].className) {
                            eles.push(allElements[i]);
                        }
                    }
                }
                return eles;
            };
        }

       
 然后就开始下载JS的电子书,可能是自己比较浮躁吧,看书真心看不进去,我还是喜欢边看边写代码这种。写了一段时间,渐渐的觉得最开始的感觉慢慢回来了,当然,也遇到了N多的问题。

       
 这里我主要讲一下tp.dom.query,也就是查询怎么做的,首先看看常用的查询有:#aa,.aa,input。

           那这样,tp.event.on就变得非常简单了:

     
 当然,还有浏览器版本的判定,暂时就不贴出来了。这里基本思路就是判定navigator.useAgent返回的字符串中,每个浏览器里面的这个字符串是不一样的,当然,这个过程比较恶心,而且有可能后面某一个浏览器会改变它的userAgent,导致整个判定失效,比如IE,听别人说后面新IE要把userAgent搞成firefox,真心搞不懂,这是要逆天吗?

       
写这个库,首先使用了命名空间,我比较喜欢toper,所以我首先定义了一个变量:

     
然后就是事件了,事件是一个比较恼火的事情,东西比较多,我把它放在了tp.event这个空间中。

           
PS:使用了原生的document.getElementsByClassName的肯定不受这个影响的。

       
 从去年暑假的时候,我决定离开jquery了,jquery是一把双刃剑,开发的时候是方便,但是,作为一个初学者,我觉得这是很不利的。

           我把每一个查询如:tp.dom.query(“#aa
input”)分为两种,一种为简单查询(也就是如查询:#aaa),另外一种是复杂查询,每个复杂查询都是由很多简单查询构成的,比如#aaa
input,就可以切成:#aaa和input。

var tp = tp || {};

       
 事件这一块儿实际上我做了N多东西,但是由于讲不完,所以暂时不说了。

     
这种方式我在tangram中没有看到,我是看了淘宝的KISSY之后学习到的,也就是所谓的AMD(异步模块定义)。

   
 也就是在一个函数内部去判定是否是IE,然后相应的执行相应的函数,但是这样,如果添加的事件监听器很多,每次都if什么的,我个人感觉很不好,所以我后面添加了一个辅助函数:

       
对动画有兴趣的童鞋,可以看看我的最近学习JS的感悟-2,关于动画的http://my.oschina.net/mingtingling/blog/127296

(function() {
    var ua = navigator.userAgent;
    tp.browser.isIe = ua.hasString("MSIE") && !ua.hasString("Opera");
    tp.browser.isFirefox = ua.hasString("Firefox");
    tp.browser.isChrome = ua.hasString("Chrome");
    tp.browser.isWebKit = ua.hasString("WebKit");
    tp.browser.isGecko = ua.hasString("Gecko") && !ua.hasString("like Gecko");
    tp.browser.isOpera = ua.hasString("Opera");
    tp.browser.isSafari = ua.hasString("Safari") && !ua.hasString('Chrome');
    tp.browser.isStrict = ("CSS1Compat" === document.compatMode);
})();
tp.event.on = function(element,event,fn) {
        _addEventListener(element,event,fn,false);
         };

          我这儿写了一个辅助函数:

       
开始写的感觉真是痛苦啊,什么都不懂,所以就去看了看tangram的源码,为什么看tangram呢,其实原因比较搞笑,当时校招的时候我面试百度前端,被刷掉了,当时面试官让我看看它们百度使用的JS库tangram,我就想看看它们那个库到底有什么了不起的。。。

      同样,在tp.a.js中,也不能使用普通的JS的写法了,而要使用:

     
 除了代码之外,工具也很重要,另一篇日志介绍JS工具的:http://my.oschina.net/mingtingling/blog/113295

var arr = new Array(),
    i;