使用正则表达式删除空标签

10

我想删除空标签,例如 <label></label><font> </font>,以便于:

<label></label><form></form>
<p>This is <span style="color: red;">red</span> 
<i>italic</i>
</p>

将被清理为:

<p>This is <span style="color: red;">red</span> 
<i>italic</i>
</p>

我在 JavaScript 中有这个正则表达式,它能删除空标签,但也会把这个删除:"<i>italic</i></p>"

str=str.replace(/<[\S]+><\/[\S]+>/gim, "");

我缺少什么?


我认为 Stack Overflow 的标记可能会影响您。对于我们来说,您的清理例程结果的前后看起来都是一样的。 - Jamie Wong
这两行代码看起来一样。您能使用代码块使文本不会被格式化吗? - Darko Kenda
修复了标记,希望现在更加清晰易懂。 - paxdiablo
这个论坛速度这么快吗?我简直不敢相信你们这些人有这么快的反应速度!! - bobby
12个回答

24
你的字符类中有"not spaces",这意味着"<i>italic</i></p>"会匹配上。你的正则表达式的前半部分将匹配"<(i>italic</i)>",后半部分将匹配 "</(p)>"。(我使用括号来显示每个 [\S]+ 匹配的内容。)
请将此改为:
/<[\S]+><\/[\S]+>/

变为这个:

/<[^/>][^>]*><\/[^>]+>/

总体而言,你应该使用适当的HTML处理器,但如果你只是搞一些HTML混合物,那么这个方法就足够了 :)


这是最接近的表达方式。其他的不起作用。 但是这个也会删除</i></p>。我正在尝试修改代码。谢谢。 - bobby
@AndreasJohansson:除非你在解析一些非常混乱的HTML,否则这并不会有什么区别。 - porges
1
所有这些都会在出现类似于“<blockquote data-instgrm-permalink="https://www.instagram.com/"></blockquote>”的情况下失败。因此,我更喜欢使用这个正则表达式:<([^/>]+|[^/>]["'][^"'>]["'][^/>]+)>\s*</[^>]+>。 - Pinonirvana

23

正则表达式不适用于HTML。如果你已经在使用JavaScript,我建议使用jQuery DOM处理。

类似于:

$('*:empty').remove();

或者:

$("*").filter(function() 
{ 
     return $.trim($(this).html()).length > 0; 
}).remove();

我在同一个函数中还有其他正则表达式清理。不幸的是,我更喜欢这种方式。该内容位于IFrame中,用户从Word文档中粘贴。我正在清理所有微软垃圾。 - bobby
1
我同意正则表达式看起来更容易,但如果你已经在JavaScript中使用jQuery会更容易,并且使扩展功能变得更加容易(如果要求您开始删除嵌套的<p>标签或嵌套超过3个级别的标签怎么办?) - Matt Mitchell
1
我知道格式在评论中不起作用,但请复制并粘贴此片段到您的文档头部:<script type="text/javascript"> $(document).ready(function() { $('*:empty').remove(); }); </script> 它等待文档准备就绪,然后删除空标签(根据Graphain的示例)。确保首先加载jQuery库,例如:<script type="text/javascript" src="jquery.min.js"></script>。 - Gert Grenander
请注意,此评论流的时候,“Graphain”是我的用户名。 - Matt Mitchell
这将返回 false -> $('<x> </x>').is(':empty') - vsync
显示剩余7条评论

9
所有正则表达式的答案只是验证。
<label></label>

但是在这种情况下
<label> </label>
<label>    </label>
<label>
</label> 

尝试使用这个模式来获取上述所有内容。
<[^/>]+>[ \n\r\t]*</[^>]+>

它还考虑到我是否有'ul'后跟'li'。我该如何跳过它? - Sankalp
1
我建议将 [\n\r\t]* 替换为 [\s]*,这意味着相同的内容加上一些其他字符(\f 和 \v)以及空格。 - Gogol

3
你需要使用/<[\S]+?><\/[\S]+?>/这个正则表达式,区别在于?跟在+后面,匹配"尽可能少的"(也称为"非贪婪匹配")非空字符(虽然至少一个),而不是裸露的+,它们匹配"尽可能多的"(也称为"贪婪匹配")。
完全避免使用正则表达式,正如其他答案建议的那样,也是一个很好的主意,但我想指出重要的贪婪与非贪婪的区别,在许多需要使用正则表达式的情况下,这将使你受益匪浅。

2

以下是一种现代的本地JavaScript解决方案;实际上与2010年的jQuery解决方案非常相似。我从那个答案中进行了适应,用于我正在开发的项目,并想在这里分享它。

