1

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

我一直在尝试使用lookbehinds(刚刚发现它们),但它们似乎不适用于JavaScript。我想要做的是删除 <p> 末尾的所有 <br>,但不是那些是段落中唯一元素的那些,例如 <p><br></p>。所以:

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

到目前为止我只有

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

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

我想要类似的东西

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

编辑:我正在使用 contenteditable div 创建一个非常简单的 RTE,每次用户想要更改某些文本时都会动态创建该 RTE。基本上只是清除多余的 span、br 和 p 标签等。

4

5 回答 5

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>

看这里。

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

于 2013-01-13T23:26:52.990 回答
2

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

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

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

于 2013-01-13T22:40:05.250 回答
0

这似乎过于复杂。您是否尝试过更简单的方法,例如:

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

这应该匹配<br>包含在段落中的任何内容,在它的最后(在结束标签之前)并且在它自己和开始标签之间有一些东西。您可能应该更改它,使其不接受空格作为有效的东西,但您明白了。

于 2013-01-13T22:47:50.763 回答
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 的)正则表达式实现这一点实际上是不可能的,因为 Regex 和 HTML 在 Chomsky 层次结构中的相对位置

  3. 看到你的代码的人会认为你通常是一个很酷的人。

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

于 2013-01-14T20:17:14.133 回答
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>  
于 2013-01-14T22:49:38.303 回答