Extend Div smoothly without lagging

Viewed 77

On button click, I am fetching data from database using codeigniter and a div is being appended with the fetched data. Problem is that while the data is of high volume, it takes time and the webpage is lagging. What should I do to resolve this issue?

My code is given below.

$("#get_spare_list").click(function() { 

    $("body").css("cursor", "progress");
    var ht = "";
    $.ajax({
        url: "<?php echo base_url('Spare/get_all_spare_json'); ?>",
        type: 'POST',
        dataType: 'json',
        data: {"param1": 'value1'},
    })
    .done(function(data) {
        var no = 1;
        var ht = "<table class='table'><thead><tr><th>No</th><th>Name</th><th>Code</th><th>Min qty</th><th>uni</th><th>Group</th><th>Sub Category</th><th>Part Number</th><th>Location</th><th>Image</th><th >Tyre</th><th>Back</th></tr></thead>";
        $.each(data, function(key, val) {
            ht +="<tr>"+"<td>"+no+"</td>"+"<td>"+val.name+"</td>"+"<td>"+val.code+"</td>"+"<td>"+val.min_qty+"</td>"+"<td>"+val.unit+"</td>"+"<td>"+val.group+"</td><td>"+val.sub_category+"</td><td>"+val.part_number+"</td><td>"+val.location+"</td>";
            if (val.image) {
                ht += "<td><a target='_blank' href='"+"<?php echo base_url('../uploads/'); ?>/"+val.image+"'><button class='fa fa-picture-o'></button></a></td>";
            }else{
                 ht += "<td></td>";
            }
            ht +="<td>"+val.tyre+"</td>";
            ht += "<td>";
                if (val.reusable == 1) {
                    ht +="yes";
                }else{
                    ht+="no";
                };
            ht += "</td>";
            ht += "<td><button class='btn edit btn-info btn-xs' data-toggle='modal' data-target='#editModel' data-id='"+val.id+"' data-name='"+val.name+"'  data-code='"+val.code+"'  data-min_qty='"+val.min_qty+"'  data-unit='"+val.unit+"'  data-group='"+val.group+"'  data-sub_category='"+val.sub_category+"' data-part_number='"+val.part_number+"' data-location='"+val.location+"'  data-tyre_number='"+val.tyre+"' data-back='"+val.reusable+"'><span class='fa fa-edit'></span></button></td>";
            ht += "</tr>";
            no++;
        });
        $("#js_res").append(ht);
        $("body").css("cursor", "default");

    })
    .fail(function() {

        alert("error");
        $("body").css("cursor", "default");
    });
});

jQuery should append data to the div smoothly without lagging and hanging of web page.

2 Answers

When the browser receives a response, the callback function passed to .done() is added to JavaScript's event loop queue.

While the data is being processed, it blocks JavaScript's event loop to process further tasks. Even the stuff like handling mouse input is blocked meanwhile. If the data being processed is too big, this becomes more observable to the end user.

You can divide the data retrieved into chunks and push them to the event loop separately by calling setTimeout() on them. Source: Cooperation part of You Don't Know JS: Async & Performance

Here is how it can work for your code below, the callback function passed to .done() and a helper function:

function(data) {
    var ht = "<table class='table'><thead><tr><th>No</th><th>Name</th><th>Code</th><th>Min qty</th><th>uni</th><th>Group</th><th>Sub Category</th><th>Part Number</th><th>Location</th><th>Image</th><th >Tyre</th><th>Back</th></tr></thead>";
    $("#js_res").append(ht);
    processData(data, 1);
    $("body").css("cursor", "default");
}

function processData(data, no) {
    var chunk = data.splice(0, 100);
    var ht = "";

    $.each(chunk, function(key, val) {
        ht +="<tr>"+"<td>"+no+"</td>"+"<td>"+val.name+"</td>"+"<td>"+val.code+"</td>"+"<td>"+val.min_qty+"</td>"+"<td>"+val.unit+"</td>"+"<td>"+val.group+"</td><td>"+val.sub_category+"</td><td>"+val.part_number+"</td><td>"+val.location+"</td>";
        if (val.image) {
            ht += "<td><a target='_blank' href='"+"<?php echo base_url('../uploads/'); ?>/"+val.image+"'><button class='fa fa-picture-o'></button></a></td>";
        }else{
             ht += "<td></td>";
        }
        ht +="<td>"+val.tyre+"</td>";
        ht += "<td>";
            if (val.reusable == 1) {
                ht +="yes";
            }else{
                ht+="no";
            };
        ht += "</td>";
        ht += "<td><button class='btn edit btn-info btn-xs' data-toggle='modal' data-target='#editModel' data-id='"+val.id+"' data-name='"+val.name+"'  data-code='"+val.code+"'  data-min_qty='"+val.min_qty+"'  data-unit='"+val.unit+"'  data-group='"+val.group+"'  data-sub_category='"+val.sub_category+"' data-part_number='"+val.part_number+"' data-location='"+val.location+"'  data-tyre_number='"+val.tyre+"' data-back='"+val.reusable+"'><span class='fa fa-edit'></span></button></td>";
        ht += "</tr>";
        no++;
    });

    $("#js_res").append(ht);
    if (data.length > 0) {
        setTimeout(function(){
            processData(data, no);
        }, 0);
    }
}

Another solution would be to use a web worker for handling the creation of these table rows.

A web worker can't edit the DOM but you can pass the data to the web worker. The worker can then create the html string and pass it back to the main JS thread which can then append it.

var myWorker = new Worker('create-table.js');

// after successful ajax call, hand data over to worker
myWorker.postMessage(data);
console.log('Message posted to worker');

inside the create-table.js we can then specify our worker:

onmessage = function(e) {
  console.log('Message received from main script');
  /* 
   This is just an example from mdn documentation
   Obviously you should recreate your logic for creating your <tr> here
  */
  var workerResult = 'Result: ' + (e.data[0]); 
  console.log('Posting message back to main script');
  postMessage(workerResult);
}

In the main js file you can then react to the result of the worker like this:

myWorker.onmessage = function(e) {
  $("#js_res").append(e.data)
  console.log('Message received from worker');
}
Related