2

I have the following:

<div id='a'>
   <div id='a1' />
   <div id='a2' />
   <div id='a3' />
   <div id='a4' />
   <div id='a5' />
</div>

Suppose that when a button is pressed I need to rearrange it like this:

<div id='a'>
   <div id='a2' />
   <div id='a4' />
   <div id='a3' />
   <div id='a1' />
   <div id='a5' />
</div>

or something similar.

Now, I know how to get all the children from div a. I know how I can sort them (whatever my algorithm is)

The question is then what's the best way to rearrange them ?

I could remove them all. and then readd them one by one in the correct order.

That'd work but I think it would be wrong because of the reflow everytime a new item is added ?

can anybody help ? Point me to an article on the web or write down a solution

thanks

chacko
  • 5,004
  • 9
  • 31
  • 39

2 Answers2

3

So you've got a sorted array, you can do 2 things [Performance Test]

var array = [ ... ]; // sorted array of elements
var len = array.length;
  1. Detach the parent before appending the elements, then append the parent back. This seems to be the fastest alternative (especially for large collections).

    var parent = root.parentNode;
    var next   = root.nextSibling;
    parent.removeChild(root);
    for (var i = 0; i < len; i++) {
      root.appendChild(array[i]);
    }
    parent.insertBefore(root, next);
    
  2. Another option would be to use documentFragment, and then you append it to the DOM at once.

    var fragment = document.createDocumentFragment();
    for (var i = 0; i < len; i++) {
        fragment.appendChild(array[i]); // lighweight, off-DOM container
    }
    root.appendChild(fragment); // append to the DOM at once
    

    Unfortunately, it slows down your code in Chrome. (faster in IE, and FF)

I would like to point out that unless you're dealing with 1000+ elements the difference won't be noticable.

gblazex
  • 49,155
  • 12
  • 98
  • 91
  • Yeah, at one point I was going to mention a `DocumentFragment` as an option, but the thing is, if they're already in the DOM, removing them will have an impact just like moving them will, so there's no real net gain. Using a fragment is certainly useful when dealing with building up something that *isn't* already in the DOM... – T.J. Crowder Jan 26 '11 at 11:19
  • 1
    @T.J. Crowder - You would think, but have you done any **testing** before writing a whole paragraph? :) I found a *30%* speed *gain* in IE 6-8, *10% gain* in FF, and *9% loss* in Chrome. It seems it does have an **impact**... It's not consistent though . http://jsperf.com/document-fragment-sort – gblazex Jan 26 '11 at 11:59
  • @galambalazs: Fair 'nuff. Ignore my update, btw, I missed that you test fragment both with and without removing the root. – T.J. Crowder Jan 26 '11 at 12:14
  • I am still amazed that there is not a standard way of doing this. I thought that re-arranging divs could be a common operation – chacko Jan 26 '11 at 12:17
  • 1
    @chacko - I think removing a node before heavy operations in order to avoid reflows is standard enough. :) – gblazex Jan 26 '11 at 12:21
  • @galambalazs: I just suddenly realized in the shower how dense we were being, looking at fragments and such rather than detaching the parent node. Came back here suspecting you would have got there already, and you have. Excellent, +1. Also: Agree that removing the container and putting it back is plenty standard enough. – T.J. Crowder Jan 26 '11 at 13:05
  • @T.J. Crowder - Yep, hiding it used to be another option, but I've always found it to be slower (which may be an evidence that not the reflows cost the most, but keeping the DOM live, like links between the objects, and lookup tables for ids etc). `:)` – gblazex Jan 26 '11 at 13:09
  • 1
    Would recommend adding an example of correctly removing and restoring the parent, e.g.: http://pastie.org/1499055 But I felt it was too big an edit to just go ahead and do. – T.J. Crowder Jan 26 '11 at 13:09
1

Use the insertBefore/appendChild DOM methods to re-order them.

And better use jQuery or another javascript framework so you don't have to do all the dirty work yourself.

ThiefMaster
  • 310,957
  • 84
  • 592
  • 636
  • No need to remove them all, `insertBefore` and `appendChild` work fine to *move* things. – T.J. Crowder Jan 26 '11 at 10:50
  • You don't have to detach them from the DOM first? – ThiefMaster Jan 26 '11 at 10:52
  • @ThiefMaster: Nope. From the docs (link in my answer): *"If the `newChild` is already in the tree, it is first removed."* So the browser knows you're moving it, and can hopefully optimize its reflows and such. – T.J. Crowder Jan 26 '11 at 10:53
  • @chacko: As I say in my answer, if you don't remove them first, you're at least giving the browser enough information that it can optimize its reflows. With remove then re-insert, it doesn't have that info. – T.J. Crowder Jan 26 '11 at 10:55
  • Does the browser even do any layout *while* the function is running? I would have thought that it would only re-layout the page once it's complete...? That's just my thought though, I don't know it for a fact ... – jcoder Jan 26 '11 at 13:18