如何在JavaScript中转义HTML?

35

提供一段文本

<b>This is some text</b>

我希望将其写入我的页面,以便它显示为:

<b>这是一些文本</b>

而不是像这样

这是一些文本

使用escape("<b>这是一些文本</b>")在Firefox中会得到以下结果:

%3Cb%3EThis%20is%20some%20text%3C/b%3E

不完全是我想要的。有什么想法吗?


你是想用POJS来实现这个功能还是考虑使用框架? - Brian Driscoll
1
我发现使用JQuery.text()而不是JQuery.html()可以解决问题。 - Micah
这个回答解决了你的问题吗?我能在JavaScript中转义HTML特殊字符吗? - Flimm
8个回答

62

这应该对你有用:http://blog.nickburwell.com/2011/02/escape-html-tags-in-javascript.html

function escapeHTML( string )
{
    var pre = document.createElement('pre');
    var text = document.createTextNode( string );
    pre.appendChild(text);
    return pre.innerHTML;
}

安全警告

该函数未对单引号和双引号进行转义,如果在错误的上下文中使用,仍可能导致XSS攻击。例如:

 var userWebsite = '" onmouseover="alert(\'gotcha\')" "';
 var profileLink = '<a href="' + escapeHtml(userWebsite) + '">Bob</a>';
 var div = document.getElemenetById('target');
 div.innerHtml = profileLink;
 // <a href="" onmouseover="alert('gotcha')" "">Bob</a>

感谢buffer指出了这个案例。代码片段摘自这篇博客文章


2
哇,太棒了,人们应该注意这个解决方案并投更多的赞! - darma
3
虽然它对 DOM 存在依赖,但它是一个非常好的解决方案。如果你在浏览器外使用 JavaScript,你需要以下其他解决方案之一。 - Robert J. Walker
3
它不会转义引号,你可能错误地认为将内容插入HTML是安全的。例如:http://benv.ca/2012/10/2/you-are-probably-misusing-DOM-text-methods/ - user
2
limc,请使用安全的解决方案更新此内容。目前我已经将其点踩,因为这可能会吓到一些人去实施 -- 当我看到您更新了答案后,我会取消点踩并点赞。谢谢! - Cody
@user,你的链接已经失效了。 - Flimm
这个方案是Vericode在他们的网站上使用的。这是我唯一能找到的满足自动化管道代码验证的方案。 - Carter Medlin

41

针对 HTML DOM 文档可用的情况,我喜欢 @limc 的回答。

对于非 HTML DOM 文档环境(如 Node.js),我喜欢 @Michele Bosi 和 @Paolo 的回答。

@Michael Bosi 的答案可以通过移除 4 次调用 replace 并使用聪明的替换函数来进行优化:

