使用Javascript将大块HTML插入元素的最佳实践是什么?

20

我正在构建一个网络应用程序(使用原型)需要将大块的HTML添加到DOM中。其中大部分是包含各种属性元素的行。

目前,我将空白行的HTML保存在变量中,并且

var blankRow = '<tr><td>'
    +'<a href="{LINK}" onclick="someFunc(\'{STRING}\');">{WORD}</a>'
    +'</td></tr>';

function insertRow(o) {
    newRow = blankRow
        .sub('{LINK}',o.link)
        .sub('{STRING}',o.string)
        .sub('{WORD}',o.word);
    $('tbodyElem').insert( newRow );
}

现在这个代码能够正常工作,但是它是否是最佳实践呢?

每当我在页面上更新代码时,我都必须更新 blankRow 中的代码,以便插入的新元素都是相同的。当我有40行 HTML 代码要放在 blankRow 中,并且还必须进行转义时,这样做就很麻烦。

有没有更简单的方法?我考虑过使用 URL 编码,然后在插入之前进行解码,但这仍然意味着需要一个 blankRow,并且需要进行大量转义。

最好的情况是有一种类似于 PHP 等的 EOF 函数。

$blankRow = <<<EOF
text
text
EOF;

这意味着不需要转义,但仍需要一个空行。

在这种情况下你该怎么办?

已解决

最终使用原型中的 DOMBuilder。 不需要其他库:

$w('a div p span img table thead td th tr tbody tfoot input').each(function(e) {
        window['$' + e] = function() {
            return new Element(e, arguments[0]);
        }
});

newPart = $div({id: 'partition-4'})
    .insert( $p()
        .insert('<b>Stuff</b>')
    )
    .insert( $p({
        id: 'a-p'})
        .insert('<b>More stuff</b>')
    );

$('parentDiv').insert(newPart);

请查看我的解决方案,这里这里


你的点击函数总是一个字符串吗?如果它是一个实际的函数会更好,但不确定是否有这个选项。另外,你是否在使用jQuery?我无法完全确定你的语法,但看起来你是在使用它。 - Nick Craver
不建议连续向DOM插入许多节点。你应该一次性完成。尝试先将它们插入到一个容器对象中,然后再从对象插入到DOM中。 - vsync
不,click函数只是一个例子,大多数时候我使用数字来减少我需要转义的数量。是的,这是原型,本应该采用jQuery的方式,但现在为时已晚。 - hamstar
嘿,vsync,我应该先创建容器对象,例如 container = document.createElement('tbody'),然后将所有的 tr 放入其中,最后执行 $('actualTable').insert(container.childElements()) 吗?或者是 .insert( container.innerHTML ) - [如果我没记错的话,insert 需要一个字符串参数...] - hamstar
3个回答

20

这是最佳实践吗?

不是。事实上,您遇到了HTML注入问题,可能导致错误,并且在注入的字符串中可能包含用户提交的内容的最坏情况下,还会出现XSS安全漏洞。

当您将纯文本内容和属性值放入HTML字符串中时,您必须对它们进行HTML编码。在PHP中,您必须对进入HTML的字符串调用htmlspecialchars()来执行此操作。在JavaScript中,您没有内置的HTML转义函数,因此必须自己制作一个,例如,通过使用s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/"/g, '&quot;')处理进入HTML的字符串。

onclick="someFunc(\'{STRING}\');"

