2

Currently I have a loop that updates the DOM in each iteration; I have learned this is a bad practice & you should update the DOM as little as possible for better speed.

So I was wondering how I go about editing the below so I can store all the elements in one element or something & then do a single DOM addition once the loop ends.

Here is the loop..

    for (var i = spot; i < spot + batchSize && i < cats.options.length; i++) {

        // Check if the cat is selected

        if (cats.options[i].selected == true) {

            // Set this category's values to some variables
            var cat_id = cats.options[i].getAttribute('value');
            var cat_name = cats.options[i].text;     

            if (checkCatSICAdd(cat_id) === false) {           

                // Now we create the new element
                var new_option = document.createElement('option');

                // Add attribute
                new_option.setAttribute('value',cat_id);

                // Create text node
                var new_text_node = document.createTextNode(cat_name);

                // Append new text node to new option element we created
                new_option.appendChild(new_text_node);

                // Append new option tag to select list
                sel_cats.appendChild(new_option);

            } else {
                failed++;
            }

        }

    }
GEOCHET
  • 21,119
  • 15
  • 74
  • 98
Brett
  • 19,449
  • 54
  • 157
  • 290

5 Answers5

4

Working with DOM element in the loop is slow - no matter if you attach them to the document or not. Attaching them at the end is a bit faster since only only redraw is required but it's still slow.

The proper way is generating a plain old string containing HTML and attaching this string to the DOM using the innerHTML property of a DOM element.

ThiefMaster
  • 310,957
  • 84
  • 592
  • 636
  • I've always avoid using innerHTML as from my own experience & from what I've read innerHTML doesn't update the DOM & that's why it shouldn't be used on form elements etc.. ? – Brett Apr 04 '11 at 22:28
  • When setting values of form elements you don't modify the DOM itself anyway.. just the attributes of *existing* elements. – ThiefMaster Apr 04 '11 at 22:33
  • Don't use innerHTML, use DOM methods. If all you are doing is moving options in and out of the DOM, you can use a documentFragment as a store to hold unused options. Just add them to the fragment to remove them from the select, or add them to the select to put them back. – RobG Apr 05 '11 at 01:07
1

Your code should be ok. The DOM won't actually redraw until the Javascript has finished executing. However, if you've encountered a problem browser that does perform badly, you could try creating a new select before your loop that is not yet attached to the DOM, populating it as you are now, then replacing sel_cats with that new select at the end. That way, the DOM is only updated once.

Jacob
  • 77,566
  • 24
  • 149
  • 228
  • That's not a bad idea. Thanks! – Brett Apr 04 '11 at 22:30
  • Actually that won't really work as I would have to delete the current select list which may or may not have options in it. – Brett Apr 04 '11 at 22:33
  • Use a document fragment to hold unused options, then just move them in or out of the select in the document. – RobG Apr 05 '11 at 01:05
1

Your way is good enough unless you have great many items added to sel_cats - you add to the DOM only once.

The only way to improve efficiency might be to store the options as raw HTML then assign that after the loop:

var arrHTML = [];
for (var i = spot; i < spot + batchSize && i < cats.options.length; i++) {
    // Check if the cat is selected
    if (cats.options[i].selected == true) {
        // Set this category's values to some variables
        var cat_id = cats.options[i].value;
        var cat_name = cats.options[i].text;     
        if (checkCatSICAdd(cat_id) === false) {           
            arrHTML.push("<option value=\"" + cat_id + "\">" + cat_name + "</option>";
        }
        else {
            failed++;
        }
    }
}
sel_cats.innerHTML = arrHTML.join("");
Shadow The GPT Wizard
  • 66,030
  • 26
  • 140
  • 208
1

Once you have the select list assigned to a variable, remove it from the dom using removeChild on its parent tag. You can then use appendChild in the loop before adding the select list back into the dom.

herostwist
  • 3,778
  • 1
  • 26
  • 34
  • Actually that won't really work as I would have to delete the current select list which may or may not have options in it – Brett Apr 04 '11 at 22:33
  • removing the element from the dom doesn't delete it from memory if you have it assigned to a variable. you can remove it, change it then add it back. changes to the element when it is not part of the dom do not trigger page redraws. – herostwist Apr 05 '11 at 09:14
0

Your code is way bloated, DOM 0 methods will be much faster.

If speed really matters, store spot + batchSize && i < cats.options.length in variables so they aren't re-calcuated on each loop (modern browsers probably don't, but older ones did):

for (var i=spot, j=spot+batchSize, k=cats.options.length; i < j && i < k; i++) {

    // Store reference to element
    var opt = cats.options[i];

    // The selected property is boolean, no need to compare
    if (opt.selected) {

        // if checkCatSICAdd() returns boolean, just use it
        // but maybe you need the boolean comparison
        if (checkCatSICAdd(opt.name) === false) {

            // Wrapped for posting
            sel_cats.options[sel_cats.options.length] = 
                                         new Option(opt.value, opt.name);

        } else {
            failed++;
        }
    }
}
RobG
  • 142,382
  • 31
  • 172
  • 209