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

自定义标签在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

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

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;
};

  

         
在使用事件代理的时候,我们经常要获取到事件的目标元素,而IE和非IE又是不一样的,所以需要单独写一个函数:

     
暂时AMD的实现方式是通过setInterval,但是即将被重构网赌平台哪个信誉好 10

 

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

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

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

       
 #aa这种比较简单,因为JS提供了API,也就是document.getElementById;input这种也比较好搞,因为JS有document.getElementsByTagName;但是.aa这种方式就比较纠结了,因为JS没有提供API,幸好,在一些浏览器中还是提供了API:document.getElementsByClassName,而那些没有提供这个API的就比较悲剧了,只能遍历所有节点,也就是使用document.getElementsByTagName(*):

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

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

     
我采用的结构是core+组件的方式,tp.core.js(压缩后为tp.core-min.js),而其他的组件每个组件一个文件,而组件之间可能存在依赖关系,这种依赖关系就通过AMD解决。

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

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

           
我这儿写了一个openClassScan参数,解释一下,这个参数是为了解决类似于<div
class = “a
b”></div>这种,因为如果要支持通过API查询如class:a,那么需要每个节点都判定是否contain这个class,比较费时间,而我认为很多时候不需要,所以默认我关闭了。

       
现在看来,我觉得学习jquery反而使我走了弯路,用jquery是比较方便,也不用考虑兼容性问题了,而且调用非常简单优雅,但是,反而我对原生js感觉越来越陌生了,也导致了后面感觉完全离不开jquery了,想去写一个XXX组件,想了一下,思路有了,然后写的时候遇到各种问题,然后就又回到jquery了。

       
这种方式也是借鉴了tangram的写法,采用对象字面量的形式。这样所有toper定义的方法都放在了tp这个私有空间内了,比如对DOM的操作就放在tp.dom中。

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

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的兼容性真心很头疼啊,就比如在DOM这一块儿,为了兼容,我都做了很长时间。当然,DOM这一块儿肯定不止这么一点内容,暂时也不写了。

     
首先是添加和移除事件监听器,由于IE和非IE采用的方式不一样,IE采用attachEvent和detechEvent,非IE采用addEventListener和removeEventListener,而且IE只支持冒泡(从当前元素冒泡到根元素),而非IE支持冒泡和捕获(从根元素捕获到当前元素)。最开始我是这样做的:

           所以,在每个查询的最开始,需要将传递的查询格式化,比如#aa
>input这种格式化为:#aa >
input,多个空格变为1个空格,>两边必须有一个空格等。

         
常用的功能当然还是阻止事件冒泡以及阻止默认事件的发生,很遗憾,IE和非IE处理方式还是不一样的,比如阻止冒泡IE采用的是cancelBubble,而其他浏览器采用的是stopPropagation,所以还是需要写:

     
 除了这种判定方式,还可以通过判定是否有某一个函数或某一个变量来判定,这种判定方式我忘记叫什么名字了,反正之前这种叫浏览器嗅探。

tp.event.on = function(element,event,fn) {
        _addEventListener(element,event,fn,false);
         };
tp.event.getTarget = function(event) {
        return event.target || event.srcElement;
    };

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

       
我看了一下,不同的库的判定方式不一样,我这儿使用的是tangram的判定方式。

tp.use("tp.dom.sizzle",function(sizzle) {});

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

         
而且这样还有一个好处,之前的方式只能采用冒泡方式,但现在,可以使用捕获,当然,只能非IE才能使用,这样在后面使用事件代理一些非冒泡的事件的时候非常有用,比如blur和focus事件。

        注意一下啊,由于JS变量作用域没有block,所以请不要使用下面这种:

     
使用use方式,它会自动去下载tp.a.js和tp.b.js,下载完成之后,执行回调函数。

You can leave a response, or trackback from your own site.

Leave a Reply

网站地图xml地图