How to improve performance when rendering <table> in IE 8? - javascript

I have a jquery function which adds tag to first row of table.
I tried using append, however its not working, so i got a solution which is very slow and it somehow gives error "Script on this page is causing internet explorer run slow..."
Function is as
jQuery.fn.fixGridView = function () {
"use strict";
// var start = +new Date(); // log start timestamp
if (jQuery(this).is('table') && this.find('thead').length === 0) {
var theadv = "<thead><tr>" + this.find('tbody tr:first').html(); +"</tr></thead>";
this.find('tbody tr:first').remove();
var htmlv = this.html();
this.html(theadv + htmlv);
}
//var end = +new Date(); // log end timestamp
// var diff = end - start;
// alert(diff);
return this;
};
Can anybody help me to make this code run faster?
EDIT: I have to use IE..that is the requirement (ie8).
Edit2: I have created js fiddle: http://jsfiddle.net/4xLzL/

To increase rendering performance you must understand that DOM manipulation (including reflows & repaints) are expensive operations. Your code currently re-creates the entire table with the <thead> added the majority of the <tbody> content remains the same. This massive "redraw" of the table is highly inefficient. Especially when in IE 8, where rendering tables is extra slow, you have to modify the DOM as little as possible.
I've refactored your logic to minimize the number of lookups performed to find elements by saving them to a variable to be re-used. Also, removed the .html('...') call that re-renders the table, but instead used the .prepend() function to add the <thead> into the <table> as the first child.
jQuery.fn.fixGridView = function () {
"use strict";
var start = +new Date(); // log start timestamp
if (this.is('table') && this.children('thead').length === 0) {
var firstRow = this.children('tbody').children('tr:first');
var thead = "<thead><tr>" + firstRow.html() + "</tr></thead>";
firstRow.remove();
this.prepend(thead);
}
var end = +new Date(); // log end timestamp
var diff = end - start;
alert(diff);
return this;
};
$(document).ready(function () {
$('table[id*="gvCategories"]').fixGridView();
});
Go ahead and test it in IE8: http://jsfiddle.net/amyamy86/4xLzL/7/

The problem is not with the plugin, but with your selector. You only want tables, so modify your selector to be as follows.
$('table [id*="gvCategories"]').fixGridView();
I also updated the fiddle.

Related

How to create an elapsed time calendar in JavaScript

I have the original code, which only allows the user to choose from future dates. However, I would like to change this so that the user can pick a previous date, and it will show the time since then. The if-statement is where it decides if the date is in the past or future. However, even when I remove the entire statement, it allows me to pick a past date but won't print it to the screen.
This is the rest of the code if you are interested/able to help. I understand the snippet doesn't show everything but I don't know what other pieces of code to include. The entirety of the code is simply copied into a Google Doc: cal.js
code snippet:
// select date
function selectDate(event) {
if (event === undefined) { // get caller element in IE8
event = window.event;
}
var callerElement = event.target || event.srcElement;
dateObject.setDate(callerElement.innerHTML);
var fullDateToday = new Date();
var dateToday = Date.UTC(fullDateToday.getFullYear(),
fullDateToday.getMonth(), fullDateToday.getDate());
var selectedDate = Date.UTC(dateObject.getFullYear(),
dateObject.getMonth(), dateObject.getDate());
** if (selectedDate <= dateToday) {
document.getElementById("cal").style.display ="block";
return false;
} **
document.getElementById("tripDate").value =
dateObject.toLocaleDateString();
hideCalendar();
countdown = setInterval(updateCountdown, 1000);
document.getElementById("countdownSection").style.display = "block";
}

Display a JavaScript variable in a page loaded in innerHTML [duplicate]

