问题
Here is my HTML "sandbox":
<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>
I would like to "merge" the p tags accordingly:
(To make it easier to see I've added a minus sign where I've made the changes:)
<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>
Every place I've added a minus sign, I have actually stripped a <p>
or </p>
tag making a merge between the two paragraph elements ( between 1 2 3 and 4 5 6 & between 10 11 12 and 13 14 15) - (This merge is only to achieve a display inline for 123-456 & 101112-131415).
How can I achieve this result with 'raw' JavaScript (not libraries please).
Note: I've tried to create something but It took me 80 lines of code - I am sure there is a better way of doing this.
Edit: Jon Hanna mentioned in his comment I could use correct HTML like so (as the result of 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>
回答1:
(Bear with me, I started writing this before your latest edit. I think it's still useful, though.)
I'm not sure what you want to achieve, but I can tell you that it's very hard to achieve exactly what you're asking for using JavaScript. This is mostly because the HTML would be invalid.
Let's first walk through why the HTML would be invalid. When the browser parses HTML, it builds a nested tree of DOM Nodes. Your first example could be represented like this:
<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
Using JavaScript and the DOM, you can walk through this tree and manipulate it. You could combine the text node from various <p>
tags and place them in a single <p>
tag without problem. But you won't be able to create the structure of your second example. It's simply not shaped like a DOM tree, so you can't use the DOM to create that HTML.
The only way manipulate the HTML into the shape of your second example is through innerHTML
, like Jon Hanna explains. The good news is that browsers will always correct invalid HTML into a valid DOM structure. The bad news is that each browser does this differently, so you never know what you end up with.
The only real solution is to rethink your HTML structure, like you've done in your latest edit.
The way to solve your original problem depends on how static your original HTML is. I'll give a crude example, for which I'll presume that:
- there's always exactly 5
<p>
's - the second through fourth of the
<p>
's are always inside the<span>
's, and the first and fifth always outside them
If those two conditions are met, you could use something like this:
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));
I've posted a working example on JSFiddle: jsfiddle.net/vSrLJ/
回答2:
You'll have to do it with obtaining the source-code through innerHTML
etc, manipulating the string and then outputting the string.
While the DOMs generally won't stop the fact that you incorrectly have <p>
inside <span>
, it's just not logically possible for them to represent the output you want, since there's no HalfWrittenPElement
object.
Your requirement to deliberately break the rules is at odds with the object model being designed to help you, so hacking with strings is all you've got.
Edit:
Though that said, the browsers will all fix it in their internal model anyway, so it's still going to end up as well-formed (if not necessarily valid) HTML. It'll just be so according to implementation-defined corrections rather than any spec.
回答3:
I haven't tested this, but you'll get the idea. You can do the debugging and crossbrowser fine-tuning yourself ;)
I'll find all the <p>
tags and for each of them, check if its siblings are <span>
, <span>
and <p>
. Then I'll put the <span>
s and the text of the second <p>
inside the first <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
}
}
Note that nextElementSibling
method is not cross-browser, but you can emulate it by calling nextSibling
until it is of the Element
type.
Note also that by calling appendChild
the element first gets removed from its original container, and only then it is appended. Which is what we want.
Quick edit:
As others have pointed out, you have invalid html, what I didn't notice at first. But I suppose you wanted to have the <span>
s properly closed.
来源:https://stackoverflow.com/questions/12014138/how-can-i-merge-two-html-tags-via-javascript