document.querySelectorAll("*:empty").forEach((x)=>{x.remove()});
document.querySelectorAll返回一个NodeList,它实际上是一个数组,包含所有与作为参数给定的CSS选择器匹配的DOM节点。
  • *:empty是一个选择器,它选择所有空元素(*表示“任何元素”),即没有内容的元素。

    这将选择整个文档中的任何空元素,如果您只想从页面的某个部分中删除任何空元素(即仅限于某些

    元素内的元素),则可以向该元素添加id,然后使用选择器#id *:empty,它表示具有id为id的元素内的任何空元素。

    这几乎肯定是您想要的。从技术上讲,一些重要标记(例如标记、
    标记、标记等)是“空的”,因此如果不指定范围,您将删除一些您可能关心的标记。

forEach循环遍历结果NodeList中的每个元素,并在其上运行匿名函数(x)=>{x.remove()}x是列表中当前的元素,调用.remove()将其从DOM中删除。
希望这能对某些人有所帮助。令人惊讶的是,JavaScript在短短的8年时间里已经取得了长足的进步;从几乎总是需要一个库才能以简洁的方式编写这样复杂的东西到能够本地实现。

编辑

因此,上面详细介绍的方法在大多数情况下都可以正常工作,但它存在两个问题:

  • <div> </div>这样的元素不被视为:empty(而不是之间的空格)。 CSS Level 4选择器通过引入:blank选择器来解决这个问题(它类似于空白,但会忽略空格),但目前只有Firefox支持它(以供应商前缀形式)。
  • 自闭合标记被:empty捕获-并且这也将是:blank的情况。

我编写了一个稍微大一点的函数来处理这两种用例:

document.querySelectorAll("*").forEach((x)=>{
    let tagName = "</" + x.tagName + ">";
    if (x.outerHTML.slice(tagName.length).toUpperCase() == tagName
        && /[^\s]/.test(x.innerHTML)) {
        x.remove();
    }
});

我们迭代页面上的每个元素。我们获取该元素的标签名称(例如,如果元素是一个div,则为DIV),并使用它构建一个闭合标签 - 例如</DIV>
那个标签有6个字符长。我们检查元素HTML的大写的最后6个字符是否与之匹配。如果匹配,则继续。如果不匹配,则该元素没有闭合标签,因此必须是自闭合的。这比列表更可取,因为这意味着如果新的自闭合标签被添加到规范中,您不必更新任何内容。
然后,我们检查元素的内容是否包含任何空格。 /[^\s]/是一个正则表达式。 []是一个正则表达式中的集合,并且将匹配其中出现的任何字符。如果^是第一个元素,则该集合变为否定的 - 它将匹配不在集合中的任何元素。 \s表示空格 - 制表符、空格、换行符。因此,[^\s]表示“任何非空格字符”。
根据匹配结果,如果标记不是自闭合的,并且其内容包含非空格字符,则我们将其删除。
当然,这比以前的一行代码要大一些,也不太优雅。但它应该对几乎所有情况都有效。

我发现这段代码实际上会删除一些必要但未关闭的标签,例如link和meta。虽然这是一个不错的“一行代码”,但它对HTML的影响有点粗糙。我正在尝试找到一种调整方法来解决这个问题。无疑,这种简单的方法确实很好。 - P070
1
@Le-Nerdtm,我在我的帖子中添加了一个编辑,应该解决了那些问题。 - Toastrackenigma

2
我喜欢MattMitchell的jQuery解决方案,但这里还有另一个使用本地JavaScript的选项。
function CleanChildren(elem)
{
    var children = elem.childNodes;
    var len = elem.childNodes.length;

    for (var i = 0; i < len; i++)
    {
        var child = children[i];

        if(child.hasChildNodes())
            CleanChildren(child);
        else
            elem.removeChildNode(child);

    }
}

1
<([^>]+)\s*>\s*<\/\1\s*>

<div>asdf</div>
<div></div> -- will match only this
<div></notdiv>
-- and this
<div  >  
    </div   >

动手试试吧 https://regexr.com/


1

这是一个贪婪正则表达式的问题。请尝试以下代码:

str=str.replace(/<[\^>]+><\/[\S]+>/gim, "");

或者

str=str.replace(/<[\S]+?><\/[\S]+>/gim, "");

在你的正则表达式中,<[\S]+?> 匹配 <i>italic</i>,而 <\/[\S]+> 则匹配 </p>


我知道缺了什么...谢谢 - bobby

0
你可以使用这个: text = text.replace(/<[^/>][^>]>\s</[^>]+>/gim, "");


0
在 CodePen 上找到了这个: 虽然是用 jQuery 写的,但能完成任务。
$('element').each(function() {
  if ($(this).text() === '') {
    $(this).remove();
  }
});

你需要修改元素,使其指向你想要删除空标签的位置。不要指向文档,因为这会导致我的回答是Toastrackenigma。

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