HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptMinor

Large table updates using AJAX in Internet Explorer 11

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
ajaxexplorerupdatesinternetlargeusingtable

Problem

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_i

Solution

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 ` somewhere.



  • Move and 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 tag.



  • Give a class name to elements that need to be addressed :



For example, change :

{{ computer.active_drive.name }}


to:

{{ computer.active_drive.name }}


  • 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 && 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
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);

Code Snippets

<td class="info" colspan="1" id="{{computer.name}}_network">{{ computer.active_drive.name }}</td>
<td class="info network" colspan="1" id="{{computer.name}}_network">{{ computer.active_drive.name }}</td>
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 && selected != 'All') {
                $workgroup.addClass(clss.warning);
            } else {
                $workgroup.removeClass(clss.warning);
            }
        }
        
// 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);

Context

StackExchange Code Review Q#118678, answer score: 4

Revisions (0)

No revisions yet.