0

这是我的 HTML“沙箱”:

<p>1 2 3</p>
<span id="myid">
<span id="mytext">
<p>4 5 6</p>
<p>7 8 9</p>
<p>10 11 12</p>
</span>
</span>
<p>13 14 15</p>

我想相应地“合并” p 标签:(
为了更容易看到我在进行更改的地方添加了一个减号:)

<p>1 2 3 -
<span id="myid">
<span id="mytext">
- 4 5 6</p>
<p>7 8 9</p>
<p>10 11 12 -
</span>
</span>
- 13 14 15</p>

我添加了一个减号的每个地方,我实际上已经剥离了一个<p></p>标记,在两个段落元素之间进行合并(在 1 2 3 和 4 5 6 之间以及 10 11 12 和 13 14 15 之间)-(此合并仅实现 123-456 和 101112-131415 的内联显示)。

如何使用“原始”JavaScript(请不要使用库)来实现此结果。

注意: 我试图创建一些东西,但我花了 80 行代码 - 我相信有更好的方法来做到这一点。

编辑: 乔恩汉纳在他的评论中提到我可以像这样使用正确的 HTML(作为 JavaScript 的结果):

    <p>1 2 3 <span class="myclass mytext">4 5 6</span></p>
<p class="myclass mytext">7 8 9</p>
<p><span class="myclass mytext"10 11 12</span> 13 14 15</p>
4

3 回答 3

1

您必须通过innerHTMLetc 获取源代码,操作字符串然后输出字符串。

虽然 DOM 通常不会阻止您错误地拥有<p>inside的事实<span>,但它们在逻辑上不可能代表您想要的输出,因为没有HalfWrittenPElement对象。

您故意违反规则的要求与旨在帮助您的对象模型不一致,因此您只能使用字符串进行破解。

编辑:

尽管如此,浏览器无论如何都会在其内部模型中修复它,因此它最终仍将是格式良好(如果不一定有效)的 HTML。它只是根据实现定义的更正而不是任何规范。

于 2012-08-17T23:05:33.627 回答
1

我没有测试过这个,但你会明白的。您可以自己进行调试和跨浏览器微调;)

我将找到所有<p>标签,并为每个标签检查其兄弟姐妹是否为<span>,<span><p>. 然后我将<span>s 和第二个的文本<p>放在第一个<p>中。

var pTags = document.getElementsByTagName('p');  // get all <p>

for (var i = 0; i < pTags.length; i++) {         // for every one of them
   var a = pTags[i];                             // take the <p> (and call it `a`)

   var b = a.nextElementSibling();               // and the next tag (call it `b`)
   if (b == null) continue;

   var c = b.nextElementSibling();               // and the next tag (call it `c`)
   if (c == null) continue;

   var d = c.nextElementSibling();               // and the next tag (call it `d`)
   if (d == null) continue;

   if (b.tagName.toUpperCase() == 'SPAN' &&      //  if b is <span>
       c.tagName.toUpperCase() == 'SPAN' &&      // and c is <span>
       d.tagName.toUpperCase() == 'P') {         // and d is <p> again

         a.appendChild(b);                       // put b inside a
         a.appendChild(c);                       // put c inside a
         a.appendChild(d.firstChild.nodeValue);  // put text of d inside a
   }
}

请注意,nextElementSibling方法不是跨浏览器的,但您可以通过调用来模拟它,nextSibling直到它属于该Element类型。

还要注意,通过调用appendChild元素首先从其原始容器中删除,然后才附加它。这就是我们想要的。

快速编辑:

正如其他人指出的那样,您的 html 无效,我一开始没有注意到。但我想你想让<span>s 正确关闭。

于 2012-08-17T23:05:46.697 回答
1

(请耐心等待,我是在您最近编辑之前开始写这篇文章的。不过,我认为它仍然有用。)

我不确定您想要实现什么,但我可以告诉您,使用 JavaScript很难完全实现您所要求的。这主要是因为 HTML 无效。

让我们先来看看为什么HTML 会无效。当浏览器解析 HTML 时,它会构建一个嵌套的 DOM 节点。你的第一个例子可以这样表示:

  • <p>
    • 1 2 3
  • <span id="myid">
    • <span id="mytext">
      • <p>
        • 4 5 6
      • <p>
        • 7 8 9
      • <p>
        • 10 11 12
  • <p>
    • 13 14 15

使用 JavaScript 和 DOM,您可以遍历这棵树并对其进行操作。您可以组合来自各种标签的文本节点<p>并将它们放在一个<p>标签中而不会出现问题。但是您将无法创建第二个示例的结构。它的形状根本不像 DOM 树,所以你不能使用 DOM 来创建 HTML。

innerHTMLJon Hanna 解释的那样,将 HTML 操作成第二个示例的形状的唯一方法是通过。好消息是浏览器总是会将无效的 HTML 纠正为有效的 DOM 结构。坏消息是每个浏览器执行此操作的方式不同,因此您永远不知道最终会得到什么。

唯一真正的解决方案是重新考虑您的 HTML 结构,就像您在最近的编辑中所做的那样。

解决原始问题的方法取决于原始 HTML 的静态程度。我将举一个粗略的例子,我假设:

  • 总是正好5 <p>
  • 的第二个到第四个<p>总是在里面<span>,第一个和第五个总是在它们外面

如果满足这两个条件,您可以使用以下内容:

var paragraphs = document.getElementsByTagName('p');
var newParagraphs = [];

function createElementWithText(tagname, text, classname) {
    var classname = classname || '';
    var element = document.createElement(tagname);
    element.className = classname;
    element.appendChild(document.createTextNode(text));
    return element;
}

newParagraphs[0] = createElementWithText('p', paragraphs[0].textContent);
newParagraphs[0].appendChild(createElementWithText('span', paragraphs[1].textContent, 'myclass mytext'));

newParagraphs[1] = createElementWithText('p', paragraphs[2].textContent, 'myclass mytext');

newParagraphs[2] = document.createElement('p');
newParagraphs[2].appendChild(createElementWithText('span', paragraphs[3].textContent, 'myclass mytext'));
newParagraphs[2].appendChild(document.createTextNode(paragraphs[4].textContent));

我在 JSFiddle 上发布了一个工作示例:jsfiddle.net/vSrLJ/

于 2012-08-17T23:51:22.717 回答