Large table updates using AJAX in Internet Explorer 11 I have a website which only needs to support IE11. It is a single page application, which has about 200 table rows and each table row has 5 child rows.

There is a pulsing function that updates the table as records come in. Table rows are skipped over if no update comes in.

However, when receiving large updates (which should only occasionally happening), the application will hang as it slowly processes the javascript. I've tried to limit the JavaScript as much as possible, but still have a long running function.

I am a backend developer by nature, and was wondering if anyone had any tips to help support large table Ajax updates for IE since IE so poorly handles JS.

function writeTableLines(tempRows){
    /* This Function takes care of updating the text and coloring of
    required dynamic fields. 
    All other values are not dynamically written.
    */
    for( i in tempRows){
   //i is the computer name
        tempValues = tempRows[i];
        // For Row
        selector = "[id='"+i+"']";

        // Network Name
        network_selector = "[id='"+i+"_network']";
        $(network_selector).text(tempValues['network']);

        if (tempValues['network_color']){
            $(network_selector).addClass(tempValues['network_color']);
            $(selector).find('.name').addClass(tempValues['network_color']);
        }else{
            $(network_selector).removeClass('warning');
             $(selector).find('.name').removeClass('warning');
        }

        // Boot Time
        boot_selector = "[id='"+i+"_boot']";
        $(boot_selector).text(tempValues['boot']);
        if (tempValues['boot_color']){
            $(boot_selector).addClass(tempValues['boot_color']);
            $(selector).find('.name').addClass(tempValues['boot_color'])
        }else{
            $(boot_selector).removeClass('issue');
            $(selector).find('.name').removeClass('issue');
        }

        // Last Checked In Timestamp
        check_in_selector = "[id='"+i+"_checked_in']";
        $(check_in_selector).text(tempValues['checked_in']);
        if (tempValues['service_unresponsive']){
            $(check_in_selector).addClass('redline');
            $(selector).find('.name').addClass('redline');
        }else{
            $(check_in_selector).removeClass('redline');
            $(selector).find('.name').removeClass('redline');
        }

        util_selector = $(selector).find('td.util').find('a');
        $(util_selector).text(tempValues['util'])
         if (tempValues['util_class']){
            $(util_selector).addClass(tempValues['util_class']);
        }else{
            $(util_selector).removeClass('redline warning');
        }

        workgroup_selector = $(selector).find('td.workgroup');

        if (($.trim(tempValues['workgroup'])) != $.trim($(workgroup_selector).text())){
            if ((tempValues['workgroup'] != selected) &amp;&amp; (selected != 'All')){
                $(workgroup_selector).addClass('warning');
            }else{
                $(workgroup_selector).removeClass('warning');
            }
        }
        $(workgroup_selector).text(tempValues['workgroup'])
        toggle_links(i, tempRows[i]);
        $('#connectionGrid').trigger('updateAll', [false]);
    }
}


This function iterates over only received data. For each row item that was received, update the text of the cell, and add coloring as necessary.

I'm thinking I might just be screwed since its IE, but am open to all suggestions and ideas.

Image of the rows - child rows only available when expanded, but still need updates.
Searching a whole, large DOM for elements is a real performance killer. When possible, always try to search a fragment, or traverse the DOM relative to a known element. 

With a little rearrangement of the HTML, "network", "boot" and "check_in" elements can be found within the corresponding "selector" element, similar to the way "util" and "workgroup" elements are currently found. This alone should give a significant performance boost.

HTML


There's a missing &lt;/tr&gt; somewhere.
Move &lt;tbody&gt; and &lt;/tbody&gt; inside the loop/if lines to give one tbody per computer block. (Hopefully tbodys will not mess up tablesorter).
Move id="{{computer.name}}" into the &lt;tbody&gt; tag.
Give a class name to elements that need to be addressed :


For example, change :   

&lt;td class="info" colspan="1" id="{{computer.name}}_network"&gt;{{ computer.active_drive.name }}&lt;/td&gt;


to:

&lt;td class="info network" colspan="1" id="{{computer.name}}_network"&gt;{{ computer.active_drive.name }}&lt;/td&gt;



Then, if they are not required elsewhere, purge all IDs in the repeated block.


Javascript

