使用纯JavaScript从文本中删除HTML标签

845
如何使用纯JavaScript从字符串中去除HTML标签,而不使用任何库?
46个回答

907

如果你在浏览器中运行,那么最简单的方法就是让浏览器为你完成...

function stripHtml(html)
{
   let tmp = document.createElement("DIV");
   tmp.innerHTML = html;
   return tmp.textContent || tmp.innerText || "";
}

注意:正如评论中的人们所指出的,如果您不控制HTML的源代码(例如,不要在任何可能来自用户输入的内容上运行此代码),最好避免使用此方法。对于这些情况,您仍然可以让浏览器为您完成工作-请参见Saba的答案,使用现在广泛可用的DOMParser


44
请记住,这种方法相当不一致,并且在某些浏览器中会无法去除某些字符。例如,在Prototype.js中,我们使用此方法来提高性能,但是需要解决一些缺陷 - http://github.com/kangax/prototype/blob/a223833c8b49ae55f03b1e1a3a5b7e9fb647c139/src/lang/string.js#L476 - kangax
12
记得你的空格可能会被打乱。我曾使用这种方法,但遇到问题是因为某些产品代码包含双倍空格,在从 DIV 获取 innerText 后变成了单个空格。结果在应用程序后期,产品代码不匹配。 - Magnus Smith
12
@Magnus Smith:如果空格是一个问题——或者说,如果你需要这段文本的用途并不直接涉及你正在处理的特定HTML DOM——那么最好使用这里提供的其他解决方案之一。这种方法的主要优点在于它是1)微不足道的,2)可以可靠地处理标签、空格、实体、注释等,与你所运行的浏览器完全相同的方式。这对于Web客户端代码经常非常有用,但在与规则不同的其他系统交互时并不一定合适。 - Shog9
245
不要在不受信任的来源HTML中使用此内容。为了解释原因,请尝试运行 strip("<img onerror='alert(\"could run arbitrary JS here\")' src=bogus>") - Mike Samuel
28
如果HTML包含图像(img标签),浏览器将请求这些图像。这不是很好。 - douyw
显示剩余14条评论

788
myString.replace(/<[^>]*>?/gm, '');

