将 <br> 标签转换为 <div> 或 <p> 标签

3

我收到的HTML格式各不相同,我正在尝试使用jQuery将代码标准化。在某些情况下,我使用换行符<br>来分隔行。我需要将其更改为<div><p>标签。例如下面的代码:

a<br>b<br>c

需要转换为

<div>a</div><div>b</div><div>c</div>

这是一个简单的例子,因为 a 中的内容可能被各种格式选项包围,如<font>, <span>等,所有这些都需要保留。

这是我的第一次尝试。它找到整个文档中所有的 <br> 标签,然后访问父元素的内容以获取 <br> 周围的所有HTML。如果内容长度为1,则 <br> 是唯一的元素(表示空行),则跳过到下一个。否则,查看每个元素,用 <div>(或 <p>)将其包围,并删除 <br>,因为它不再需要。变量 wrapper 取决于浏览器,可以是 "<div />""<p />"

$("br").each(function() {
  // Find all the br tags' parent nodes
  var elements = $(this).parent().contents();
  if (elements.length > 1) {
    // If there is one element than it is br so skip on.
    elements.each(function() {
      // Loop through each child element of the parent 
      var nodeName = this.nodeName.toLowerCase();
      if (nodeName !== 'div' && nodeName !== 'p') {
      // Make sure each child that is not <br> is wrapped. Remove <br> tags.
        if (nodeName == 'br') {
          $(this).remove();
        } else {
          $(this).wrap(wrapper);
        }
      }
    });
  }
});

但是,这种方法行不通,因为它会在每个子元素周围添加<div> (或<p>)标签,所以像<font>a</font>bc<br>de<b>f</b>这样的内容最终会变成:

<div><font>a</font></div>
<div>bc</div>
<div>de</div>
<div><b>f</b></div>

当它确实需要时

<div><font>a</font>bc</div>
<div>de<b>f</b></div>

如果我在文本解析器中执行此操作,我会像上面对变量elements进行的操作一样找到内容,然后在开头添加一个<div>,在结尾添加一个</div>,然后将每个<br>替换为</div><div>(除非它已经是<div><br></div>,此时我会跳过它)。我不知道如何在jQuery / Javascript中执行此操作。特别是关于使用<br>替换</div><div>的部分。(第一部分是elements.wrap(wrapper)。)

谁能给予一些帮助或者提供另一种方法将不胜感激。

--- 更新 ---

@Ian提供了大部分有效的代码。我稍微扩展了一下,并认为应该在这里包含它。我已在FF,Safari,Chrome和IE 9+中测试过。

$("br").each(function(idx, el) {
    var $el = $(el),
        $parent = $el.parent(),
        $contents = $parent.contents(),
        $cur, $set, i;

    $set = $();
        if ($contents.length > 1) {
        for (i = 0; i < $contents.length; i++) {
            $cur = $contents.eq(i);

            if ($cur.is("br")) {
                $set.wrapAll(wrapper);
                $cur.remove();
                $set = $();
            } else {
                $set = $set.add($cur);
            }
        }
        $set.wrapAll(wrapper);
    }
});

1
谢谢,@TimurShahbanov。你能具体说明一下吗?你对prepend()有什么想法? - eliajf
@tryingToGetProgrammingStraight $(this).wrap(wrapper) 将<div>或<p>添加到我的描述中所解释的包装器中。 - eliajf
我认为这大致就是你要找的:http://jsfiddle.net/GV9fL/。尝试更改HTML并确保它按预期工作(使用浏览器开发者工具的DOM检查器进行检查)。 它不使用任何荒谬的文本/ HTML解析/重建。 但有点混乱,可能需要重新组织。 - Ian
@Ian 这是获胜者,只有一个修改,在我所有不同的Safari示例中至少如此。 我仍然需要在其他浏览器上进行测试。 我希望我能为您点赞。 我将for循环包装在if ($contents.length> 1)中,以确保我们不会删除合法的空白行:<div> <br> </ div>。 - eliajf
根据我的经验,如果初始设置正确,contenteditable不会返回<br><br>。当然,初始设置因浏览器而异。Safari和Chrome以<div><br></div>开头,IE以<div></div>开头,FF以<p><br></p>开头。从那时起,所有行都将包装在适当的<div>或<p>中。 - eliajf
显示剩余2条评论
6个回答

6

我的建议是按照 <br> 进行文本分割:

var lines = content.split("<br>");

现在,您可以将每行文本都用 <div> 标签包裹起来:
var newContent = "";
for(var i=0,l=lines.length;i<l;i++){
     newContent += "<div>"+lines[i]+"</div>";

}