The javascript can now be written to exploit the tbody wrappers.

function writeTableLines(tempRows) {
    /* 
     This Function takes care of updating the text and coloring of required dynamic fields. 
     All other values are not dynamically written.
    */
    var tempValues, $tbody, $name, $network, $boot, $check_in, $util, $workgroup,
        $connectionGrid = $('#connectionGrid');

    // Avoid creating so many strings in the loop by defining class names and selectors here.
    // This is more a memory consideration than speed.
    var clss = {
        'warning': 'warning',
        'issue': 'issue',
        'redline': 'redline'
        'redlineWarning': 'redline warning',
    };
    var selectors = {
        'network': '.network',
        'boot': '.boot',
        'check_in': '.checked_in',
        'name': '.name',
        'util': 'td.util a',
        'workgroup': 'td.workgroup'
    };

    for(i in tempRows) {
        tempValues = tempRows[i];

        // Find the container
        $tbody = $('#' + i); // This is the only element in each block that needs an ID.

        if($tbody.length == 0) return; // avoid unnecessary work if element is not found

        // Now find elements by class, within the container
        $network = $tbody.find(selectors.network);
        $boot = $tbody.find(selectors.boot);
        $check_in = $tbody.find(selectors.checked_in);
        $name = $tbody.find(selectors.name);
        $util = $tbody.find(selectors.util);
        $workgroup = $tbody.find(selectors.workgroup);

        // In all the code below, address tempValues properties with dot.notation, not associative['notation']

        $network.text(tempValues.network);
        if (tempValues.network_color) {
            $network.addClass(tempValues.network_color);
            $name.addClass(tempValues.network_color);
        } else {
            $network.removeClass(clss.warning);
            $name.removeClass(clss.warning);
        }

        $boot.text(tempValues.boot);
        if (tempValues.boot_color) {
            $boot.addClass(tempValues.boot_color);
            $name.addClass(tempValues.boot_color);
        } else {
            $boot.removeClass(clss.issue);
            $name.removeClass(clss.issue);
        }

        $check_in.text(tempValues.checked_in);
        if (tempValues.service_unresponsive) {
            $check_in.addClass(clss.redline);
            $name.addClass(clss.redline);
        } else {
            $check_in.removeClass(clss.redline);
            $name.removeClass(clss.redline);
        }

        $util.text(tempValues.util);
        if (tempValues.util_class) {
            $util.addClass(tempValues.util_class);
        } else {
            $util.removeClass(clss.redlineWarning);
        }

        if (($.trim(tempValues.workgroup)) != $.trim($workgroup.text())) {
            if (tempValues.workgroup != selected &amp;&amp; selected != 'All') {
                $workgroup.addClass(clss.warning);
            } else {
                $workgroup.removeClass(clss.warning);
            }
        }
        $workgroup.text(tempValues.workgroup);

        toggle_links(i, tempValues);
        $connectionGrid.trigger('updateAll', [false]);
    }
}


Some of the code looks to be as little dodgy. For example, .addClass(tempValues.network_color) ... .removeClass('warning') means that any added class that is not warning will never be removed (unless by some other code). Contrast with .addClass('redline') ... .removeClass('redline'), which is guaranteed to add/remove the same class.

Aside: With the tbodys in place, you could consider styling them with eg a border that will expand/contract as the details are shown/hidden.

If performance is still poor, you'll need to investigate deeper to discover what's taking time. Though I'm not an expert driver, Chrome debug tools are very good for diagnosis.

Edit

Back to a single &lt;tbody&gt; but with class="info network", class="info boot", class="info check_in", in place, try selecting as follows :

// Find the parent row
var $tr = $('#' + i); // A parent row

if($tr.length == 0) return; // avoid unnecessary work if element is not found

var $childRows = $tr.nextUntil(".parent"); // the parent's child rows

$network = $childRows.find(selectors.network);
$boot = $childRows.find(selectors.boot);
$check_in = $childRows.find(selectors.checked_in);

$name = $tr.find(selectors.name);
$util = $tr.find(selectors.util);
$workgroup = $tr.find(selectors.workgroup);


This will be slightly less efficient than finding elements within tbody containers but still better than finding by ID - and Tablesorter will still work.