仅在<p> </p>之前直接匹配,但不在<p> </p>之后直接匹配

时间:2013-01-13 22:35:33

标签: javascript regex

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

我一直在尝试使用lookbehinds(刚刚发现它们),但它们似乎不适用于JavaScript。我想要做的是删除所有&lt; br&gt;在&lt; p&gt;的最后,但不是段落中唯一的元素,例如&lt; p&gt;&lt; br&gt;&lt; / p&gt;。所以:

<p>Blah<br><br><br></p> becomes <p>Blah</p>
<p><br></p> stays the same.

到目前为止我只有

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

无论有多少,都会删除段落末尾的所有&lt; br&gt;。

我想要像

这样的东西
html = html.replace(/(?<!<p>)(?:<br\s?\/?>)+(<\/p>)/g, '$1');

编辑:我使用contenteditable div来创建一个非常简单的RTE,每当用户想要更改某些文本时,它就会动态创建。基本上只是清除reduntant span,br和p标签等。

5 个答案:

答案 0 :(得分:3)

使用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>

See it here.

免责声明:我没有清理它。我稍后会再回来。

答案 1 :(得分:2)

你是对的,你can't使用正则表达式来解析HTML,因为他们无法这样做。

是的,您还有其他选择。有几个宽容的HTML解析JS库最初定位到Node,但应该在浏览器中工作。

您还可以利用浏览器具有内置HTML解析器的事实,并使用它来解析您的HTML。在这种情况下可能会使用DocumentFragment。或者,在您的情况下,您只需修改contenteditable元素中的DOM。

答案 2 :(得分:0)

这似乎过于复杂。你有没有尝试过更简单的东西:

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

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

答案 3 :(得分:0)

这里有几行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. 你正在做的事情更加明显。当您或其他开发人员稍后回到此处时,您将不必考虑“正则表达式%&^@!£%*cthulu&GJHS^&@到底做了什么?”

  2. 扩展/修改更容易。如果你的要求甚至稍微复杂一点,用(JavaScript)的正则表达式because of Regex and HTMLs relative positions in the Chomsky hierarchy实现这一点几乎是不可能的。

  3. 看到你的代码的人会觉得你一般都很酷。

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

答案 4 :(得分:0)

正则表达式解决方案(不是我建议你应该使用它来解析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>