patternjavascriptMinor
Large table updates using AJAX in Internet Explorer 11
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
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
For example, change :
to:
Javascript
The javascript can now be written to exploit the tbody wrappers.
Some of the code looks to be as little dodgy. For example, .addClass(tempValues.network_color) ... .removeClass('warning')
// Find the parent row
var $tr = $('#' + i);
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
andinside the loop/if lines to give one tbody per computer block. (Hopefully tbodys will not mess up tablesorter).
- Move id="{{computer.name}}"
into thetag.
- 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.