简体   繁体   中英

how to split an html inline element if it has block element inside with javascript

first exuse me for my bad english. i'll try to explain my question well!

so my problem is that i need to split span html element if it has any elements inside like that

<span>
  bla-bla \r\n
    <div>hello</div> 
  world:)
</span>

into

<span>
  bla-bla \r\n
</span>
<div>
  hello
</div>
<span>
  world:)
</span>

and if span has multiple divs or there are multiple spans like

<span class="bold">
  bla 
  <span class="anyclass">
    bla 
    <div>
      Hello
    </div> 
    bla 
    <span class="anyclass#2"> 
      bla-bla 
    </span> 
    <h1>
      amigo
    </h1> 
    <span class="anyclass#3">
      <h2>
        :)
      </h2>
      world
    </span>
  </span> 
  la-la
</span>

it should be splited into that

<span class="bold">
  bla 
  <span class="anyclass">
    bla 
  </span>
</span>
<div>
  Hello
</div>
<span class="bold">
  <span class="anyclass">
    bla 
    <span class="anyclass#2"> 
      bla-bla 
    </span> 
  </span>
</span>
<h1>
  amigo 
</h1>
<span class="bold">
  <span class="anyclass">
    <span class="anyclass#3">
    </span>
  </span>
</span>
<h2>
  :)
</h2>
<span class="bold">
  <span class="anyclass">
    <span class="anyclass#3">
      world
    </span>
  </span> 
  la-la
</span>

well, there coud be multiple divs and h1 or any of them inside span or there could be multiple inserted spans and div inside of them and i'm realy stuck in that.

i know it looks creepy but i need to do something with it!

if enyone knows how to resolve this jigsaw or knows something or allready faced something like that plaase help me!

thanks a lot!

A <span> is a generic inline text element. inline-elements do not create breaks.

A <div> is short for division-element, which is a generic block-level element, which creates breaks (by default).

Block-elements are allowed to contain other block elements and inline-elements.

Inline-elements are allowed to contain other inline-elements, though are forbidden to contain block-level elements.

In other words, you can not put a <div> element inside of a <span> element.

If you wish create a line-break, you can use the CSS clear property on inline-elements, though you can not validly keep block-level elements inside inline-elements, so fix that now before you get stuck with poor code.

The CSS clear property has the following values: left , right , both , inherit , none .

You can use the any of the following...

 span.clear {clear: both;} span.clear_left {clear: left;} span.clear_right {clear: right;}
 <span class='clear'>clear: both</span> <span class='clear_left'>clear: left</span> <span class='clear_right'>clear: right</span>

Also remove the headers out from inline elements, that's just...not good the way your HTML is.

I heavily recommend that you research to greatly improve your understanding the context of elements starting here...

http://www.w3.org/TR/xhtml11/doctype.html#s_doctype

Keep in mind that the stricter your code adheres to standards the easier it will become to work with your code as it reduces the subjectivity of any given situation.

This is way more complicated than it looks. Here is my solution:

jsfiddle

function removeSiblings(el, before) {
    var parent = el.parentNode;
    var children = [].slice.call(parent.childNodes);
    var active = !!before;
    for (var i = 0; i < children.length; i++) {
        if (children[i] == el) {
            active = !active;
        } else if (active) {
            parent.removeChild(children[i]);
        }
    }
}

function removeBeforeOrAfter(top, path, before) {
    var els = [];
    for (var i = 0; i < path.length; i++) {
        els.push(restorePath(top, path.slice(0, i + 1)));
    }
    for (var i = 0; i < els.length; i++) {
        removeSiblings(els[i], before);
    }
}

var indexPath = [];
function walkPath(el, condition) {
    if (el.nodeType !== Node.ELEMENT_NODE) {
        return null;
    }
    if (!condition(el)) {
        return el;
    }
    var children = [].slice.call(el.childNodes);
    for (var i = 0; i < children.length; i++) {
        indexPath.push(i);
        var result = walkPath(children[i], condition);
        if (result) {
            return result;
        }
        indexPath.pop();
    }
    return null;
}

function restorePath(el, path) {
    for (var i = 0; i < path.length; i++) {
        el = el.childNodes.item(path[i]);
    }
    return el;
}

function isInlineElement(el) {
    return /^(A|B|BR|CODE|I|IMG|SPAN|STRONG)$/.test(el.tagName); // list not complete
}

function split(wrongNode, topNode, indexPath) {
    var clone = topNode.cloneNode(true);
    var wrongClone = restorePath(clone, indexPath);
    if (topNode.nextSibling) {
        topNode.parentNode.insertBefore(clone, topNode.nextSibling);
    } else {
        topNode.parentNode.appendChild(clone);
    }
    removeBeforeOrAfter(topNode, indexPath, false);
    topNode.parentNode.insertBefore(wrongNode, topNode.nextSibling);
    removeBeforeOrAfter(clone, indexPath, true);
    wrongClone.parentNode.removeChild(wrongClone);
}

$(document).ready(function() {
    var nodes = document.body.childNodes; // live nodeList!
    for (var i = 0; i < nodes.length; i++) {
        var wrongNode = walkPath(nodes[i], isInlineElement);
        if (wrongNode) {
            split(wrongNode, nodes[i], indexPath);
        }
        indexPath = [];
    }
});

EDIT: Note, you can get your preferred structure with the below Jquery code

 $(document).ready(function(){

$('span').each(function(){

    $(this).children(':not("span")').before('</span').after('<span>');

});

});

Though, this is a dirty way of doing, considering this unusual expectation, I think this has to be the way. The disadvantage is that you have to manually check for class or any attributes and had to be added to the span element which is being appended (which is not done in here; just span elements are rearranged). . . .

Here is the fiddle ...

The above fiddle uses all the non span childs which can be filtered to specific tags like div h1 h2 etc depending on your needs. . . .

Thanks guys! I was thinking everything about but not the way Pumbaa80 did!

I spent a day trying to understand how it works and it seems this is the only and the easiest way to split inline elements as I need!

By the way it has one bug - it doesn't split span#span2 in this example:

<span id="span1">
  <div id="div1">
    <span id="span2">
      <div id="div2">
        Hello!
      </div>
    </span>
  </div>
</span>

I hope in a few days I'll finish my version of Pumbaa80 's script and add it to this post!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM