views:

78

answers:

4

Hi guys,

So I've built this mail-program-like HTML viewer (no real emails, just some messages from user to user). It basically behaves just like windows explorer, where you have the labels at the top and the files you can sort by date or alphabetically. Each message looks like this:

<div class="mail_msg" d_id="363">
    <div class="done"><img src="../"></div>
    <div class="absender">demo</div>
    <div class="subject">subject</div>
    <div class="name">name</div>
    <div class="date">16.09.2010</div>
</div>

Atop of all these messages there is a bar to sort them alphabetically or by date. (using prototypejs)

$$('.absender_label','.subject_label', '.bname_label').each(function(el){
    el.observe('click', function(){

        /* ... */

        var tar = el.className.split('_')[0];
        var els = $('widget_mail_'+target).select('.mail_msg'),
            sortedEls = els.sort(function(x, y) {
                var a = x.down('.'+tar).innerHTML.toLowerCase(),
                    b = y.down('.'+tar).innerHTML.toLowerCase();

                return a < b ? -1 : a > b ? 1 : 0;
            });

        $('widget_mail_'+target).select('.mail_msg').invoke('remove');
        sortedEls.each(function(x){
            if(dir=='bottom') {
                $('widget_mail_'+target).insert({bottom:x});
            } else if(dir=='top') {
                $('widget_mail_'+target).down('.mail_msg_label').insert({after:x});
            }
        });
    });
});

I know that there is way too much DOM-manipulation going on. I select all related divs, sort them, throw away all the old unsorted messages and insert the sorted ones at the top or at the bottom (first click from A-Z, second click from Z-A).

It still works with hundreds of messages, but it takes 2 or 3 seconds. I receive the messages as JSON and parse HTML from that, so using a table sorting script like this is not an option at this point.

How can I optimize this sorting?

+2  A: 

You can use .detach() to remove elements from the DOM, sort it and then display it. Maybe this will speed up you script.

jcubic
I was thinking the same thing. Still, I don't think this is the *only* thing which needs to be done; I think browsers are getting much better at reflowing as little as possible.
strager
well, I'm not using jquery. detach() looks pretty nice, but is there a prototypejs equivalent for that?
koko
I found this page http://bytes.com/topic/javascript/answers/448176-detach-dom-element
jcubic
@koko, There is `Element.remove`. This may remove event handlers, though (I am unsure). Also, you could try hiding the parent element (with e.g. `display: none;`) to prevent reflows while you edit the DOM elements.
strager
+1  A: 

You could give elements absolute positions via CSS and just change their positions, instead of manipulating the DOM.

Ates Goral
This totally ruins selection, though, and is bound to break e.g. when the window's size changes (in which case you'd need *more* code to handle such cases, causing more manual reflow).
strager
@strager: Good points. This is indeed not an ideal solution.
Ates Goral
+2  A: 

Each time the user sort, you sort the dataset.
Then you render them as HTML using innerHTML, each time.

You can use a Javascript template engine to do that(I contribute to PURE but there are plenty of engines available today)

If a template engine is an overkill for you, you can:

  • stringify your HTML above
  • for each record in your data, build a line filling some placeholders
  • concatenate the lines in a string
  • and finally inject the string using innerHTML
  • sort your data and restart

While it seems heavier, this will be faster than all the DOM manipulations.

Mic
I think it would not make a great difference to sort the JSON and build the HTML template instead of sorting the way I do now. But it's a funny idea to stringify the HTML and sort it that way. It does sound strange but it might be faster.
koko
@koko: Don't start with HTML. Start with data and then generate enough HTML cells to hold the data. Then sort and populate the cells.
Ates Goral
@koko, here is an example with my template engine, but you should get the same speed sorting your data and using strings: http://gist.github.com/584717
Mic
in this case you lose all event-listeners and references to sorted elements
chapluck
@chapluck, you can use event delegation to a parent of the list
Mic
@Mic, in simple case i can. But if i use some js-control(like date-picker that is bound to some anchor) i have to rewrite logic of js-control or recreate it every sorting.
chapluck
@chapluck, may be having a control on each lines for a big list is the problem to solve. Not the list generation ;)
Mic
A: 

Add to your JSON dataset just one more field like: related DOMElement position or DOMElement reference.

mail_msg["dom_el_ref"] = createRelatedDomEL(mail_msg);

Do all sorting and comparing stuff with JSON dataset. And after that make according order in DOM-list.

chapluck