但随着参加项目标前端规模稳步扩展

        }

// IE5678

};

    var htmlDocSelectors = htmlElSelectors.concat([‘getElementById’,
‘getElementsByName’]);

</form>

    // for (var len = nodes.length; i < len; ++i){

复制代码

     * @method

 

复制代码

十、隐藏的武士刀三: document.scripts                                  
                                   

<a href=”javascript:%20void%200;”>links</a>

};

0级DOM武士刀                          

            wraperFactory(node);

       var i = 0;

// IE9

    var wraperFactory = function(node){

   document.all.item({String} id或name)[{Number} 索引];

 

        else{

 

  获取文档中所有拥有href属性的a和area对象的引用。但在IE5678中
document.links是个类函数,而在Webkit和Molliza中是个HTMLCollection对象。

        }

 

 

        }

        var nativeGetById = host.getElementById;

 

            }

五、无法更改执行上下文的this引用?                        

// Webkit、Molliza

   document.all.item({String} id或name, {Number} 索引); 

<a name=”a1″ id=”b1″>anchor1</a>

    //
IE567下,获取id属性值或name属性值匹配的所有元素,返回一个有函数功能的[object
Object]对象

三、被遗忘的小伙伴getElementsByClassName                                
           

<a name=”a1″ id=”b2″>anchor2</a>

 

   document.all.tags({String} tag)({Number} 索引); 

复制代码

document.getElementsByName(‘inner’).length; // 0

    (! + [1,]) && wraperFactory(doc);

 

        };

Function.prototype.just4Test = function(){

    };

            nodes = removeNestForm(nodes);

 

 

         * @return {HTMLElementNode|Null}

        var ret = node;

    document.all({Number} 索引); // 获取第一个元素(指定索引值的元素)

 

    document.all({String} id或name); 

   // IE8+,只返回第一个元素

复制代码

复制代码

// 下面的代码将会抛异常

 

十五、隐藏的武士刀八:
document.applets                       

 

            }

 

   document.all.item({String} id或name)[{Number} 索引];

document.getElementsByTagName(‘form’)[1].length; //
undefined,非嵌套的form节点.length没有input节点时返回0,而嵌套的form节点.length必定返回undefined

var anchors = document.anchors;