This question already has answers here:
Load scripts inside innerHTML [duplicate]
(2 answers)
Closed 4 years ago.
I have an HTML page that loads within another HTML page via innerHTML. After several days of work, this script works fine and another JS file is called for the interior page, a file (named "Unified_app.js") that basically runs some date calculations. Everything is working fine and the correct dates print to the console. However, I can't figure out on the page within a page can display the console dates. Document.write does not work in this situation (I'm assuming because of the tags are not read properly?), so I need to come up with a workaround. Any ideas?
This is the innerHTML functions as I have them:
function getYearOffset(strCutoffDate, intYearOffset)
{
var datCurrentDate = new Date();
var intCurrentYear = datCurrentDate.getFullYear();
var intCurrentMonth = strCutoffDate.substr(5, 2) - 1;
var intCurrentDay = strCutoffDate.substr(8, 2);
var datCutoffDate = new Date(intCurrentYear, intCurrentMonth, intCurrentDay);
if (Number(datCurrentDate) < Number(datCutoffDate))
{
var datRequestedDate = new Date(datCurrentDate.getFullYear(), intCurrentMonth, intCurrentDay);
}
else
{
var datRequestedDate = new Date(datCurrentDate.getFullYear() + intYearOffset, intCurrentMonth, intCurrentDay);
}
return datRequestedDate.getFullYear();
}
var script = document.createElement("script");
script.src = "/resource/resmgr/scripts/Unified_app.js";
document.head.appendChild(script);
function getInclude(strIncludeContainer, strIncludeURL)
{
var strPage = '';
var intIndexOfBodyOpen = 0;
var intIndexOfBodyClose = 0;
var objXhttp;
objXhttp = new XMLHttpRequest();
objXhttp.onreadystatechange = function()
{
if (this.readyState == 4 && this.status == 200)
{
strPage = this.responseText;
intIndexOfBodyOpen = strPage.indexOf('<body>');
intIndexOfBodyClose = strPage.indexOf('</body>');
document.getElementById(strIncludeContainer).innerHTML = strPage.substring(intIndexOfBodyOpen + 6, intIndexOfBodyClose);
}
};
objXhttp.open("GET", strIncludeURL, true);
objXhttp.send();
}
I'm using:
<script>document.write(award_year1);</script>
to write the following date calls:
const date = new Date();
let offset = 0;
const threshold = new Date();
threshold.setMonth(3); //January is 0!
threshold.setDate(3);
if (Date.now() > threshold) {
offset = 1;
}
var theDate = new Date();
var award_year1 = date.getFullYear() + offset;
var award_year2 = date.getFullYear() + 1 + offset;
console.log(award_year1);
console.log(award_year2);
When loading the page-within-a-page HTML file or the interior page itself I get the correct date calculations sent to the console, but I can't seem to get them to print within the innerHTML page when loaded into the other page. Any ideas you could send me down the right path? This is probably beyond my level of understanding of JavaScript. I thought perhaps my code was not in the correct order but I've been fiddling with this and can't seem to figure out where or why.
I'm not sure if this will solve the problem but you can try it.
As you said the document.write will not be triggered cause your JS is loaded before your DOM is.
document.addEventListener("DOMContentLoaded", function(event) {
//your functions
});
Maybe this will help you out
I guess this is just not possible. I ended up replacing the innerHTML with an iframe and that seems to have worked so that I can now use script tags. Not an ideal solution but it works

Adding two number variables returns NaN

Working on a simple game, using delta time for the first time.
Delta time (dt) is created in this function:
function main() {
var now = Date.now();
var dt = (now - lastTime) / 1000.00;
update(dt);
render();
lastTime = now;
requestAnimationFrame(main);
};
gameTime (my second variable) is just created as a var.
var gameTime = 0;
The problem comes when I try to add dt to gameTime, here:
function update(dt){
gameTime += dt;
};
this returns NaN, whatever I seem to do to it. I can display gameTime, and add to it using ++, and I can display dt (0.017 usually), but as soon as I add one to the other, I get NaN.
How can I fix this?
Does your lastTime contain anything before running main() for the first time?
If not, then this code var dt = (now - lastTime) / 1000.00; can't work because lastTime is undefined
First of all, if you return you can see your result which is not NaN, basically you write it like this :
function update(dt)
{
return gameTime += dt;
};
Second of all,if you are not using this code in vanilla javascript and it's related to unity, try to initiate your variables. like how you defined var gameTime = 0;, check other variables and initiate them.

JavaScript browser parsing speed testing

