只匹配<br>如果在</p>但不能直接跟在</p>后面

Only match <br> if directly before </p> but not directly after </p>

本文关键字:后面 但不能 如果 br      更新时间:2023-09-26

我正在尝试使用正则表达式来清理在我自己的html5 RTE中生成的一些代码。搜索周围,我看到很多人说正则表达式不应该用来解析html…但我正在用JavaScript做这个客户端。除了正则表达式,我还有其他选择吗?

我一直在尝试使用lookbehind(刚刚发现他们),但他们似乎不与JavaScript工作。我想做的是删除所有的

s的最后,但不是段落中唯一的元素,如


所以:
<p>Blah<br><br><br></p> becomes <p>Blah</p>
<p><br></p> stays the same.

目前只有

html = html.replace(/(?:<br's?'/?>)+(<'/p>)/g, '$1');

将删除段落末尾的所有
,无论有多少。

我想要像

html = html.replace(/(?<!<p>)(?:<br's?'/?>)+(<'/p>)/g, '$1');

编辑:我使用一个可满足的div来创建一个非常简单的RTE,每次用户想要改变一些文本时动态创建。基本上只是清除多余的span, br和p标签,等等。

使用DOM解析器

的想法是保留所有连续的<br>元素。每次出现非空文本元素或任何其他元素时清除数组。

如果在循环结束时存在<br>列表,则删除它们。这些是末尾的<br>元素。

var $pp = document.getElementsByTagName("p");
for(var i = 0, $p = $pp[0], $br = [], alone = true; i < $pp.length; i = i + 1, $p = $pp[i], $br = [], alone = true){
  for(var j = 0, $child = $p.childNodes[0]; j < $p.childNodes.length; j = j + 1, $child = $p.childNodes[j]){
    if(($child.tagName !== "BR") && ($child.textContent.trim() !== "")){
      alone = false;
      $br = [];
    } else {
      $br.push($child);
    }
  }
  for(var j = 0; j < $br.length - alone; j = j + 1){
    $p.removeChild($br[j]);
  }  
}
例如,

<p>Foo<br><br><br></p>
<p>Foo<br>Bar<br><br></p>
<p><br></p>

<p>Foo</p>
<p>Foo<br>Bar</p>
<p><br></p>

看这里

免责声明:我没有清理它。

你是对的,你不能使用正则表达式来解析HTML,因为它们不能这样做。

是的,你有其他选择。有几个宽容的HTML解析JS库最初是针对Node的,但应该在浏览器中工作。

  • htmlparser
  • htmlparser2
  • HTML5解析器

你也可以利用浏览器内置的HTML解析器,用它来解析你的HTML。在这种情况下,DocumentFragment可能是有用的。或者,您可以简单地修改contenteditable元素中的DOM。

这看起来太复杂了。你有没有试过一些更简单的东西,比如:

<p>.+(<br>)+<'/p>

这应该匹配任何包含在段落中的<br>,在它的最后(在结束标记之前),并且在它自己和开始标记之间有一些东西。你可能应该改变它,使它不接受空格作为有效的东西,但你知道的。

下面是几行jQuery:

// Note: in order to load the html into the dom it needs a root. I'm using `div`:
var input = '<div>' +
  '<p>Blah<br><br><br></p> becomes <p>Blah</p>' +
  '<p><br></p> stays the same.' +
  '</div>';
// Load the html into a jQuery object:
var $html = $(input);
// Get all the `<br>`s at the end of `p`s that are not the only-child:
var $lastBreaks = $html.find('p>:last-child:not(:only-child)').filter('br');
// Remove any immediately preceding `br`s:
$lastBreaks.prevUntil(':not(br)').remove();
// Remove the last `br`s themselves
$lastBreaks.remove();
// Output:
console.log($html.html());

输出:

<p>Blah</p> becomes <p>Blah</p><p><br></p> stays the same.
http://jsfiddle.net/nnH4G/

此方法优于使用正则表达式的原因:

  1. 你在做什么更明显。当您或其他开发人员稍后回到这个问题时,您不必考虑"regex %&^@!£%*cthulu&GJHS^&@到底是做什么的?"

  2. 更容易扩展/修改。如果你的需求稍微复杂一点,用(JavaScript的)正则表达式是不可能实现的,因为正则表达式和html在Chomsky层次结构中的相对位置。

  3. 看到你的代码的人会觉得你是个很酷的人。

正如其他答案所指出的那样,jQuery绝不是唯一的方法。但是考虑到它在客户端无处不在,它是一个非常有用的工具。

正则表达式解决方案(不是说我建议您应该在DOM解析中使用它):

从你的问题中我不清楚你想要发生什么,例如,
'<p><br><br></p>',所以下面有两个解决方案。

如果你想让它保持原样,你可以使用1);如果你想让它变成'<p></p>',你可以使用2):

1)

html = html.replace( 
    /<p>(['s'S]+?)(?:<br>)+<'/p>/g,
    function ( $0, $1 ) { return $1 == '<br>' ? $0 : '<p>' + $1 + '</p>' }
)

测试
function test(html) {
    return html.replace( 
        /<p>(['s'S]+?)(?:<br>)+<'/p>/g,
        function ( $0, $1 ) { return $1 == '<br>' ? $0 : '<p>' + $1 + '</p>' }
    )
}
test( '<p>Blah</p>' );                // <p>Blah</p>
test( '<p>Blah<br><br><br></p>' );    // <p>Blah</p>   
test( '<p><br>Blah<br></p>' );        // <p><br>Blah</p>
test( '<p><br></p>' );                // <p><br></p>
test( '<p><br><br></p>' );            // <p><br><br></p>   

2)

html = html.replace( /(?:([^>]|[^pb]>)(?:<br>)+|(?:<br>){2,})<'/p>/g, '$1</p>' );

测试
function test(html) {
    return html.replace( /(?:([^>]|[^pb]>)(?:<br>)+|(?:<br>){2,})<'/p>/g, '$1</p>' );
}
test( '<p>Blah</p>' );                // <p>Blah</p>
test( '<p>Blah<br><br><br></p>' );    // <p>Blah</p>   
test( '<p><br>Blah<br></p>' );        // <p><br>Blah</p>
test( '<p><br></p>' );                // <p><br></p>
test( '<p><br><br></p>' );            // <p></p>