// IE8+、Webkit和Molliza下均显示div

    var removeNestForm = function(node){

            for (var i = 0, len = node.length; i < len; ++i){

<a name=”a3″ id=”b3″>anchor3</a>

 

document.getElementsByName(‘inner’).length; // 0

    document.all({String} id或name); 

 

        if (node === null || typeof node === ‘undefined’) return null;

            , i = 0, currNS, currWraper;

console.log(node.tagName.toLocaleLowerCase());

 

  首先这里说的原始选择器是指除 querySelector 、 querySelectorAll
外的其他选择器。从前我只使用 getElementById
获取元素并没有觉得有什么问题,但随着参与项目的前端规模逐步扩大,踩的坑就越来越多,于是将踩过的和学习过的经验教训记录在这里,供以后好查阅。

 

复制代码

   document.all.item({String} id或name, {Number} 索引); 

 

            }

  1. HTMLElement的选择器: getElementsByTagName 、 getElementsByClassName 

 

   //
IE567,获取id属性值或name属性值匹配的所有元素,返回一个有函数功能的[object
Object]对象

            var nodes = nativeGetByName.call(host, tag);

                var nodes = doc.all[id];

十二、隐藏的武士刀五:
document.anchors                        

document.links[{Number} 索引];

 

    //
IE5678下,获取NodeList,但在IE567中通过Object.prototype.toString.call()获取内部类型时,返回的是[object
Object]

 
上面说到的选择器是各大浏览器厂商都支持,而IE独创的选择器我想大家都会想到是
document.all ,但这个类函数水可不浅,下面让我们来踩一下吧!

html

                isNestForm(tmp) || ret.push(tmp);

四、IE567下getElementById的诡异行为                                    
                

    nsWrapers.getElementsByClassName = function(node){

var a = document.getElementsByTagName(‘a’)[0];

            wraperFactory(nodes);

 
0级DOM:在W3C标准DOM起草前,由网景公司定义的节点操控API,并后来作为W3C标准的0级DOM规范。

 

   //
IE5678,通过标签名获取匹配的所有元素,返回一个有函数功能的[objectg
Object]对象

       // }

anchors[{Number|String} 索引]; // 返回指定位置的元素

html:

 

    document.all[`id或name`];

  IE5678、Webkit和Molliza都会排除嵌套的form元素,而IE9会保留form元素。

    <form name=”inner” id=”inner” class=”inner”>

anchors({String} id或name); // 返回第一个id或name匹配的元素

// IE567下显示span

        var nativeGetByTagName = host.getElementsByTagName;

 * @param {String} cls 类名

            wraperFactory(nodes);

    //   node = nodes[i];

     * @param {Node} node DOM元素

错误的示例:

    document.all({String} id或name, 索引); // 抛异常

 

 

// 获取指定位置的元素对象

 

        var host = node;

 

// Webkit和Molliza中通过id或name属性值获取元素对象

    // 选择器加工工厂对象

   // IE8+,只返回一个HTMLCommentElement对象

 
获取文档中所有embed的对象引用。该方法在IE5678下返回的是一个类函数,在Webkit、Molliza下返回一个HTMLCollection对象。

 

 

document.links({String} id或name);

         */

 

    };

 

     */

         * @method

document.getElementById(‘inner’); // 1,dom树有嵌套的form节点

 
 获取文档中所有script对象的引用。但从IE5678到Webkit、Molliza都包含以自闭合格式声明的script对象
<script /> ,正确的声明格式是 <script></script> 。

console.log(typeof document.getElementById.just4Test); // 返回undefined

复制代码

document.getElementById = function(id){

 

        /**
修复IE567下document.geElementById会获取name属性值相同的元素

anchors.length; // 返回4,包含links

 

    document.all[{Number} 索引];

anchors[{Number|String} 索引]; // 返回指定位置的元素

    document.all(); // 获取第一个元素(指定索引值的元素)

 

    // if (!nodes) return null;

 

        return ret;

复制代码

document.getElementsByTagName(‘form’).length; //
2,dom树有嵌套的form节点

         */

document.form[0].length; // 2

// IE5678中通过id或name属性值获取元素对象

一、前言                                                                
                                  

 

            if (node.nodeType === 1){

        <input type=”text” name=”innerInput”/>

html

 

六、IE5678下选择器的原型链上少了Function?                              
                      

十四、隐藏的武士刀七:
document.embeds                      

   // IE5678,获取指定位置的元素(HTMLElement)

   document.all.item({String} id或name)({Number} 索引);

        return function(id){

            for (var i = node.length – 1; i >= 0; –i){

 

复制代码

        if (node.nodeType === 1){

    nsWrapers.getElementsByName = function(node){

 

 

 * @param {Node} node DOM元素

 

        }

 

 

 

}(window, document);

// 以getElementsByName为例

     */

 

 

     * @param {String} cls 类名

 

        return function(tag){

   return nativeGetByName.call(this, name); 

<div id=”dummy”></div>

        /**
修复IE5678下document.geElementsByName没有继承Function方法的诡异行为

  总结一句,若要使用那就使用 document.all[{String} id或name]
就好了(其他返回的是正常的NodeList嘛),其它用法能不用就坚决不用吧。

 

        return nodes;

复制代码

    </form>

 

            return nodes;

// IE5678、Webkit和Molliza,会排除嵌套的form元素

 

                var tmp = node[i];

 

十三、隐藏的武士刀六:
document.images                      

 
 写到这里我想有人会说哪有人会写嵌套form的啊,确实能写出这种html结构出来的,我也十分佩服。总结一句,真心请大伙不要嵌套form。下面我们再罗列出

         * @method

  对于像我这样被专注于管理类后台系统开发的伪前端码农来说,
getElementsByClassName
确实是见都没见过,因为IE5678原生就不支持它。但从命名可知其功能就是,它是通过类名选择元素。那么我们就可以polyfill一下了。

            ret = isNestForm(node) ? null : node;

   if (node && node.id !== id){

   console.log(‘just4Test’); 

复制代码

            }

 

但在IE5678中
document.scripts是个类函数,而在Webkit和Molliza中是个HTMLCollection对象。在IE5678下的具体玩法如下:

 

九、隐藏的武士刀二: document.links
                       

  效果和document.embeds一样

  1. form元素个数差异

 

// IE5678、Webkit和Molliza中获取指定位置的元素对象

下面代码级的验证:

 

 
无论是在w3c还是其他渠道查阅都被告知该函数用于获取页面上所有form元素,当然这点说得一点都没有错,但不够深入。那么如何深入呢?那么就要从form的嵌套入手了。

 

document.getElementsByClassName(‘inner’).length; // 0

                node.className.search(new RegExp(‘\\b’ + cls +
‘\\b’, ‘i’)) >= 0 && nodes.push(node);

   /** IE5678中用于判断是否为嵌套form

 
获取文档中所有style和link的CSSStyleSheet类型对象的引用,与document.getElementsByTagName(‘style’)和document.getElementsByTagName(‘link’)获取的是HTMLStyleElement类型对象是不同的,在IE5678中是一个类函数,Webkit和Molliza中是一个StyleSheetList类型对象(属于NodeList类型,想了解跟多NodeList和HTMLCollection可留意另一篇《JS魔法堂:那些困扰你的DOM集合类型》)。由于涉及的边幅过大,因此打算另开一篇《JS魔法堂:哈佬,css.js!》

 

// Webkit和Molliza

  1. HTMLDocument的选择器: getElementById 、 getElementsByName 、
    getElementsByTagName、 getElementsByClassName 

 

            var nodes = _getElementsByClassName(host, cls);

     * @param {HTMLFormElement} form

  自从知道
Function.prototype.call、Function.prototype.apply和Fucntion.prototype.bind
后,锁定执行上下文(EC)的this引用变得十分的简单(具体的polyfill可浏览《一起Polyfill系统:Function.prototype.bind的四个阶段》)。但倘若你想通过锁定getElementById、getElementsByName的this引用,从而达到选择根节点的动态变换,那将掉进另一个坑中。

nativeGetId.call(a, ‘innerImg’);

 

  var node = nativeGetById.call(this, id);

 

                if (currWraper = nsWrapers[currNS]){

        var nativeGetByName = host.getElementsByName;

首先这里说的原始选择器是指除
querySelector 、 querySelectorAll 外的其他选择器。从前我只使用
getElementById 获取元素并没有觉得有什么…

 

void function(global, doc){

复制代码

   document.all.tags({String} tag); 

 

 

            nodes = removeNestForm(nodes);

            }

二、HTMLDocument和HTMLElement下的常规选择器                            
       

 

 

 

 

};

  获取文档中所有img的对象引用。
该方法在IE5678下返回的是一个类函数,在Webkit、Molliza下返回一个HTMLCollection对象。

 

 

document.links[{String} id或name];

    document.all({String} id或name, {Number} 索引); // 获取HTMLElement

   document.all.item({String} id或name);

document.getElementsByName = function(name){

    };

   // IE567,返回元素(HTMLElement)

复制代码

 

document.links({Number} 索引);

   document.all.item({String} id或name);

        else if (node.length){

 

 

 
 获取文档中所有applet的对象引用。该方法在IE5678下返回的是一个类函数,在Webkit、Molliza下返回一个HTMLCollection对象。

七、IE独创的选择器                                                      
                                 

 

 
通过在Chrome的调试工具可查看Webkit解析生成的DOM树结构,是不生产嵌套的form元素的,并且将嵌套的form节点下的子节点提取到上一级。而在IE5678下,通过调试工具发现DOM树中依然包含嵌套的form元素节点,但其下的子节点被提取到上一级。而IE9下的嵌套form节点在DOM树中被完整的构建,因此不仅DOM中包含嵌套的form节点,而且其子节点并没有被提取到上一级。

// IE9,保留嵌套的form元素

            return node;

 

                nodes = nodes.concat(_getElementsByClassName(node,
cls));

var _getElementsByClassName = function(node, cls){

 

十六、隐藏的武士刀九: document.plugins
                      

 

};

                var i = 0;

 

document.form[0].length; // 1

 

    //   if (node && node.id === id) break;

 

        for (;(curr = forms[i++], curr && curr !== form);){}

return node;

 

 
 获取文档中所有锚对象(HTMLAnchorElement)的引用。该方法在IE5678下返回的是一个类函数,在Webkit、Molliza下返回一个HTMLCollection对象。并且在IE5678和Webkit、Molliza的获取的锚对象个数也不同。

复制代码

 

            while (currNS = ns[i++]){

                wraperFactory(node[i]);

 

            ret = [];

 

    var isNestForm = function(form){

document.forms.length; // 返回2

    <input type=”text” name=”outerInput”/>

 

 

    document.all({String} id或name)({Number} 索引); // 获取HTMLElement

    return nodes;

            node.className.search(new RegExp(‘\\b’ + cls + ‘\\b’,
‘i’)) >= 0 && nodes.push(node);

   }

 

 

   document.all.tags({String} tag)[{Number} 索引]; 

         */

var node = document.getElementById(“dummy”);

    /** 通过类名选择元素

 

         * @param {String} id

 
事实证明IE5678下选择器的原型链没有Function,那选择器就无法共享各种对Function原型的增强了,所以我们需要通过一层薄薄的封装来处理。

    nsWrapers.getElementsByTagName = function(node){

document.getElementById(‘inner’); //
null,dom树没有嵌套的form节点所以找不到

 

 

  1. form节点下表单节点的差异

    };

     * @method 

var nativeGetById = document.getElementById;

示例:

    };

 

                    node[currNS] = currWraper(node);

anchors[{String} id或name]; // 返回第一个id或name匹配的元素

 

        return !curr;

        return function(cls){

        var forms = document.forms, i = 0, curr;

document.scripts({Number} 索引);

 

            node = removeNestForm(node);

针对上述IE的bug我们可以进行简单的修复

十一、隐藏的武士刀四: document.styleSheets
                      

// IE5678

   document.all.item(); // 获取第一个元素

            var nodes = nativeGetByTagName.call(host, tag);

 
通过望文生义,getElementById理应只返回id属性值匹配的元素,而IE8+、webkit和molliza也是这样做的。但IE567却不遵循这一法则,它们会获取id属性值或name属性值匹配的元素,然后以第一个匹配的元素作为返回值。

        var host = node;

            nodes = removeNestForm(nodes);

 

     var nodes = document.all[id];

 

};

复制代码

  也许你看到这个标题的时候会认为这是不可能的事,因为
document.getElementById.call 是真实存在的呀。但 document.getElementById
instanceof Function
居然返回false,现在头大了吧。让我们再通过下面对Function原型增强来验证一下吧!

                for (;(node = nodes && nodes[i++] || null, node &&
node.id !== id);){}

            return nodes;

// IE5678

 

 

 

 

        while(node = seed[i++]){

// IE5678中获取指定位置的元素对象

            return nodes;

        };

        var host = node;

document.forms.length; // 返回1

    var htmlElSelectors = [‘getElementsByTagName’,
‘getElementsByClassName’];

 

        var seed = node.childNodes, nodes = [], i = 0, node;

 

        if (node.tagName && node.tagName.toLocaleLowerCase() ===
‘form’){

        } 

    var seed = node.childNodes, nodes = [], i = 0, node;

 

javascript

 

   document.all.item({Number} 索引);

 

/** 通过类名选择元素

根据现象推测,getElementId内部实现可能是针对特定的DOM对象而工作的,所以当强行改变this引用时,就会跑异常。

 

        if (!node) return void 0;

 

javascript

八、隐藏的武士刀一: document.forms                                    
                                

 

        /**
修复IE5678下document.geElementsByTagName没有继承Function方法的诡异行为

        var host = node;

 

   document.all.item({String} id或name)({Number} 索引);

 

 

    }

            nodes = nodes.concat(_getElementsByClassName(node, cls));

                }

 
 这里对getElementById,getElementsByTagName,getElementsByName进行了封装从而继承Function,并polyfill了getElementsByClassName,并排除嵌套form的问题。

    };

     * @return {Boolean}

 

        }

  return _getElementsByClassName(cls);

  通过 form元素.length 可获取其下的 input节点 个数,通过
form元素[{Number} 索引] 获取指定位置的 input元素 。

    };

 

    nsWrapers.getElementById = function(node){

 * @method

       for (;(node = nodes && nodes[i++] || null, node && node.id !==
id);){} 

    //
IE5678下,获取的是指定索引值的元素HTMLElement通过Object.prototype.toString.call()获取内部类型时,返回的是[object
Object]

 

<form name=”outer” id=”outer”>

        };

    // 上面的for循环是把玩语法而已,效果和下面的一样

    var nsWrapers = {};

 

            var node = nativeGetById.call(host, id);

 

 

<span name=”dummy”></span>

document.form[0].length; // 2

anchors.length; // 返回3

         *
修复IE5678下document.geElementById没有继承Function方法的诡异行为

         * @method

 

 */

 

 

    while(node = seed[i++]){

        };

            if (node && node.id !== id){

    //
IE8下,获取的是第一个匹配的元素HTMLElement通过Object.prototype.toString.call()获取内部类型时,返回的是[object
Object]

document.getElementsByTagName(‘form’).length; //
1,dom树没有嵌套的form节点所以找不到

    var _getElementsByClassName = function(node, cls){

    

    };

var nativeGetId = document.getElementById;

 

 

十七、完整实现                                  

document.getElementsByClassName = function(cls){

            wraperFactory(nodes);

document.form[1].length; // 1

复制代码

        return function(tag){

 

 

        if (node.tagName !== ‘form’ && node.length && node[0]){

            var ns = !node.ownerDocument ? htmlDocSelectors :
htmlElSelectors

 

复制代码

var nativeGetByName = document.getElementsByName;

document.scripts[{Number} 索引];

// Webkit和Molliza

相关文章