I'm looking into the speed of JavaScript parsers in web browsers, importantly it needs to be easy to demonstrate. I came up with a simple test - the idea being that each script block is parsed and executed individually, so a large block of script could be timed:
<script>var start = new Date().getTime();</script>
<script>
/*! jQuery v1.8.2 jquery.com | jquery.org/license */
...
</script>
<script>alert ( new Date().getTime() - start );</script>
Superficially this appears to work, removing the middle script block will result in a negligible time.
However I'm not certain that my logic is not fundamentally flawed.
It seems the answer is broadly yes, but to get a reasonable result (like anything else) the test should be run many times to level out the effects of compilation caching and garbage collection. The test above can easily be placed into the Parse-n-Load library: http://carlos.bueno.org/2010/02/measuring-javascript-parse-and-load.html
Thanks for your help
This may be of help!
var start = new Date().getTime();
var end = new Date().getTime();
var time = end - start;
alert('Execution time: ' + time);
If you want to benchmark your JavaScript, include MilliSeconds etc.
var t = new Date();
var start = t.getTime()*1000 + t.getMilliseconds();
/* do something*/
var t2 = new Date();
var end = t2.getTime()*1000 + t.getMilliseconds();
alert("The Code needed " + (end-start) + " milliseconds. That are " + parseInt((end-start)/1000) + " seconds.");
You might want to differentiate between parsing and execution time. You could do something like
<script>start = Date.now();</script>
<script>
parsed = Date.now();
/*! jQuery v1.8.2 jquery.com | jquery.org/license */
…
</script>
<script>var end = Date.now();
alert ( "parsed in " + (parsed - start) + "ms" );
alert ( "executed in " + (end - parsed) + "ms" );
alert ( "overall time: " + (end - start) + "ms" );
</script>
With that you might be able to detect cached parse trees etc. Yet, for more distinct information have a look at your developer tools, they show such type of information in their profiler section. Or in Opera, it's included in the load process of scripts in the network panel.
This answer is from 10 years in the future.
There are a number of approaches to timing web page processes including:
Date-related methods:
Date.now();
and:
console.time-related methods:
console.time('myTimer');
console.timeLog('myTimer');
console.timeEnd('myTimer');
but, since late 2015, the ideal way to time web page processes using high-resolution timestamps has been:
window.performance.now();
Using Performance:
The Performance interface, accessed via window.performance has numerous methods, including:
timeOrigin
mark
measure
getEntries
toJSON
and more.
But in order to time a script, all you need is window.performance.now():
let scriptStart = window.performance.now();
let scriptEnd = window.performance.now();
let scriptDuration = (scriptEnd - scriptStart);
Working Example:
let paragraph = document.querySelector('p');
let button = document.querySelector('button');
const runTimedScript = () => {
let scriptStart = window.performance.now();
for (let i = 0; i < 10000; i++) {
paragraph.textContent = 'Loop iteration ' + (i + 1);
}
let scriptEnd = window.performance.now();
let scriptDuration = (scriptEnd - scriptStart);
button.textContent = 'Re-run Script';
console.log('The script ran in ' + scriptDuration + ' milliseconds');
}
button.addEventListener('click', runTimedScript, false);
button {
cursor: pointer;
}
<p></p>
<button type="button">Run Script</button>
<p>To see how long the script takes to run,<br />
click the button above repeatedly.</p>
Further Reading:
To find out more about the Performance Interface, see:
Performance API (MDN)

IE8 (javascript): very slow to load large list of options in SELECT element

