在Javascript中如何对包含换行符的HTML实体进行反转义?

4
如果您有一个包含 HTML 实体的字符串并希望将其反转义,建议使用以下解决方案或其变体:
function htmlDecode(input){
  var e = document.createElement('div');
  e.innerHTML = input;
  return e.childNodes.length === 0 ? "" : e.childNodes[0].nodeValue;
}

htmlDecode("<img src='myimage.jpg'>"); 
// returns "<img src='myimage.jpg'>"

例如,可以参考这个回答:https://dev59.com/o3I-5IYBdhLWcg3wVWpi#1912522

只要字符串不包含换行符并且我们不在运行早于10版的Internet Explorer上(测试版本为9和8),这个方法就能正常工作。

如果字符串包含换行符,则IE 8和9将其替换为空格,而不是像Chrome、Safari、Firefox和IE 10一样保持不变。

htmlDecode("Hello\nWorld"); 
// returns "Hello World" on IE 8 and 9

有没有解决方案适用于IE 10版本之前的浏览器?

你想对换行符做什么?删除它吗? - David Hellsing
我想保持它不变。我会添加一个例子。 - mgd
我不确定它是否完全符合您的需求,但您是否查看过这个HTML编码和解码JavaScript库 - freefaller
1个回答

4
最简单的方法,但可能不是最有效的解决方案是,让htmlDecode()仅对字符和实体引用进行操作。
var s = "foo\n&amp;\nbar";
s = s.replace(/(&[^;]+;)+/g, htmlDecode);

更高效的方法是使用优化后的htmlDecode()重写,它仅在每个输入中调用一次,仅作用于字符和实体引用,并重复使用DOM元素对象:
function htmlDecode (input)
{
  var e = document.createElement("span");

  var result = input.replace(/(&[^;]+;)+/g, function (match) {
    e.innerHTML = match;
    return e.firstChild.nodeValue;
  });

  return result;
}

/* returns "foo\n&\nbar" */
htmlDecode("foo\n&amp;\nbar");

Wladimir Palant指出了此函数存在XSS问题:一些(HTML5)事件监听器属性的值,如onerror,如果你将带有这些指定属性的元素的HTML赋值给innerHTML属性,则会被执行。因此,你不应该在包含实际HTML的任意输入上使用此函数,只能用于已经转义的HTML。否则,你应该相应地调整正则表达式,例如使用/(&[^;<>]+;)+/来防止匹配包含标签的&…;

对于任意的HTML,请参阅他的替代方法,但请注意它的兼容性没有本方法高。


1
谢谢。运行得很好。我建议您编辑示例字符串“foo & bar”,以包括“\n”字符,如此“foo\n&\nbar”,以说明代码正确处理换行符。另外,您能否解释一下为什么'e'涉及循环引用? - mgd
2
循环引用(如理解有误请指正):ee.ownerDocument → (e.ownerDocument.defaultView === window) → window.htmlDecodewindow.htmlDecode.[[Scope]]e。问题在于(旧版)JScript的GC即使e超出范围也无法清除它。请参见:了解和解决Internet Explorer泄漏模式 - PointedEars
1
@PointedEars:学到了新东西。谢谢! - Aaron Digulla
请考虑限制正则表达式接受的字符,这样它就不会匹配 HTML 标记,例如 /(&[^;<>\s]+;)+/。然后您的 htmlDecode 函数将可以安全地用于不安全的输入,而原始函数无法做到这一点(请参见我的答案)。 - Wladimir Palant
@WladimirPalant 我已经加了一个警告。如果有选择的话,应该更喜欢你在答案中提出的方法。 - PointedEars
显示剩余13条评论

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