这是一种全新级别的转义混乱。在事件处理程序属性中的JavaScript字符串文字中,您必须JS编码该字符串(\转义'\加上一些Unicode字符以实现完整性),然后再对结果进行HTML编码。否则,该字符串可能会突破其字符串文字分隔符并注入任意JS代码。在所有情况下都要避免使用内联事件处理程序属性,尤其是在模板中。

使用HTML字符串创建页面内容是很糟糕的。您很可能会出现转义错误并危及应用程序的安全性。改用类似DOM的方法,您就不必担心这个问题了。您似乎正在使用jQuery,因此请尝试jQuery 1.4元素创建快捷方式:

$('<tr>').append(
    $('<td>').append(
        $('<a>', {
            href: o.link,
            text: o.word,
            onclick: function() {
                someFunc(o.string);
            }
        })
    )
);

或者,将空行作为HTML实际嵌入文档中,但在开始时隐藏它(display: none),或将其从文档中分离(removeChild或jQuery detach)。然后,在需要新行时,克隆空行并进行所需更改:

var blankRow= $('#blankRow').detach();

    ...

var newRow= blankRow.clone();
var link= newRow.find('td>a');
link.attr('href': o.link);
link.text(o.word);
link.click(function() {
    someFunc(o.string);
});

如果必须从字符串模板创建内容,请确保您的模板化函数默认情况下对每个替换进行HTML转义,并通过选择已解析内容内部的节点调用click(function() { ... })来附加事件。或者使用事件委派(例如jQuery的live())来处理事件,而无需在添加新节点时绑定到它们。


1
真是个艰巨的任务。感谢你的帮助。现在真希望我使用的是jQuery而不是Prototype :( - hamstar
2
@hamstar:糟糕!我应该注意到那是一个原型ID选择器$而不是jQuery。天啊。原型有一个类似的(在我看来:某些方面更好的)创建元素的快捷方式,new Element。请参见http://prototypejs.org/2007/5/12/dom-builder。 - bobince
最终我用这种方式做了,效果非常好!花了一些时间才弄清楚。如果有人想看代码,请访问http://gist.github.com/402668或http://pastebin.com/Du5Ejjjj。非常感谢您的信息,Bobince! - hamstar
最佳实践是使用文档片段来提高性能(它们在添加到DOM之前存储在内存中),特别是对于循环元素。这是John Resig在他的博客上发表的一篇文章http://ejohn.org/blog/dom-documentfragments/。 - yoshyosh
@bobince 很棒的回复。在阅读了我最初几本关于Web开发的书籍后,这篇帖子比所有书籍加起来更让我对最佳实践有所启迪。 - Mackie Messer
显示剩余2条评论

5
你使用了一种很好的方法,考虑到其他方式,速度相当快。
另一种替代方案是在JavaScript中构建要使用的DOM元素,通过调用多个document.createElement(tagName)函数来实现。但是,在我看来,你的代码将会迅速增长,而收益甚微。
我的另一种喜欢的创建DOM的方式是将要复制的代码放置在HTML主体内部,给它一个类名和/或ID,例如"template",并且使用CSS将其隐藏,然后在需要时根据事件获取元素,克隆它,并设置所需的属性,最后将其附加到它所属的位置上。

1

如果我理解得正确,你的问题是关于如何像在PHP(或Perl)中那样初始化大字符串?对于长(多行)字符串,我使用以下方法:

var blankRow = [
     'a line',
     'another line',
     'still more lines',
     'lines lines lines',
     '... etc'
    ].join('')

你也可以使用反斜杠进行换行,但这样可能会有些混乱:

var blankRow = 'a line\
another line\
still more lines\
lines lines lines\
... etc';

根据评论进行编辑:我也很懒!所以我使用这个来转义:

function qs(str){
    str = str || this || '';
    return "'"+str+"'";
}

function qd(str){
    str = str || this || '';
    return '"'+str+'"';

}

String.prototype.qs = qs;
String.prototype.qd = qd;

// applied:
var blankRow = [
         'a line',
         'another '+qd('line'),
         'still more lines',
         'lines '+'lines'.qs()+ ' lines',
         '... etc'
        ].join('');

懒惰的问题在于:坚持自己的懒惰其实是相当辛苦的工作。


啊,这是一个聪明的做法。我提到 PHP 的例子是因为你不需要在中间转义任何东西 ;) <-- 懒惰 - hamstar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接