小松的技术博客

六和敬

若今生迷局深陷,射影含沙。便许你来世袖手天下,一幕繁华。 你可愿转身落座,掌间朱砂,共我温酒煮茶。

DOM节点的创建

浏览器共提供了四种手段来创建节点:document.createElement、innerHTML、insertAdjacentHTML、createContextualFragment。

document.createElement

这是最为熟悉的一种创建节点的方式,传入一个标签名,然后返回此类型的元素节点。它能够成功返回的浏览器还不能支持的节点,IE6~8对HTML5的支持基于此功能实现。

但IE6-8存在一个和标准浏览器不一样的现象:IE6-8允许用户连同属性一起传入,但标准浏览器不支持此用法,例如document.createElement('<div id="aaa">')在IE6-8下生成的节点为<div id="aaa"></div>,而在标准浏览器则直接报错。但这对IE6-8其实是很重要的,因为IE6-8下input和iframe的name属性是只读的,因此可以用这种方式生成带name属性的节点。

//http://thunderguy.com/semicolon/2005/05/23/setting-the-name-attribute-in-internet-explorer/
function createNamedElement(type, name) {
    var element = null;
    // Try the IE way; this fails on standards-compliant browsers
    try {
        element = document.createElement('<'+type+' name="'+name+'">');
    } catch (e) {
    }
    if (!element || element.nodeName != type.toUpperCase()) {
        // Non-IE browser; use canonical method to create named element
        element = document.createElement(type);
        element.name = name;
    }
    return element;
}

innerHTML

innerHTML的效率是高于createElement2-10倍不等的,并且可以批量生成一堆节点。但是使用innerHTML需要一些主要的地方:

  • IE会对innerHTML的输入字符串进行trimLeft操作,即会去掉输入开始的字符串,而其它浏览器认为要忠于用户输入,故没有此操作。
  • IE下某些节点元素的innerHTML是只读的,重写会报错。其节点有:COL,COLGROUP,FRAMESET,HEAD,HTML,STYLE,TABLE,TBODY,TFOOT,THEAD ,TITLE,TR.
  • IE6-8的innerHTML会忽略掉no-scope element(注释、style、script、link、meta、noscript)。要想生成这些节点,需要在它们之前加上一些东西,比如文字或其它标签。
  • 一般情况下innerHTML不会执行script里面的脚本。jQuery团队采取的办法是把它里面的内容抽取出来然后全局eval
  • 有些标签不能单独作为div的子元素,如th、td等元素,需要在外面包几层才能正确解释,否则会当成不同文本节点解释。

createContextualFragment

createContextualtualFragment是Range对象的一个实例方法,它允许我们将字符串转换为文档碎片,然后由你决定插入到哪里。我的上一篇博文DOM之Range操作有讲到RangecreateContextualtualFragment的相关知识。

InsertAdjacentHTML

这原本也是IE的一个私有实现,不过其它一些浏览器也支持了此方法。相比其它API,这它的灵活性也是最高的,可以很方便的选择插入节点的位置:

  • afterBegin:插入到元素内部的最前面
  • beforEnd:插入到元素内部的最后面
  • beforeBegin:插入到元素的前面
  • afterEnd:插入到元素后面

如果浏览器不支持insertAdjacentHTML,我们还可以用createContextualFragment来模拟:

if(typeof HTMLElement !== "undefined" && !HTMLElement.prototype.insertAdjacentElement){
        HTMLElement.prototype.insertAdjacentElement = function(where,parseNode){
            switch(where.toLowerCase){
                case 'beforebegin':
                    this.parentNode.insertBefore(parseNode,this);
                    break;
                case 'afterbegin':
                    this.insertBefore(parseNode,this.firstChild);
                    break;
                case 'beforeend':
                    this.appendChild(parentNode);
                    break;
                case 'afterend':
                    if(this.nextSibling){
                        this.parentNode.insertBefore(parseNode,this.nextSibling);
                    }else{
                        this.parentNode.appendChild(parseNode);

                    }
                    break;
            }
        }
        HTMLElement.prototype.insertAdjacentHTML = function(where,htmlstr){
            var r = this.ownerDocument.createRange();
            r.setStartBefore(this);
            var parsedHTML = r.createContextualFragment(htmlstr);
            this.insertAdjacentElement(where,parsedHTML);
        }
        HTMLElement.prototype.insertAdjacentText = function(where,txtstr){
            var parsedText = document.createTextNode(txtstr);
            this.insertAdjacentElement(where,parsedText);
        }
    }
←支付宝← →微信 →
comments powered by Disqus