Mike K
Mike K

Reputation: 486

Using jQuery to close a div and reopen it around an element

My HTML looks like this:

<div class='background'>
    <div class='item' id='item1'>... </div>
    <div class='item' id='item2'>... </div>
    <div class='item' id='item3'>... </div>
    ... (more 'item's follow)
</div> <!-- background>

What I've been trying to accomplish, using jQuery, is to close the background div before item2, then reopen it again afterwards. Like this:

$('#item2').before('</div>');
$('#item2').after('<div class="background">');

But jQuery perversely insists on resolving this to:

<div class='background'>
    <div class='item' id='item1'>... </div>
    <div class="background"></div>
    <div class='item' id='item2'>... </div>
    <div class='item' id='item3'>... </div>
    ...
</div> <!-- background>

which is, obviously, useless.

In increasing desperation, I've tried using the .html() method to force the </div>... <div> tags to appear. No use: jQuery simply can't believe I want to insert that markup. (This tortured syntax:

var oldc = $('#item2').html();
var newc = '</div></div>'+oldc+'<div class="background"><div>';
$('#item2').html(newc);

resolves to:

<div class="item" id="item2">...
<div class="background"><div></div></div>
</div>

(as viewed in Firebug).)

Is there a simple way to resolve this using jQuery, or am I being fundamentally misguided?

To clarify: what I want to end up with is this:

<div class='background'>
    <div class='item' id='item1'>... </div>
</div>
    <div class='item' id='item2'>... </div>
<div class="background">
    <div class='item' id='item3'>... </div>
    ... (more 'item's follow)
</div> <!-- background>

Upvotes: 0

Views: 960

Answers (2)

jfriend00
jfriend00

Reputation: 707218

You can't just add close tags like that with DOM manipulation. You can create the new div you want, append it after the existing one and then move some DOM elements from the original background div to the new background div. You could do that with this code:

var newContainer = $("<div class='background'></div>");
$(".background").after(newContainer);
$("#item2").nextAll().appendTo(newContainer);

This creates the new background div, places it after the current background div, then gets all siblings after #item2 and moves them to the new background div.

Working Demo: http://jsfiddle.net/jfriend00/G3uyA/


OK, based on your clarification, this code will do the same as the above, but also pull item2 out to be at the same level as the background:

var origContainer = $(".background");
var target = $("#item2");
$("<div class='background'></div>").insertAfter(origContainer).append(target.nextAll());
target.insertAfter(origContainer);

Working Demo: http://jsfiddle.net/jfriend00/7Dsqw/

Upvotes: 3

Purag
Purag

Reputation: 17061

I think the most efficient way to do this would be to not have these elements contained in the .background div by default.

Then, target all elements that come after #item1 and use wrapAll() to wrap them with .background:

$("#item1").nextAll().wrapAll( $("<div>").addClass("background") );

Then target #item1 and use wrap() to wrap it in a second .background element:

$("#item1").wrap( $("<div>").addClass("background") );

You can even chain them:

$("#item1")
    .nextAll()
    .wrapAll( $("<div>").addClass("background") )
    .end()
    .wrap( $("<div>").addClass("background") );

Example.

The reason we wrap the ones after #item1 first is because once #item1 is wrapped, using nextAll() wouldn't give us anything since the rest of the elements would no longer be its siblings.


EDIT: After your clarification, I modified the above example slightly to not include #item2 in the nextAll(). This way, #item1 will be placed in its own .background element and anything after #item2 the same. #item2 will not be affected:

$("#item1")
    .nextAll(":not(#item2)")
    .wrapAll( $("<div>").addClass("background") )
    .end()
    .wrap( $("<div>").addClass("background") );

Example.


And finally, if you're not actually able to remove the original .background from the existing HTML, you can use unwrap() to remove the parent but leave all the children:

$("#item1")
    .unwrap()
    .nextAll(":not(#item2)")
    .wrapAll( $("<div>").addClass("background") )
    .end()
    .wrap( $("<div>").addClass("background") );

Example.

Upvotes: 1

Related Questions