KISSY字符串创建节点的bug(DOM.create)

关于bug认定:之所以认为这是bug,主要参考了jQuery实现的效果。KISSY在开发中参考了其他类库的优点,jQuery自然是其中之一。

bug描述:当利用S.Node方法将字串转换为NodeList,返回的对象是DocumentFragment对象,与预期不符。

var ps = S.Node("<p></p><p></p>").appendTo(document.body);
console.log(ps[0]); // 预期是第一个p对象,而firebug中显示,返回的是DocumentFragment对象,又因为已经append过了,在DocumentFragment对象中也不存在p元素对象了,且不存在ps[1]。如果只创建一个元素是不会有这个问题的。

bug原因分析:在创建大量元素的情况下,基于性能等原因,我们会将元素先存放到超空间中,在完成所有元素的创建以后一起添加到页面中。一般我们都是用DocumentFragment对象来存放这些创建的元素对象。初步判断KISSY保持chain特性的时候错误的将fragment对象添加到this中返回了。

源码分析:由于本人是在使用S.Node方法的时候发现这个bug的。

故在node模块(build/node/node-pkg.js)中查找Node方法,代码如下:

function Node(html, props, ownerDocument) {
    /* .. some code*/

    // handle element or text node
    if (nodeTypeIs(html, 1) || nodeTypeIs(html, 3)) {
        domNode = html;
    }
    else if (S.isString(html)) {
        domNode = DOM.create(html, props, ownerDocument);
    }

    self[0] = domNode;
}

可以看到,当传入的参数是节点或者文本对象的时候,已经就完成操作了。而如果是字符串,则调用了DOM.create方法来完成操作。
我们再看看DOM.create方法。在dom-create模块(build/dom/dom-pkg.js)中,create方法:

create: function(html, props, ownerDoc) {
    if (nodeTypeIs(html, 1) || nodeTypeIs(html, 3)) return cloneNode(html);
    if (isKSNode(html)) return cloneNode(html[0]);
    if (!(html = S.trim(html))) return null;

    var ret = null, creators = DOM._creators,
        m, tag = DIV, k, nodes;

    // 简单 tag, 比如 DOM.create('')
    if ((m = RE_SIMPLE_TAG.exec(html))) {
        ret = (ownerDoc || doc).createElement(m[1]);
    }
    // 复杂情况,比如 DOM.create('<img src="sprite.png" />')
    else {
        if ((m = RE_TAG.exec(html)) && (k = m[1]) && S.isFunction(creators[(k = k.toLowerCase())])) {
            tag = k;
        }

        nodes = creators[tag](html, ownerDoc).childNodes;

        if (nodes.length === 1) {
            // return single node, breaking parentNode ref from "fragment"
            ret = nodes[0][PARENT_NODE].removeChild(nodes[0]);
        }
        else {
            // return multiple nodes as a fragment
            ret = nl2frag(nodes, ownerDoc || doc);
        }
    }

    return attachProps(ret, props);
}

最终聚焦到1530line,nl2frag方法。同时从注释也可以看到,与bug出现的情景是一致的。而nl2frag方法的代码如下:

function nl2frag(nodes, ownerDoc) {
    var ret = null, i, len;

    if (nodes && (nodes.push || nodes.item) && nodes[0]) {
        ownerDoc = ownerDoc || nodes[0].ownerDocument;
        ret = ownerDoc.createDocumentFragment();

        if (nodes.item) { // convert live list to static array
            nodes = S.makeArray(nodes);
        }

        for (i = 0,len = nodes.length; i < len; i++) {
            ret.appendChild(nodes[i]);
        }
    }
    else {
        S.log('Unable to convert ' + nodes + ' to fragment.');
    }

    return ret;
}

这里返回的ret就是DocumentFragment对象。再看create方法,在最后返回的时候经过attachProps处理。
props是传入的另一个参数,api的解释是元素的属性,命名上也说得通。让我们看看attachProps做了哪些操作:

function attachProps(elem, props) {
    if (isElementNode(elem) && S.isPlainObject(props)) {
        DOM.attr(elem, props);
    }
    return elem;
}

实际上只是把相关的属性挂载到创建的对象上。最终返回了elem,即ret,即DocumentFragment对象。

基本上就是这样的了。

另外,附加发现DOM.create方法创建单个元素的时候返回的不是数组。。。在我看来这算是不符预期,应该也是bug吧。。

附上简单的测试代码文件

posted on 2010-09-27 21:32  Akecn  阅读(2002)  评论(0编辑  收藏  举报