I'm loading SELECT element with 6000 items using createElement and add methods. The code is shown below, and can also be accessed here. In IE8 it takes around 16 seconds to load the list and about the same time to clear it. In IE9 and Firefox the loading time is < 2 seconds and clearing time is < 1 second. Any ideas on how I can improve the speed in IE8?
Thank you.
<script type="text/javascript">
window.onload = loadList;
function loadList() {
clearList();
var start = new Date().getTime();
var o = document.getElementById("listLookupAvailableItems")
for (var i = 0; i < 6000; i++) {
var option = document.createElement("option");
option.text = 'ABCDF ' + i;
option.value = option.text;
o.add(option, o.options[null]);
}
log('Load time: ' + (new Date().getTime() - start));
}
function clearList() {
var start = new Date().getTime();
document.getElementById("listLookupAvailableItems").options.length = 0;
log('Clear time: ' + (new Date().getTime() - start));
return false;
}
function log(txt) {
document.getElementById('infoPanel').innerHTML += '</br>' + txt;
}
</script>
My guess is that that particular DOM operation is just really slow in IE8. In general, manipulating the DOM is the slowest type of operation in any browser. To get around that I typically try to find ways to combine my changes into one DOM update (e.g. add an HTML "batch" of 6000 rows to a table instead of individually adding 6000 rows to a table).
In this example the only way to do that would probably be to create all of the <option> elements as HTML and then use innerHTML to insert them into the <select>. See this jsfiddle example: http://jsfiddle.net/pseudosavant/bVAFF/
I don't have IE8 to test with, but it is much faster even in Firefox (22ms vs 500ms) for me.
Update
Looks like it didn't work with innerHTML in IE for loading the list, but it did work for clearing it. Loading it works using jQuery $(o).html(html); though. I updated the jsfiddle example and it works in IE9, and hopefully IE8 now.
Javascript:
$(document).ready(function(){
loadListBatch();
});
function loadListBatch() {
clearListBatch();
var start = new Date().getTime();
var o = document.getElementById("listLookupAvailableItems")
var html = "";
for (var i = 0; i < 6000; i++) {
html += "<option value='"+'XYZ' + i+"'>"+'XYZ ' + i+"</option>";
}
// o.innerHTML = html; // Only one DOM operation, but still doesn't work in IE
$(o).html(html); // Using jQuery to insert the HTML makes it work with IE
console.log('Load time: ' + (new Date().getTime() - start));
}
function clearListBatch() {
var start = new Date().getTime();
document.getElementById("listLookupAvailableItems").innerHTML = ""; // It was already only one DOM call, but this is faster it seems.
console.log('Clear time: ' + (new Date().getTime() - start));
return false;
}
If you are supporting IE7/IE8 you should minimize JavaScript manipulation of the DOM. So if you are appending, inserting or deleting nodes you need to minimize DOM manipulation in general. The best solution is to bulk update items.
So, if you have a select list and you are doing JQuery.append() you will get better performance if you concatenate your entire options string before appending.
var str = $('<option value="x">Item 1</option>' + '<option value="y">Item 2</option>');
$('#selectMenu').append(str);
//or in a loop
var array = ['orange','apple','grapes','mangoes'];
var str = '';
for (var x= 0; x < array.length; x++) {
str = str + '<option value="' + x + '">' + x + '</option>';
}
$('#selectMenu').append(str);
Additionally, if you want to see how slowly JavaScript is executed by IE8 run the SunSpider JS test. Firefox 22 and Chrome 27 are around 300 ms while IE8 is around 4,000 ms. That tells a lot about why your JS speeds are slow. Interestingly IE10 comes in at less than 200 ms now. http://www.webkit.org/perf/sunspider/sunspider.html
I had a very similar situation.
I have a set of inputs with 1700+ so I provided a "filter" option that would copy the select and apply a filter based on a besides the copied list. (It "opens" a dialog that expands the dropdownlistbox to a list almost as big as 80% of the screen)
Copying the worked unnoticeably in other browsers but took 8-15 secs in IE.
The solution, based on previous answers, and also based on this post (Learn the slow (and fast) way to append elements to the DOM) was to add all the items to a HTL string, then assigning this to the innerHTML of a new object, one that is not yet part of the DOM. And finally, replacing the object from the DOM with the new one.
This apparently reduces the number of "reflow" operations performed by the browser, which is most likely the culprit of such slow performance.
Some of the test before implementing this style, was to run the full for loop without adding the options to the list, and in such test, the code executed very fast, it was clear that selectElement.add(optionElement) was the slow part.
Here is an example of what my function ended like:
function fillselector(IdSelect){
var selector = document.getElementById(IdSelect);
if( !selector ){
alert('Original select element not found.');
return;
}
var lista = document.getElementById('selectFilter_lista');
if( !lista ){
alert('Copied list element not found.');
return;
}
var filterText = noSpecialChars(document.getElementById('selectFilter_text').value);
var options =''
for (var i = 0; i < selector.length; i++){
if (filterText == '' || noSpecialChars(selector[i].text).indexOf(filterText) != -1 ){
//Commented code works but is painfuly slow on IE
//var option = document.createElement("option");
//option.value = selector[i].value;
//option.text = selector[i].text;
//lista.add(option);
options += '<option value="'+selector[i].value+'">'+selector[i].text+'</option>';
}
}
var newList = document.createElement('select');
newList.id='selectFilter_list';
newList.className='selectFilter_list';
newList.size = 20;
newList.ondblclick= function(){closeselector(IdSelect, true);}
newList.innerHTML = options;
newList.value = selector.value;
var listParent = lista.parentElement; //<div> that only contains the <select>
listParent.removeChild(lista);
listParent.appendChild(newList);
}

Categories