views:

70

answers:

3

I am working on a Browser-based media player which is written almost entirely in HTML 5 and JavaScript. The backend is written in PHP but it has one function which is to fill the playlist on the initial load. And the rest is all JS. There is a search bar that refines the playlist. I want it to refine as the person is typing, like most media players do. The only problem with this is that it is very slow and laggy as there are about 1000 songs in the whole program and there is likely to be more as time goes on.

The original playlist load is an ajax call to a PHP page that returns the results as JSON. Each item has 4 attirbutes:

  1. artist
  2. album
  3. file
  4. url

I then loop through each object and add it to an array called playlist. At the end of the looping a copy of playlist is created, backup. This is so that I can refine the playlist variable when people refine their search, but still repopulated it from backup without making another server request.

The method refine() is called when the user types a key into the searchbox. It flushes playlist and searches through each property (not including url) of each object in the backup array for a match in the string. If there is a match in any of the properties, it appends the information to a table that displays the playlist, and adds it to the object to playlist for access by the actual player.

Code for the refine() method:

function refine() {
    $('#loadinggif').show();
    $('#library').html("<table id='libtable'><tr><th>Artist</th><th>Album</th><th>File</th><th>&nbsp;</th></tr></table>");
    playlist = [];
    for (var j = 0; j < backup.length; j++) {
        var sfile = new String(backup[j].file);
        var salbum = new String(backup[j].album);
        var sartist = new String(backup[j].artist);
        if (sfile.toLowerCase().search($('#search').val().toLowerCase()) !== -1 || salbum.toLowerCase().search($('#search').val().toLowerCase()) !== -1 || sartist.toLowerCase().search($('#search').val().toLowerCase()) !== -1) {
            playlist.push(backup[j]);
            num = playlist.length-1;
            $("<tr></tr>").html("<td>" + num + "</td><td>" + sartist + "</td><td>" + salbum + "</td><td>" + sfile + "</td><td><a href='#' onclick='setplay(" + num +");'>Play</a></td>").appendTo('#libtable');
        }
    }
    $('#loadinggif').hide();
}

As I said before, for the first couple of letters typed, this is very slow and laggy. I am looking for ways to refine this to make it much faster and more smooth.

+1  A: 

One thing you could do is to take advantage of jQuery's ability to cache document fragments (the example is from a talk John Resig gave but you could apply it to your code):

// SLOW AS IT IS NOT CACHED. BECAUSE OF FOO
$("ul").append("<li><a>" + foo + "</a></li>");

// FAST. DOCUMENT FRAGMENT IS CACHED
$("<li><a></a></li>")
    .find("a").text(foo).end()
    .appendTo("ul");

This would be applicable to your line above:

$("<tr></tr>").html("<td>" + num + "</td><td>" + sartist + "</td><td>" + salbum + "</td><td>" + sfile + "</td><td><a href='#' onclick='setplay(" + num +");'>Play</a></td>").appendTo('#libtable');
gaoshan88
+3  A: 
  1. $('#search') isn't cheap. Move it outside the loop.
  2. Move append() outside the loop. Just accumulate the markup in a string and append it once after the loop.

This should be much faster

function refine() {
    $('#loadinggif').show();
    $('#library').html("<table id='libtable'><tr><th>Artist</th><th>Album</th><th>File</th><th>&nbsp;</th></tr></table>");
    playlist = [];
    var srchText = $('#search').val().toLowerCase();
    var markup = ["<tbody>"];
    for (var j = 0; j < backup.length; j++) {
        var sfile = backup[j].file.toLowerCase();
        var salbum = backup[j].album.toLowerCase();
        var sartist = backup[j].artist.toLowerCase();

        if (sfile.search(srchText) !== -1 || salbum.search(srchText) !== -1 || sartist.search(srchText) !== -1) {
            playlist.push(backup[j]);
            num = playlist.length-1;
            markup.push("<tr><td>" + num + "</td><td>" + sartist + "</td><td>" + salbum + "</td><td>" + sfile + "</td><td><a href='#' onclick='setplay(" + num +");'>Play</a></td></tr>");
        }
   }   
   markup.push("</tbody>");
   $("#libtable").append(markup.join(''));
   $('#loadinggif').hide();
}
Chetan Sastry
+1  A: 

I would suggest building your playlist information a little differently. You could probably get a pretty decent performance gain just by splitting up your playlist info by first letter.

albums = {
    'a': [list of albums starting with a],
    'b': ...
}

And doing the same for files and artists, of course.

Seph
+1 for pre-cooking data in a format that makes later manipulation more effective.
quixoto