19

I have a set of divs that looks like this:

<div id="con">
    <div> 1 </div>
    <div> 2 </div>
    <div> 3 </div>
    <div> 4 </div>
    <div> 5 </div>
</div>

But I want them to flip so that it looks like this:

<div> 5 </div>
<div> 4 </div>
<div> 3 </div>
<div> 2 </div>
<div> 1 </div>

So that when a new <div> is added it goes to the end of the list.

How can I do this (or is there a better way of doing this)?

Danziger
  • 16,123
  • 4
  • 41
  • 70
user1020293
  • 195
  • 1
  • 1
  • 7
  • 3
    _"So that when a div is added it will go to the end of the list...."_ - is your question how to reverse existing elements (some good answers for that below), or how to add new elements in a particular spot? Starting from your desired output, if you added a new `
    6
    ` should it go below 1 or above 5?
    – nnnnnn Oct 30 '11 at 23:03

7 Answers7

20

A vanilla JS solution:

function reverseChildren(parent) {
    for (var i = 1; i < parent.childNodes.length; i++){
        parent.insertBefore(parent.childNodes[i], parent.firstChild);
    }
}
akinuri
  • 9,179
  • 10
  • 57
  • 92
Ayman Abdel-Rahman
  • 512
  • 1
  • 6
  • 13
19

Wrapped up as a nice jQuery function available on any set of selections:

$.fn.reverseChildren = function() {
  return this.each(function(){
    var $this = $(this);
    $this.children().each(function(){ $this.prepend(this) });
  });
};
$('#con').reverseChildren();

Proof: http://jsfiddle.net/R4t4X/1/

Edit: fixed to support arbitrary jQuery selections

Phrogz
  • 284,740
  • 104
  • 634
  • 722
12

I found all the above somehow unsatisfying. Here is a vanilla JS one-liner:

parent.append(...Array.from(parent.childNodes).reverse());  

Snippet with explanations:

// Get the parent element.
const parent = document.getElementById('con');
// Shallow copy to array: get a `reverse` method.
const arr = Array.from(parent.childNodes);
// `reverse` works in place but conveniently returns the array for chaining.
arr.reverse();
// The experimental (as of 2018) `append` appends all its arguments in the order they are given. An already existing parent-child relationship (as in this case) is "overwritten", i.e. the node to append is cut from and re-inserted into the DOM.
parent.append(...arr);
<div id="con">
  <div> 1 </div>
  <div> 2 </div>
  <div> 3 </div>
  <div> 4 </div>
  <div> 5 </div>
</div>
Unmitigated
  • 46,070
  • 7
  • 44
  • 60
Chris K
  • 1,166
  • 13
  • 17
6

without a library:

function reverseChildNodes(node) {
    var parentNode = node.parentNode, nextSibling = node.nextSibling,
        frag = node.ownerDocument.createDocumentFragment();
    parentNode.removeChild(node);
    while(node.lastChild)
        frag.appendChild(node.lastChild);
    node.appendChild(frag);
    parentNode.insertBefore(node, nextSibling);
    return node;
}

reverseChildNodes(document.getElementById('con'));

jQuery-style:

$.fn.reverseChildNodes = (function() {
    function reverseChildNodes(node) {
        var parentNode = node.parentNode, nextSibling = node.nextSibling,
            frag = node.ownerDocument.createDocumentFragment();
        parentNode.removeChild(node);
        while(node.lastChild)
            frag.appendChild(node.lastChild);
        node.appendChild(frag);
        parentNode.insertBefore(node, nextSibling);
        return node;
    };
    return function() {
        this.each(function() {
            reverseChildNodes(this);
        });
        return this;
    };
})();

$('#con').reverseChildNodes();

jsPerf Test

Saxoier
  • 1,277
  • 1
  • 8
  • 8
  • Yes, also very hard for all to read and thus change and maintain when compared with solution below. – Michael Durrant Oct 30 '11 at 13:19
  • @Phrogz and Michael Durrant: If you prefer more than two reflows than you can [this version](http://jsfiddle.net/akfHm/). – Saxoier Oct 30 '11 at 13:26
  • @Saxoier is a better solution then accepted answer since it doesn't depend on jQuery, or more importantly cause reflow when dealing with large number of elements. – holmberd May 19 '18 at 21:05
3

One way:

function flip(){
 var l=$('#con > div').length,i=1;
 while(i<l){
   $('#con > div').filter(':eq(' + i + ')').prependTo($('#con'));
   i++;
 }
}
Shad
  • 14,384
  • 2
  • 20
  • 34
  • This is the one that worked for me because my parent div had other dynamically added child elements and I needed to match exact ones. – spcsLrg Mar 10 '21 at 14:17
0

Back in 2011 when this question was asked Flexbox had been around for only 1-2 years and support wasn't great, but now it's 2020 and things have improved a lot, so you could do that using only CSS!

You could try using Flexbox's flex-direction: column-reverse:

ul {
  display: flex;
  flex-direction: column-reverse;
}
<ul>
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
  <li>5</li>
</ul>

If you want to see this in action in a more complete example that toggles between column and column-reverse dynamically, check this other question: How can I reverse the order of div elements upside down on 'onclick'?

Danziger
  • 16,123
  • 4
  • 41
  • 70
  • 1
    I think this answer could be better served by being simplified to only the flexbox selectors. Your average Google searcher is going to get overwhelmed with this. – user120242 May 05 '20 at 23:57
  • 1
    Yep, you are probably right. I simplified this answer and added a link to the other one for a more complex example. Thanks for the tip! – Danziger May 06 '20 at 00:03
  • Flexbox reverse is just a cosmetic appearance trick while the internal structure is not reversed so, any scripting or logic iterates over the div elements it will find that they're still in the original order and not actually reversed. This answer defeats the purpose of the question which requested the reverse of the "div" elements not just UI render in the browser. – Abdulhameed Nov 08 '21 at 13:58
  • @Abdulhameed It's not clear from the OP's question what the goal is, and it also mentions "or is there a better way of doing this". If the goal is cosmetic, then this would be "a better way". This answer is just providing an alternative for other users that get here that might not want/need a JS solution to reverse the actual DOM nodes. – Danziger Nov 08 '21 at 16:04
0

Another (simpler?) vanilla javascript response: http://jsfiddle.net/d9fNv/

var con = document.getElementById('con');
var els = Array.prototype.slice.call(con.childNodes);
for (var i = els.length -1; i>=0; i--) {
    con.appendChild(els[i]);
}

Alternatively, a shorter but less efficient method: http://jsfiddle.net/d9fNv/1/

var con = document.getElementById('con');
Array.prototype.slice.call(con.childNodes).reverse().forEach(function(el) {
    con.appendChild(el);
});
Benjie
  • 7,413
  • 4
  • 27
  • 43