Search code examples
javascriptjqueryajaxtablesorter

Everything works but there is LAG in the script?


I have a somewhat big piece of javascript/ajax code, and I would like to see if it is possible to make it run more efficiently WITHOUT taking out any functions.

My problem is that I'm getting a page called 'getlastupdate.php', it only has 14 numbers, these numbers are the date and time that a database table was last updated.

the format is:

  • Year-Month-Day-HH-mm:ss

Here's an example: 20130724082105

I took out the ':.-' because we don't need it.

My javascript code compares this with another ajax GET that repeatedly fetches the current date/time from 'getlastupdate.php'. If it finds that the two values are not the same it triggers another GET to fetch the row info.

It takes the updated row info and displays it in the browser. Finally it GETs the entire updated table and puts it back into the table (tablesorter).

Everything seems to work fine however I noticed some LAG when the script is adding the new table info and deleting the old ? or at least I think that is the main cause for the LAG.

Here is the code:

<link type="text/css" rel="stylesheet" href="tablesorter/qtip/jquery.qtip.min.css" />
<script type="text/javascript" src="tablesorter/jquery-1.10.2.min.js"></script> 
<!-- blue theme stylesheet -->
<link rel="stylesheet" href="tablesorter/final/theme.blue.css">
<!-- tablesorter plugin -->
<script src="tablesorter/final/jquery.tablesorter.js"></script>
<!-- tablesorter widget file - loaded after the plugin -->
<script src="tablesorter/final/jquery.tablesorter.widgets.js"></script>


<!-- START: toastmessage Notify -->
    <link type="text/css" href="tablesorter/final/toastmessage/jquery.toastmessage-min.css" rel="stylesheet"/>
    <script type="text/javascript" src="tablesorter/final/toastmessage/jquery.toastmessage-min.js"></script>
<!-- END: toastmessage Notify -->


<script type="text/javascript" src="tablesorter/qtip/jquery.qtip.min.js"></script>


<script type="text/javascript">
var comper;
function checkComper() {
var SvInfo;

  var onResponse = function(comperNow) {  // comperNow is the latest date/time var
    //check if comper has been set / first time method is called
    if (comper === undefined) {
      comper = comperNow;
      return;
    }

    if (comper !== comperNow) {


var Vinfoo;
$.get("getlastupdate2.php", function(primaryAddType){
//   alert("Data: " + primaryAddType);
   Vinfoo = primaryAddType;


// show a message to the visitor ----> 
//alert(Vinfoo);
  $().toastmessage('showNoticeToast', Vinfoo);


 });
$('#append').trigger('click'); // UPDATE THE TABLE BUT IT TAKES 3-4 SEC. AND I NEED IT TO SPEED UP!

// and update comper to show the date/time from comperNow
comper = comperNow;
}
};
$.get('getlastupdate.php', onResponse);

}
var tid = setInterval(checkComper, 2000); // repeat myself