function escape(s) {
    let lookup = {
        '&': "&amp;",
        '"': "&quot;",
        '\'': "&apos;",
        '<': "&lt;",
        '>': "&gt;"
    };
    return s.replace( /[&"'<>]/g, c => lookup[c] );
}
console.log(escape("<b>This is 'some' text.</b>"));

使用精心选择的正则表达式可以优化Paolo的范围测试,并且可以通过使用替换函数来消除for循环:

@Paolo的范围测试可以通过精心选择的正则表达式进行优化,使用替换函数可以消除for循环:

function escape(s) {
    return s.replace(
        /[^0-9A-Za-z ]/g,
        c => "&#" + c.charCodeAt(0) + ";"
    );
}
console.log(escape("<b>This is 'some' text</b>"));

正如 @Paolo 所指出的,这种策略将适用于更多的情况。


1
Stephan,这是我见过的最优雅的解决方案 - 非常感谢你的回答![点赞]。 - Cody
2
各位,如果你们想要一个完整的解决方案,请移步至:https://github.com/janl/mustache.js/blob/master/mustache.js#L55它包含了所有字符!!感谢 @Error 指出了那篇文章,让我找到了这个方法。 - João Antunes
2
第二个适用于没有DOM的NodeJS;但是,我会扩展它以不包括许多其他常见字符。通过对不在正则表达式跳过字符列表中的所有内容进行编码,它也最兼容HTML规范的任何新特殊添加。 - James Wilkins
1
你还应该转义单引号 ('),因为在 HTML 中它可以用来代替引号包装属性值。你可以用 &apos; 替换它。 - Finesse
1
@JoãoAntunes 几年后,我发现你的评论仍然有价值,我实际上使用了来自mustache.js的那一段代码。我认为你的评论应该成为一个真正的答案,并且可能指向一个特定的文件修订版本,以显示确切的行,例如截至今天 - superjos

27

我最终做了这个:

function escapeHTML(s) { 
    return s.replace(/&/g, '&amp;')
            .replace(/"/g, '&quot;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;');
}

6
这与kapa/Headshota的回答完全相同,比你早发布了一年以上,复制他们的答案会受到-1的扣分。(添加缩进应该是编辑而不是将积分归为自己。) - Luc
你还应该转义单引号 ('),因为它在 HTML 中可以代替引号。你可以用 &apos; 替换它。 - Finesse

7

试试这个用于JavaScript的HTML实体

function htmlEntities(str) {
    return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}

适用于PHP,但问题的标签表明JavaScript是期望的解决方案语言。 - Robert J. Walker

4

传统转义

如果你使用的是XHTML,你需要使用CDATA部分。在HTML中也可以使用这些,但HTML不太严格。

我将字符串常量分开,以便此代码可以内联在CDATA块中的XHTML上运行。如果您将JavaScript作为单独的文件源,请不必担心。请注意,如果您正在使用带有内联JavaScript的XHTML,则需要将代码包含在CDATA块中,否则其中一些内容将无法正常工作。您会遇到奇怪、微妙的错误。

function htmlentities(text) {
    var escaped = text.replace(/\]\]>/g, ']]' + '>]]&gt;<' + '![CDATA[');
    return '<' + '![CDATA[' + escaped + ']]' + '>';
}

DOM文本节点

转义文本的“正确”方式是使用DOM函数document.createTextNode。这并不实际转义文本;它只是告诉浏览器创建一个文本元素,该元素本质上是未解析的。然而,您必须愿意使用DOM才能使此方法起作用:也就是说,您必须使用appendChild等方法,而不是innerHTML属性或类似的方法。这将填充具有ID an-element 的元素,并且该文本将不被解析为(X)HTML:

var textNode = document.createTextNode("<strong>This won't be bold.  The tags " +
    "will be visible.</strong>");
document.getElementById('an-element').appendChild(textNode);

jQuery DOM封装器

jQuery提供了一个方便的DOM封装器text,代替了JavaScript中的createTextNode。这非常方便。以下是使用jQuery实现相同功能的示例:

$('#an-element').text("<strong>This won't be bold.  The tags will be " +
    "visible.</strong>");

@cHao 是的。它很受欢迎,因为它很严格。你知道你会得到什么。 - Zenexer

2
这是一个可以将尖括号替换为它们的HTML实体的函数。您可能还想扩展它以包括其他字符。
function htmlEntities( html ) {
    html = html.replace( /[<>]/g, function( match ) {
        if( match === '<' ) return '&lt;';
        else return '&gt;';
    });
    return html;
}

console.log( htmlEntities( '<b>replaced</b>' ) ); // &lt;b&gt;replaced&lt;/b&gt;

2
你可以对字符串中的所有字符进行编码:
function encode(e){return e.replace(/[^]/g,function(e){return"&#"+e.charCodeAt(0)+";"})}

或者只针对需要关注的主要字符进行处理(&、inebreaks、<、>、" 和 '),例如:

function encode(r){
return r.replace(/[\x26\x0A\<>'"]/g,function(r){return"&#"+r.charCodeAt(0)+";"})
}

test.value=encode('Encode HTML entities!\n\n"Safe" escape <script id=\'\'> & useful in <pre> tags!');

testing.innerHTML=test.value;

/*************
* \x26 is &ampersand (it has to be first),
* \x0A is newline,
*************/
<textarea id=test rows="9" cols="55"></textarea>

<div id="testing">www.WHAK.com</div>


0
我使用以下函数对每个字符进行转义,使用&#nnn;标记,除了a-z A-Z 0-9空格
function Escape( s )
{
    var h,
        i,
        n,
        c;

    n = s.length;
    h = '';

    for( i = 0; i < n; i++ )
    {
        c = s.charCodeAt( i );
        if( ( c >= 48 && c <= 57 ) 
          ||( c >= 65 && c <= 90 ) 
          ||( c >= 97 && c <=122 )
          ||( c == 32 ) )
        {
            h += String.fromCharCode( c );
        }
        else
        {
            h += '&#' + c + ';';
        }
    }

    return h;
}

例子:

Escape('<b>This is some text</b>')

返回

&#60;b&#62;这是一些文本&#60;&#47;b&#62;

该函数能够防止代码注入攻击,支持Unicode,纯JavaScript。

虽然这种方法比创建DOM文本节点的方法慢约50倍,但该函数仍然可以在100-150毫秒内转义一百万(1,000,000)个字符的字符串。

(在2011年早期的MacBook Pro上测试- Safari 9 - Mavericks)


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