9
如果您通过document.write注入或通过包含>的字符串进行串联后再通过innerHTML注入,则无法针对<img src=http://www.google.com.kh/images/srpr/nav_logo27.png onload="alert(42)" 执行。 - Mike Samuel
2
@PerishableDave,我同意第二个>会被保留。但这并不是注入风险。风险出现在第一个<被保留的情况下,这会导致HTML解析器在第二个开始时处于除data state之外的上下文中。请注意,没有从数据状态转换到> - Mike Samuel
126
@MikeSamuel 我们已经决定采用这个答案了吗?我这里是一个比较新手的用户,准备复制粘贴。 - Ziggy
3
如果给出类似<button onClick="dostuff('>');"></button>这样的代码,我相信它会产生混淆。即使是正确编写的HTML,您仍然需要考虑引号内可能包含大于号的情况。另外,您可能希望至少删除所有<script>标签内的文本。 - Jonathon
19
@AntonioMax,我已经一再回答了这个问题(https://dev59.com/YHVC5IYBdhLWcg3wbglT#430240),但就你的问题而言,因为“安全关键代码不应该被复制和粘贴”,所以你应该下载一个库,并保持其更新和修补程序,以便在最近发现的漏洞和浏览器变化方面保持安全。 - Mike Samuel
显示剩余19条评论

289

我想分享编辑过的Shog9批准的答案。


正如Mike Samuel在评论中指出的那样,该函数可以执行内联JavaScript代码。
但是Shog9说得对,“让浏览器为你完成...”

因此,这是我的编辑版本,使用DOM解析器

function strip(html){
   let doc = new DOMParser().parseFromString(html, 'text/html');
   return doc.body.textContent || "";
}

这里是测试内联JavaScript代码的代码:

strip("<img onerror='alert(\"could run arbitrary JS here\")' src=bogus>")

另外,它在解析过程中不会请求资源(如图像)

strip("Just text <img src='https://assets.rbl.ms/4155638/980x.jpg'>")

11
值得注意的是,这个解决方案只在浏览器中可行。 - kris_IV
1
这不是去除标签,而更像是PHP的htmlspecialchars()。对我仍然很有用。 - Daantje
2
此外,它不会尝试使用正则表达式解析HTML。 - törzsmókus
6
这应该是被接受的答案,因为这是最安全和最快的方法。 - the_previ
1
这似乎不能递归地去除HTML标签。我如何递归解析HTML为文本?谢谢。 - Teddy C
显示剩余5条评论

279

最简单的方法:

jQuery(html).text();

这会从一个 HTML 字符串中获取所有的文本。


114
由于我们的项目通常都包含大量的Javascript,所以我们总是使用jQuery。因此,我们没有添加过多代码,而是利用了现有的API代码... - Mark
40
你使用它,但原帖的作者可能不会。问题是关于JavaScript而不是jQuery。 - Rafael Herscovici
119
对于需要执行与OP相同操作(如我)且不介意使用jQuery(如我)的人来说,这仍然是一个有用的答案,更不用说如果OP正在考虑使用jQuery,它可能对他们也有用处。该网站的重点是分享知识。请记住,没有充分理由责备有用的答案可能会产生冷却效应。 - acjay
31
惊讶地发现,有多个回答的帖子对我来说是最有用的,因为通常第二个回答符合我的精确需求,而第一个回答则适用于一般情况。 - Eric G
38
如果字符串的某些部分没有用HTML标签包装起来,那么它将无法正常工作。例如,"<b>Error:</b> Please enter a valid email" 只会返回 "Error:"。 - Aamir Afridi
显示剩余17条评论

61

作为jQuery方法的一个扩展,如果你的字符串可能不包含HTML(例如,如果你想从表单字段中删除HTML),

jQuery(html).text();

如果没有HTML,将返回空字符串

使用:

jQuery('<p>' + html + '</p>').text();
更新: 根据评论所指出的,在某些情况下,如果html的值可能受到攻击者的影响,则此解决方案将执行包含在html内的javascript,请使用其他解决方案。

而不是。


15
或者 $("<p>").html(html).text(); 的意思是将一个包含 HTML 标签的字符串转换为纯文本格式的字符串。 - Dimitar Dimitrov
6
这段代码仍然会执行可能危险的操作:jQuery('<span>Text :) <img src="a" onerror="alert(1)"></span>').text() - Simon
尝试使用jQuery("aa<script>alert(1)</script>a").text(); - Grzegorz Kaczan

48

将HTML转换为纯文本电子邮件并保留超链接(a href)

hypoxide发布的上述函数效果很好,但我想要的是能够将在Web富文本编辑器(例如FCKEditor)中创建的HTML转换成纯文本,并清除所有HTML标记,但保留所有链接的内容。这是因为我希望同时拥有HTML和纯文本版本以便于创建STMP电子邮件的正确部分(包含HTML和纯文本)。

在经过长时间的谷歌搜索后,我和我的同事使用了JavaScript中的正则表达式引擎得出了以下解决方案:

str='this string has <i>html</i> code i want to <b>remove</b><br>Link Number 1 -><a href="http://www.bbc.co.uk">BBC</a> Link Number 1<br><p>Now back to normal text and stuff</p>
';
str=str.replace(/<br>/gi, "\n");
str=str.replace(/<p.*>/gi, "\n");
str=str.replace(/<a.*href="(.*?)".*>(.*?)<\/a>/gi, " $2 (Link->$1) ");
str=str.replace(/<(?:.|\s)*?>/g, "");

str 变量最初是这样的:

this string has <i>html</i> code i want to <b>remove</b><br>Link Number 1 -><a href="http://www.bbc.co.uk">BBC</a> Link Number 1<br><p>Now back to normal text and stuff</p>

然后在代码运行后,它看起来像这样:

this string has html code i want to remove
Link Number 1 -> BBC (Link->http://www.bbc.co.uk)  Link Number 1


Now back to normal text and stuff

可以看到,所有的HTML标记都被删除了,链接已经保留,超链接文本仍然完整。我还用换行符\n(换行字符)替换了<p><br>标签,以便保留一定的视觉格式。

要更改链接格式(例如BBC(Link->http://www.bbc.co.uk)),只需编辑$2(Link->$1),其中$1是href URL / URI,而$2是超链接文本。对于纯文本正文中直接使用链接,大多数SMTP邮件客户端会将其转换为用户可以单击的链接。

希望您觉得这个有用。


它无法处理“ ”。 - Rose Nettoyeur
1
强制性警告:https://dev59.com/X3I-5IYBdhLWcg3wq6do#1732454 - törzsmókus

36

对被接受答案的改进。

function strip(html)
{
   var tmp = document.implementation.createHTMLDocument("New").body;
   tmp.innerHTML = html;
   return tmp.textContent || tmp.innerText || "";
}

这样运行起来就不会有任何问题:

strip("<img onerror='alert(\"could run arbitrary JS here\")' src=bogus>")

Firefox、Chromium和Explorer 9+是安全的。 Opera Presto仍然存在漏洞。 此外,在Chromium和Firefox中,提到的图片不会被下载,这有助于节省HTTP请求。


这只是部分解决方案,但无法防止<script><script>alert();的安全问题。 - Arth
1
在Linux上的Chromium/Opera/Firefox中不运行任何脚本,那为什么它不安全呢? - Janghou
非常抱歉,我可能测试有误了,在 jsFiddle 上可能忘记再次点击运行按钮了。 - Arth
“New”这个参数似乎是多余的,我认为是吗? - Jon Schneider
根据规范,现在是可选的,但以前不是。 - Janghou
strip("aa<script>alert(1)</script>a") - Grzegorz Kaczan

36
这应该适用于任何JavaScript环境(包括NodeJS)。
    const text = `
    <html lang="en">
      <head>
        <style type="text/css">*{color:red}</style>
        <script>alert('hello')</script>
      </head>
      <body><b>This is some text</b><br/><body>
    </html>`;
    
    // Remove style tags and content
    text.replace(/<style[^>]*>.*<\/style>/g, '')
        // Remove script tags and content
        .replace(/<script[^>]*>.*<\/script>/g, '')
        // Remove all opening, closing and orphan HTML tags
        .replace(/<[^>]+>/g, '')
        // Remove leading spaces and repeated CR/LF
        .replace(/([\r\n]+ +)+/g, '');

@pstanton,你能给出一个可行的例子吗? - Karl.S
3
<html><style..>* {font-family:comic-sans;}</style>Some Text</html> 可以翻译为: <html><style..>* {字体族谱:漫画 sans;}</style>一些文本</html> - pstanton
请仔细阅读以下注意事项:https://dev59.com/X3I-5IYBdhLWcg3wq6do#1732454 - törzsmókus
1
由于没有字符串的开头或结尾锚点,因此m模式修饰符是无意义的。由于前两个模式具有共同的开始和结束,因此可以通过捕获标记名称,然后使用反向引用来 consolide 它们的内容。 - mickmackusa
@mickmackusa确实,除此之外,使用XML解析器是去除你想要的标签的最佳方法,正如törzsmókus在上面评论的那样。 - Karl.S
显示剩余2条评论

22
var text = html.replace(/<\/?("[^"]*"|'[^']*'|[^>])*(>|$)/g, "");

这是一个正则表达式版本,可以更好地处理格式不正确的HTML,例如:

未关闭的标签

一些文本 <img

标签属性中的 "<", ">"

一些文本 <img alt="x > y">

换行符

一些 <a href="http://google.com">

代码

var html = '<br>This <img alt="a>b" \r\n src="a_b.gif" />is > \nmy<>< > <a>"text"</a'
var text = html.replace(/<\/?("[^"]*"|'[^']*'|[^>])*(>|$)/g, "");

你怎么才能完全相反地翻转它?我想仅对文本部分使用 string.replace(),并保留任何HTML标签及其属性不变。 - Ade
2
我个人最喜欢的方法是,我还会加上去除换行符:`const deTagged = myString.replace(/<\/?("[^"]*"|'[^']*'|[^>])*(>|$)/g, ''); const deNewlined = deTagged.replace(/\n/g, '');` - Leigh Mathieson

19

我修改了Jibberboy2000的答案,增加了几种<BR />标记格式,删除了所有<SCRIPT><STYLE>标签内的内容,通过删除多余的换行符和空格并将一些HTML编码转换为普通文本来格式化生成的HTML。经过测试,似乎可以将大部分完整的网页转换为只保留页面标题和内容的简单文本。

在这个简单的例子中,

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<!--comment-->

<head>

<title>This is my title</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<style>

    body {margin-top: 15px;}
    a { color: #D80C1F; font-weight:bold; text-decoration:none; }

</style>
</head>

<body>
    <center>
        This string has <i>html</i> code i want to <b>remove</b><br>
        In this line <a href="http://www.bbc.co.uk">BBC</a> with link is mentioned.<br/>Now back to &quot;normal text&quot; and stuff using &lt;html encoding&gt;                 
    </center>
</body>
</html>

变成

这是我的标题

这个字符串包含我想要删除的html代码

这一行提到了带有链接的BBC (http://www.bbc.co.uk)。

现在回到“正常文本”和使用的东西

JavaScript函数和测试页面如下所示:

function convertHtmlToText() {
    var inputText = document.getElementById("input").value;
    var returnText = "" + inputText;

    //-- remove BR tags and replace them with line break
    returnText=returnText.replace(/<br>/gi, "\n");
    returnText=returnText.replace(/<br\s\/>/gi, "\n");
    returnText=returnText.replace(/<br\/>/gi, "\n");

    //-- remove P and A tags but preserve what's inside of them
    returnText=returnText.replace(/<p.*>/gi, "\n");
    returnText=returnText.replace(/<a.*href="(.*?)".*>(.*?)<\/a>/gi, " $2 ($1)");

    //-- remove all inside SCRIPT and STYLE tags
    returnText=returnText.replace(/<script.*>[\w\W]{1,}(.*?)[\w\W]{1,}<\/script>/gi, "");
    returnText=returnText.replace(/<style.*>[\w\W]{1,}(.*?)[\w\W]{1,}<\/style>/gi, "");
    //-- remove all else
    returnText=returnText.replace(/<(?:.|\s)*?>/g, "");

    //-- get rid of more than 2 multiple line breaks:
    returnText=returnText.replace(/(?:(?:\r\n|\r|\n)\s*){2,}/gim, "\n\n");

    //-- get rid of more than 2 spaces:
    returnText = returnText.replace(/ +(?= )/g,'');

    //-- get rid of html-encoded characters:
    returnText=returnText.replace(/&nbsp;/gi," ");
    returnText=returnText.replace(/&amp;/gi,"&");
    returnText=returnText.replace(/&quot;/gi,'"');
    returnText=returnText.replace(/&lt;/gi,'<');
    returnText=returnText.replace(/&gt;/gi,'>');

    //-- return
    document.getElementById("output").value = returnText;
}

它与以下HTML一起使用:

<textarea id="input" style="width: 400px; height: 300px;"></textarea><br />
<button onclick="convertHtmlToText()">CONVERT</button><br />
<textarea id="output" style="width: 400px; height: 300px;"></textarea><br />

2
我喜欢这个解决方案,因为它处理了HTML特殊字符...但仍然远远不够...对我来说最好的答案应该涉及所有这些特殊字符(这可能就是jQuery所做的)。 - Daniel Gerson
3
我认为 /<p.*>/gi 应该是 /<p.*?>/gi - cbron
请注意,要删除所有<br>标签,您可以使用一个好的正则表达式而不是三个替换,例如:/<br\s*\/?>/。此外,除了解码实体之外,您似乎可以使用一个单独的正则表达式,类似于:/<[a-z].*?\/?>/ - Alexis Wilke
不错的脚本。但是表格内容怎么办?有什么想法可以展示它吗? - Hristo Enev
@DanielGerson,对HTML进行编码很快就会变得非常棘手,但是最好的方法似乎是使用he库 - KyleMit
此函数有许多迭代,可能会导致长文本的多个实例中出现内存泄漏。 - Matías Fork

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