$(function() {

$('[title!=""]').qtip({}); // A bit better. Grab elements with a title attribute that isn't blank. <-- CUSTOM TOOLTIP

var $table = $("table.tablesorter");
$("#append").click(function(e) { 
e.preventDefault();
$.get('updatetable.php', function(data) 
{
$table
.find('tbody')
.html('')
.append(data);
$table.trigger("update", [true]);

$('[title!=""]').qtip({}); // A bit better. Grab elements with a title attribute that isn't blank. <-- CUSTOM TOOLTIP

});
});

  // call the tablesorter plugin
$("table.tablesorter").tablesorter({
    theme: 'blue',

    // hidden filter input/selects will resize the columns, so try to minimize the change
    widthFixed : true,

    //sortList: [[2,0],[1,0]],

    // initialize zebra striping and filter widgets
    widgets: ["saveSort", "zebra", "filter"],

//  headers: {  },

    widgetOptions : {

      // If there are child rows in the table (rows with class name from "cssChildRow" option)
      // and this option is true and a match is found anywhere in the child row, then it will make that row
      // visible; default is false
      filter_childRows : false, 

      // if true, a filter will be added to the top of each table column;
      // disabled by using -> headers: { 1: { filter: false } } OR add class="filter-false"
      // if you set this to false, make sure you perform a search using the second method below
      filter_columnFilters : true,

      // css class applied to the table row containing the filters & the inputs within that row
      filter_cssFilter : 'tablesorter-filter',

      // class added to filtered rows (rows that are not showing); needed by pager plugin
      filter_filteredRow   : 'filtered',

      // add custom filter elements to the filter row
      // see the filter formatter demos for more specifics
      filter_formatter : null,

      // add custom filter functions using this option
      // see the filter widget custom demo for more specifics on how to use this option
      filter_functions : null,

      // if true, filters are collapsed initially, but can be revealed by hovering over the grey bar immediately
      // below the header row. Additionally, tabbing through the document will open the filter row when an input gets focus
      filter_hideFilters : false, // true, (see note in the options section above)

      // Set this option to false to make the searches case sensitive
      filter_ignoreCase : true,

      // if true, search column content while the user types (with a delay)
      filter_liveSearch : true,

      // jQuery selector string of an element used to reset the filters
      filter_reset : 'button.reset',

      // Delay in milliseconds before the filter widget starts searching; This option prevents searching for
      // every character while typing and should make searching large tables faster.
      filter_searchDelay : 300,

      // if true, server-side filtering should be performed because client-side filtering will be disabled, but
      // the ui and events will still be used.
      filter_serversideFiltering: false,

      // Set this option to true to use the filter to find text from the start of the column
      // So typing in "a" will find "albert" but not "frank", both have a's; default is false
      filter_startsWith : false,

      // Filter using parsed content for ALL columns
      // be careful on using this on date columns as the date is parsed and stored as time in seconds
      filter_useParsedData : false

    }

  });

  // External search
  // buttons set up like this:
  // <button type="button" class="search" data-filter-column="4" data-filter-text="2?%">Saved Search</button>
  $('button.search').click(function(){
    /*** first method *** data-filter-column="1" data-filter-text="!son"
      add search value to Discount column (zero based index) input */
    var filters = [],
      col = $(this).data('filter-column'), // zero-based index
      txt = $(this).data('filter-text'); // text to add to filter

    filters[col] = txt;
    // using "table.hasFilters" here to make sure we aren't targetting a sticky header
    $.tablesorter.setFilters( $('table.hasFilters'), filters, true ); // new v2.9

    /** old method (prior to tablsorter v2.9 ***
    var filters = $('table.tablesorter').find('input.tablesorter-filter');
    filters.val(''); // clear all filters
    filters.eq(col).val(txt).trigger('search', false);
    ******/

    /*** second method ***
      this method bypasses the filter inputs, so the "filter_columnFilters"
      option can be set to false (no column filters showing)
    ******/
    /*
    var columns = [];
    columns[5] = '2?%'; // or define the array this way [ '', '', '', '', '', '2?%' ]
    $('table').trigger('search', [ columns ]);
    */

    return false;
  });

});
</script>

I found the source of the LAG if I comment out this line:

$('#append').trigger('click'); // UPDATE THE TABLE BUT IT TAKES 3-4 SEC. AND I NEED IT TO SPEED UP! it will speed everything up but how do I get it to speed up or even slow down so that it doesn't lock up the current page.

What also speeds-up the loading is if I comment out this:

    var $table = $("table.tablesorter");
    $("#append").click(function(e) { 
    e.preventDefault();
    $.get('updatetable.php', function(data) 
    {
    $table
    .find('tbody')
    .html('')
    .append(data);
    $table.trigger("update", [true]);

// ******** +++++++++++ **************    

    $('[title!=""]').qtip({}); // <-- CUSTOM TOOLTIP THIS SLOWS DOWN THE LOADING ALSO!!!!!

// ******** +++++++++++ **************    
    });
    });

@Abudoul Sy, Here is what I have now:

