0
\$\begingroup\$

I am trying to write an efficient loop for rendering a jQuery Mobile listview. Is this the most efficient loop? I append elements 100 at a time to try and allow the user to see progress without blocking the UI. This currently still needs work.

I also use a blend of html strings and jQuery DOM objects. I'm not sure about the performance cost.

What are your thoughts? Assume items could be an array of 600/700 items.

/**
 * Renders the passed in list items into a JQM listview.
 * @param items {Array{Objects}} - An array of key/value objects
 */
function renderSourceItems(items)
{
    var content = $page.find(".content");

    if (!items)
    {
        content.html('<p>No Data Found</p>');
    }
    else
    {
        var column,
            value,
            list,
            markup = "";

        column = _.keys(items[0])[0]; // The key is the database column

        content.empty();

        $('<ul/>', {
            "data-role" : "listview",
            "data-filter": "true",
            "data-theme" : "c"
        }).appendTo(content).listview();

        list = content.find('ul');

        for (var i = 0; i < items.length; i++)
        {
            value = items[i][column];
            markup += '<li><a href="#apage" data-column="' + column  +'" data-value="' +  value +'">' + (value || "[No Value]") + '</a></li>';

            if (i % 100 === 0)
            {
                list.append(markup).listview("refresh");
                markup = "";
            }
        }

        // refresh triggers the page to adjust its sizes,
        // otherwise long lists can force the footer navbar to the bottom of the list intead of the screen bottom.
        list.append(markup).listview("refresh");
    }
}
\$\endgroup\$
2
\$\begingroup\$
//elements that don 't change should be declared in a scope that doesn't 
 //get executed all the time to avoid redundant opearation
var content = $(".content", $page);

function renderSourceItems(items) {

  //JS normally pulls up variable declarations, even when declared
  //anywhere. To avoid readability issues, it's best you declare
  //them up here
  var column, list;

  //the "return early" or "blacklist" method returns if any anomalies
  //are found earlier in the code. That way, you don't use nested ifs
  if (!items) {
    content.html('<p> No Data Found </p>');
    return;
  }

  //down here, it's assumed that items does exist and has content

  column = _.keys(items[0])[0];
  content.empty();

  //depending on the browser, DOM strings vs jQuery DOM vary in
  //performance. Since it varies, I'd rather improve readability
  //by using one of them that's more readable, where in my case
  //it's jQuery DOM building
  list = $('<ul/>', {
    "data-role": "listview",
    "data-filter": "true",
    "data-theme": "c"
  });


  //our recursive function using setTimeout to avoid freezing UI
  (function looper(i) {

    var value, item;

    if (!(i % 100)) {
      value = items[i][column];

      //each item gets built using our readable method
      //here we make a list item and append to it a link with
      //the given data
      item = $('<li/>').appendTo(list);
      $('<a/>', {
        'href': '#apage ',
        'data - column ': column,
        'data - value ': value
      }).text(value || ' [No Value]').appendTo(item);

    }

    setTimeout(function () {
      if (++i < items.length) {
        //when not yet the last item, repeat
        looper(i);
      } else {
        //else we end

        //now you notice that we only append the list to the DOM down here
        //why not append it beforehand? The issue is DOM access - it's slow!
        //we take advantage of jQuery using DOM fragments, DOM elements that
        //are not in the DOM and just operate off-DOM. We only append
        //to the DOM after everything is completed, thus minimizing
        //DOM access from items.length times to just 1
        list.appendTo(content).listview("refresh");
      }
    }, 200);

    //start with index 0
  }(0));



}
\$\endgroup\$
  • \$\begingroup\$ nice answer, one thing the for loop has to append all the li items I just put the "break" in as a chance to update the ui so the user knows the site hasnt frozen \$\endgroup\$ – JonWells Mar 25 '13 at 15:40
  • \$\begingroup\$ @CrimsonChin you could do a recursive setTimeout \$\endgroup\$ – Joseph Mar 25 '13 at 16:55
  • \$\begingroup\$ I might, i don't want do multiple appends are considerably slower but they would give the user better visual feedback \$\endgroup\$ – JonWells Mar 26 '13 at 8:22
0
\$\begingroup\$
  • If you have a reasonably or entirely fixed template, use something like HandlebarsJS to make a template that you populate.

  • Now about performance, I would suggest you make a jsPerf test for your specific case and try out the different options to see which is fastest since the right answer is really different for each case. Here's an interesting answer to a question about passing HTML strings: https://stackoverflow.com/questions/3015335/jquery-html-vs-append

  • A big no no for me is putting an append in a loop. It's going to be much better if you do a loop and cache the things to append into a variable, then do a single append with that. DOM manipulations carry some of the highest costs to performance. If you check out this article you can see the tests with arrays and performance costs first hand: http://www.learningjquery.com/2009/03/43439-reasons-to-use-append-correctly

\$\endgroup\$
  • \$\begingroup\$ Thanks, I originally had the append outside the loop and rendered the whole list as a markup string but with lists of 700 items the UI seems to be blocked for ages (couple of seconds). \$\endgroup\$ – JonWells Mar 25 '13 at 15:07
  • \$\begingroup\$ Yeah if you check out that last article you can see their tests and it's something like 9-10x faster to have it outside. They included some string concatenation improvements there too, which might help reduce those "ages". \$\endgroup\$ – Jonny Sooter Mar 25 '13 at 15:12
  • \$\begingroup\$ @CrimsonChin a couple os seconds to append 700 items on a mobile device browser does not sound that bad to me \$\endgroup\$ – Mirko Apr 5 '14 at 22:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.