2

如果我有一个从 JavaScript 对象生成 HTML 代码的prototype.js 模板,我怎样才能让它自动转义 JavaScript 对象数据中的 HTML 实体,以便 HTML 不会中断?

示例 JavaScript 代码:

var data = {
    name: 'Josh',
    url: 'http://josh.gitlin.name/',
    statement: 'I\'m a JavaScript and PHP developer. Find me on Stack Overflow and say "hi"!'
}

var template = new Template(
    '<div><label>Name: <input name=\"name\" value=\"#{name}\"></label></div>'+
    '<div><label>URL: <input name=\"url\" value=\"#{url}\"></label></div>'+
    '<div><label>Personal Statement: <input name=\"statement\" value=\"#{statement}\"></label></div>'
);

$('test').update(template.evaluate(data));

上面的 JSFiddle 代码

上面的代码会产生一个格式错误的<input>标签,因为data.statement它包含一个引号"。如何在不修改对象或克隆对象Template的情况下自动转义上述示例中的 HTML 实体?​</p> datadata

4

2 回答 2

1

为了做到这一点,需要做两件事:

  1. 类的escape方法Template必须知道转义 HTML 实体
  2. escapeHTMLPrototype.js 添加到String对象的方法必须扩展为将引号编码为&quot;实体。(可能还有更多。现在太简单了......)

这两者都可以通过以下代码实现:

String.prototype.escapeHTML = String.prototype.escapeHTML.wrap(function(proceed){
    return proceed().replace(/"/g,'&quot;');
});

Template.addMethods({
    evaluateEscapeHTML: function(object) {
        if (object && Object.isFunction(object.toTemplateReplacements))
          object = object.toTemplateReplacements();

        return this.template.gsub(this.pattern, function(match) {
          if (object == null) return (match[1] + '');

          var before = match[1] || '';
          if (before == '\\') return match[2];

          var ctx = object, expr = match[3],
              pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;

          match = pattern.exec(expr);
          if (match == null) return before;

          while (match != null) {
            var comp = match[1].startsWith('[') ? match[2].replace(/\\\\]/g, ']') : match[1];
            ctx = ctx[comp];
            if (null == ctx || '' == match[3]) break;
            expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
            match = pattern.exec(expr);
          }

          return before + String.interpret(ctx).escapeHTML();
        });
    }
});

这段代码将扩展 Prototype.jsevaluateEscapeHTML并向 Templates 添加一个新方法,所以这段代码应该运行一次,从那时起,任何一个Template都会有一个新evaluateEscapeHTML方法:

var template = new Template(
    '<div><label>Name: <input name=\"name\" value=\"#{name}\"></label></div>'+
    '<div><label>URL: <input name=\"url\" value=\"#{url}\"></label></div>'+
    '<div><label>Personal Statement: <input name=\"statement\" value=\"#{statement}\"></label></div>'
);

$('test').update(template.evaluate(data));

JSFiddle 来测试这个

请注意,我必须prototype.js 1.7 版的类中复制整个方法。这有一个主要缺点,如果该方法的未来版本得到改进,我的代码也需要改进。但是,我找不到更好的方法来做到这一点。 evaluateTemplateevaluate

对我所做的方法的具体修改evaluate是更改此行:

return before + String.interpret(ctx);

至:

return before + String.interpret(ctx).escapeHTML();

就是这样,这就是为什么我无法以某种方式extendwrap其余代码如此遗憾的原因......

于 2012-07-19T19:57:01.110 回答
1

阅读源代码,模板中没有任何内容可以转义 html。此外,提供String#escapeHTML的内容非常糟糕,因为它试图用正则表达式(wtf2)剥离标签(wtf),甚至不转义引号。

您可以使用以下方法手动转义您的字符串:

function escapeHTML(text) {
    var map = {
        "&" : "amp",
        "'": "#39",
        '"': "quot",
        "<": "lt",
        ">": "gt"

    };

    return text.replace( /[&'"<>]/g, function(m) {
        return "&" + map[m] + ";";
    });
}
于 2012-07-19T19:58:06.250 回答