不是我点的踩,但这有点像用正则表达式解析HTML。在一般情况下,这样做可以工作,但你很容易想出方法来轻松破坏它。 - James Montagne
正如@JamesMontagne所指出的那样,一个破坏的案例是<div>a<div><br></div></div>。 - eliajf
我的示例不会创建br标签。试一下并展示它的失败之处。你给出的示例与你在主要问题中所问的相矛盾。为什么它被div包围? - Ibu
我不太确定如何处理这个问题。在某些情况下,您、PeterOlson和funkwurm的代码都可以正常工作,但并非所有情况都是如此。我已经给三个人点了赞。事实证明,<br>标签不能仅仅被替换掉。有些情况下,标签必须保持原样,因为它们是代码形成的方式。例如,<div>a</div><div><br></div><div>b</div>。这就是一个<空行> b的表示方式。您的代码完全删除了空行。评论中的Ian编写了最接近在所有情况下都能正常工作的jsfiddle版本。 - eliajf

3

您可以执行您所描述的文本替换方法。

function changeBrToDiv(node) {
  var html = $(node).html();
  html = "<div>" + html.split("<br>").join("</div><div>") + "</div>";
  node.html(html);
}

你可以在jsFiddle上查看此操作的实现

1

<br>标签不包含文本,而且可能存在多个相同的<br>标签具有相同的父标签,如下所示:

<div id="theOne">text<br>more text<br>even more text</div>

使用 $('br').each() 然后再使用 $(this).parent(),会导致 <div id="theOne"> 出现两次。

如果您愿意将HTML视为文本进行处理,则可以采用以下方法(类似于Ibu但更简单):

var newContent = '<div>'+content.split('<br>').join('</div><div>')+'</div>';

如果您想使用节点操作(您对类似答案进行了反对),那么您将不得不使用本机JavaScript .childNodes 来访问文本节点,据我所知,jQuery不允许您这样做。

1
对我来说,似乎你希望在遍历DOM之前保留br标签。在你的第二个例子中,如果没有将br标签保留作为参考点,你就不知道要达到多高才能打开div标签。我会获取父元素,向上查找DOM,直到找到打开的<body><br>标签,然后在那里插入打开标签。反复操作即可。

0

我认为这可能是你的问题:

var elements = $(this).parent().contents();

应该是

var elements = $(this).parent().children();

由于 contents 方法返回的是文本节点,而 children 方法只返回实际的 DOM 元素。


2
切换到children()方法可以获取元素节点,但无法获取文本节点。不过还是谢谢。 - eliajf
@eliajf 但是你为什么想要文本节点? - Math chiller
@tryingToGetProgrammingStraight 你说得对,我想要在修订版中获取子节点。然而,在我上面写的那个例子中,仅仅使用grabbing children()是不够的。在那种情况下,我需要文本和元素节点。 - eliajf

0
你可以在POJS中尝试类似这样的操作;操纵DOM而不是将标记视为文本。您可能需要稍微修改它以适应您的确切要求。
HTML
<div>one
    <br>two</div>
<br>
<div>
    <br>
</div>
<div>
    <div>three
        <br><font>a</font>bc
        <br>de<b>f</b>

    </div>
</div>
<span>a</span>

<br>b<span>c</span>d
<br>e<span>f</span>g

Javascript

function prepend(referenceNode, newNode) {
    if (referenceNode.firstChild) {
        referenceNode.insertBefore(newNode, referenceNode.firstChild);
    } else {
        referenceNode.appendChild(newNode);
    }
}

var brs = document.getElementsByTagName("br"),
    docFragment = document.createDocumentFragment(),
    index1,
    index2,
    parent,
    siblings,
    sibling,
    br;

for (index1 = brs.length - 1; index1 >= 0; index1 -= 1) {
    div = document.createElement("div");
    br = brs[index1];
    parent = br.parentNode;
    siblings = parent.childNodes;
    for (index2 = siblings.length - 1; index2 >= 0; index2 -= 1) {
        sibling = siblings[index2];
        if (sibling === br) {
            break;
        }

        prepend(div, sibling);
    }

    prepend(docFragment, div);
    br.parentNode.removeChild(br);
    if (!brs.length && siblings.length) {
        div = document.createElement("div");
        for (index2 = siblings.length - 1; index2 >= 0; index2 -= 1) {
            prepend(div, siblings[index2]);
        }

        prepend(docFragment, div);
    }

    if (docFragment.childNodes.length && !siblings.length) {
        parent.appendChild(docFragment);
    }
}

jsfiddle


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