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");
 }
}
Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Mar 25, 2013 at 14:35
\$\endgroup\$

2 Answers 2

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));
}
answered Mar 25, 2013 at 15:21
\$\endgroup\$
3
  • \$\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\$ Commented Mar 25, 2013 at 15:40
  • \$\begingroup\$ @CrimsonChin you could do a recursive setTimeout \$\endgroup\$ Commented Mar 25, 2013 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\$ Commented Mar 26, 2013 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

answered Mar 25, 2013 at 15:04
\$\endgroup\$
3
  • \$\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\$ Commented Mar 25, 2013 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\$ Commented Mar 25, 2013 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\$ Commented Apr 5, 2014 at 22:19

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.