<script type="text/javascript">
// I should be put at the top of the file (not in an Ajax Loop)
var compileHTML = function (html) {
        var div = document.createElement("div");
        div.innerHTML = html;
        var fragment = document.createDocumentFragment();
        while ( div.firstChild ) {
            fragment.appendChild( div.firstChild );
        }
        return fragment
    };

var comper;
function checkComper() {
var SvInfo;

  var onResponse = function(comperNow) {  // comperNow is the latest date/time var
    //check if comper has been set / first time method is called
    if (comper === undefined) {
      comper = comperNow;
      return;
    }

    if (comper !== comperNow) {


var Vinfoo;
$.get("getlastupdate2.php", function(primaryAddType){
//   alert("Data: " + primaryAddType);
   Vinfoo = primaryAddType;


// show a message to the visitor ----> 
//alert(Vinfoo);
  $().toastmessage('showNoticeToast', Vinfoo);


 });
$('#append').trigger('click'); // UPDATE THE TABLE BUT IT TAKES 3-4 SEC. AND I NEED IT TO SPEED UP!

// and update comper to show the date/time from comperNow
comper = comperNow;
}
};
$.get('getlastupdate.php', onResponse);

}
var tid = setInterval(checkComper, 2000); // repeat myself

$(function() {

        $(document).ready(function()
        {
        $('[title!=""]').qtip({}); // A bit better. Grab elements with a title attribute that isn't blank. <-- CUSTOM TOOLTIP
        })

var $table = $("table.tablesorter");
//Keeping a reference variable to 
var $tableContents = $table.find('tbody');


$("#append").click(function(e) { 
e.preventDefault();
$.get('updatetable.php', function(data) 
{
$table

 //When ajax is done
var compiledHtml = window.compileHTML(data); 
 $tableContents.html(compiledHTML);
 $table.trigger("update", [true]);


        $(document).ready(function()
        {
        $('[title!=""]').qtip({}); // A bit better. Grab elements with a title attribute that isn't blank. <-- CUSTOM TOOLTIP
        })

});
});

  // call the tablesorter plugin
$("table.tablesorter").tablesorter({
    theme: 'blue',

    // hidden filter input/selects will resize the columns, so try to minimize the change
    widthFixed : true,

    //sortList: [[2,0],[1,0]],

    // initialize zebra striping and filter widgets
    widgets: ["saveSort", "zebra", "filter"],

    headers: { 8: { sorter: false, filter: false } },

    widgetOptions : {

      // If there are child rows in the table (rows with class name from "cssChildRow" option)
      // and this option is true and a match is found anywhere in the child row, then it will make that row
      // visible; default is false
      filter_childRows : false, 

      // if true, a filter will be added to the top of each table column;
      // disabled by using -> headers: { 1: { filter: false } } OR add class="filter-false"
      // if you set this to false, make sure you perform a search using the second method below
      filter_columnFilters : true,

      // css class applied to the table row containing the filters & the inputs within that row
      filter_cssFilter : 'tablesorter-filter',

      // class added to filtered rows (rows that are not showing); needed by pager plugin
      filter_filteredRow   : 'filtered',

      // add custom filter elements to the filter row
      // see the filter formatter demos for more specifics
      filter_formatter : null,

      // add custom filter functions using this option
      // see the filter widget custom demo for more specifics on how to use this option
      filter_functions : null,

      // if true, filters are collapsed initially, but can be revealed by hovering over the grey bar immediately
      // below the header row. Additionally, tabbing through the document will open the filter row when an input gets focus
      filter_hideFilters : false, // true, (see note in the options section above)

      // Set this option to false to make the searches case sensitive
      filter_ignoreCase : true,

      // if true, search column content while the user types (with a delay)
      filter_liveSearch : true,

      // jQuery selector string of an element used to reset the filters
      filter_reset : 'button.reset',

      // Delay in milliseconds before the filter widget starts searching; This option prevents searching for
      // every character while typing and should make searching large tables faster.
      filter_searchDelay : 300,

      // if true, server-side filtering should be performed because client-side filtering will be disabled, but
      // the ui and events will still be used.
      filter_serversideFiltering: false,

      // Set this option to true to use the filter to find text from the start of the column
      // So typing in "a" will find "albert" but not "frank", both have a's; default is false
      filter_startsWith : false,

      // Filter using parsed content for ALL columns
      // be careful on using this on date columns as the date is parsed and stored as time in seconds
      filter_useParsedData : false

    }

  });

  // External search
  // buttons set up like this:
  // <button type="button" class="search" data-filter-column="4" data-filter-text="2?%">Saved Search</button>
  $('button.search').click(function(){
    /*** first method *** data-filter-column="1" data-filter-text="!son"
      add search value to Discount column (zero based index) input */
    var filters = [],
      col = $(this).data('filter-column'), // zero-based index
      txt = $(this).data('filter-text'); // text to add to filter

    filters[col] = txt;
    // using "table.hasFilters" here to make sure we aren't targetting a sticky header
    $.tablesorter.setFilters( $('table.hasFilters'), filters, true ); // new v2.9

    /** old method (prior to tablsorter v2.9 ***
    var filters = $('table.tablesorter').find('input.tablesorter-filter');
    filters.val(''); // clear all filters
    filters.eq(col).val(txt).trigger('search', false);
    ******/

    /*** second method ***
      this method bypasses the filter inputs, so the "filter_columnFilters"
      option can be set to false (no column filters showing)
    ******/
    /*
    var columns = [];
    columns[5] = '2?%'; // or define the array this way [ '', '', '', '', '', '2?%' ]
    $('table').trigger('search', [ columns ]);
    */

    return false;
  });

});
</script>

