在Javascript中有效地替换HTML块中的字符串

时间:2012-02-13 15:20:11

标签: javascript html regex mootools

我使用Javascript(使用Mootools)使用HTML“模板”元素动态构建大页面,多次复制相同的模板以填充页面。在每个模板中,我使用需要替换的字符串关键字来创建唯一ID。我遇到了严重的性能问题,因为执行所有这些替换需要几秒钟,尤其是在IE中。代码如下所示:

var fieldTemplate = $$('.fieldTemplate')[0];
var fieldTr = fieldTemplate.clone(true, true);
fieldTr.removeClass('fieldTemplate');
replaceIdsHelper(fieldTr, ':FIELD_NODE_ID:', fieldNodeId);
parentTable.grab(fieldTr);

replaceIdsHelper()是根据IE9的分析器的问题方法。我尝试过这种方法的两种实现方式:

// Retrieve the entire HTML body of the element, replace the string and set the HTML back.
var html = rootElem.get('html').replace(new RegExp(replaceStr, 'g'), id);
rootElem.set('html', html);

// Load the child elements and replace just their IDs selectively
rootElem.getElements('*').each(function(elem) {
    var elemId = elem.get('id');
    if (elemId != null) elemId = elemId.replace(replaceStr, id);
    elem.set('id', elemId)
});

然而,考虑到调用此方法的次数(约200 ...),这两种方法都非常慢。其他一切运行正常,它只是取代了这些似乎是主要性能瓶颈的ID。有谁知道是否有办法有效地做到这一点,或者它可能运行得如此之慢?元素开始隐藏,直到它们被创建之后才被DOM抓取,所以没有重绘。

顺便说一下,我以这种方式构建页面的原因是为了保持代码干净,因为我们需要能够在加载后动态创建新元素。从服务器端执行此操作会使事情变得更加复杂。

2 个答案:

答案 0 :(得分:2)

我不是百分百肯定,但听起来问题是问题在于对dom树的索引。

首先,您是否必须使用ID或者您可以管理课程吗?因为你说更换id是主要问题。

另外,为什么要克隆dom树的一部分而不是只插入一个新的html? 您可以使用String的替代方法(使用MooTools时),如下所示:

var template = '<div id="{ID}" class="{CLASSES}">{CONTENT}</div>';
template.substitute({ID: "id1", CLASSES: "c1 c2", CONTENT: "this is the content" });

您可以在此处阅读更多相关信息http://mootools.net/docs/core/Types/String#String:substitute

然后,只需取出该字符串并将其作为html放入容器中,让我们说:

$("container_id").set("html", template);

我认为它可以提高效率,因为它不会克隆然后再次索引,但我不能确定。试一试,看看会发生什么。

答案 1 :(得分:1)

你可以采取一些措施来优化它 - 而@nizan tomer所说的非常好,伪模板是一种很好的模式。

首先。

var fieldTemplate = $$('.fieldTemplate')[0];
var fieldTr = fieldTemplate.clone(true, true);

你应该这样做:

var templateHTML = somenode.getElement(".fieldTemplate").get("html"); // no need to clone it.

模板本身应该/可以像建议一样,例如:

<td id="{id}">{something}</td>

只读一次,不需要为每个项克隆它 - 而是使用新的Element构造函数并设置innerHTML - 注意它缺少<tr> </tr>

如果您有一个包含数据的对象,例如:

var rows = [{
    id: "row1",
    something: "hello"
}, {
    id: "row2",
    something: "there"
}];

Array.each(function(obj, index) {
    var newel = new Element("tr", {
        html: templateHTML.substitute(obj)
    });
    // defer the inject so it's non-blocking of the UI thread:
    newel.inject.delay(10, newel, parentTable);
    // if you need to know when done, use a counter + index
    // in a function and fire a ready.
});

或者,使用文档片段:

Element.implement({
    docFragment: function(){
        return document.createDocumentFragment();
    }
});

(function() {
    var fragment = Element.docFragment();  

    Array.each(function(obj) {
        fragment.appendChild(new Element("tr", {
            html: templateHTML.substitute(obj)
        }));
    });

    // inject all in one go, single dom access
    parentTable.appendChild(fragment);
})();

我对这两种方法都进行了jsperf测试: http://jsperf.com/inject-vs-fragment-in-mootools

与Firefox和ie9相比,Chrome以惊人的优势获胜。同样令人惊讶的是,在Firefox中,单个注入比片段更快。也许瓶颈在于它是桌子上的TR,这一直是狡猾的。

对于模板:您还可以使用诸如mustache或underscore.js模板之类的东西。