Solution

  • Check out your code:
    Everytime you're doing an Ajax Request you're doing this:

    $table
    .find('tbody')
    .html('')
    .append(data);
    $table.trigger("update", [true]);
    

    To explain what it does :

    1. you call $.find on a html tag (slow)
    2. you remove the content of the table (causes the browser to repaint the pixels in white)
    3. you ask the Browser's DOM to parse a whole html table causing multiple layout reprocessing and repaints (critically bad)
    4. calling trigger on the table (ok)

    I think that just by processing your data better can speed up your process countless times depending on the size of your table. Consider this:

    // I should be put at the top of the file (not in an Ajax Loop)
    window.compileHTML = function (html) {
        var div = document.createElement("div");
        div.innerHTML = html;
        var fragment = document.createDocumentFragment();
        while ( div.firstChild ) {
            fragment.appendChild( div.firstChild );
        }
        return fragment
    };
    //Keeping a reference variable to 
    var $tableContents = $table.find('tbody')
    
    //When ajax is done
    var compiledHtml = window.compileHTML(data);
    $tableContents.html(compiledHtml);
    $table.trigger("update", [true]);
    

    Edit: If compileHTML doesn't work. A nice way of doing almost the same thing (but without compiling) would be to do:

    //put just after $table declaration
    var $tableContents = $table.find('tbody')
    
    //When ajax is done
    var $html = $('<tbody/>').html(data);
    $tableContents.replaceWith($html)
    $table.trigger("update", [true]);
    

    Edit:

    What I did was creating a document fragment (outside of DOM), let the html processing via compileHTML not have any incidence on the current page. And when all is done. Appending only the root of the new dom tree created to the DOM, causing only one repaint against: "as many as there is cells and rows" will cause your browser to look more stable.

    Optional (not totally): There is probably more pitfalls on your code, but investigating performances is hard. I would recommend doing a timeline check on Chrome/Firebug -> open console (F12 or Alt+shift+i) press the "timeline" tab.
    Press (ctrl/cmd)+E and let and update pass , repress ctrl+E and you'll see some histogram chart, the bigger bars mean your app is laggy/janky, you can then click on the bar to see the stack trace of all the function called and thus be optimized.
    Hope that helps :)