Forum Moderators: open
im moving all our reports onto the intranet and am wondering if someone could help..
there are certain reports that show information in 2 or 3 currencies. the idea is to have a dropdown that allows you to select the currency and the info in the table changes..
the way i have addressed this is to generate a large table with all the data in and set the display property on the non showing cells to hidden and the run this bit of JS
selyLine = "";
function toggleCols(sel){
selects = document.getElementsByTagName("select");
ccy = sel.value;
currentSelected = sel.selectedIndex;
for (x=0;x<selects.length;x++){
selects[x].selectedIndex = currentSelected;
}
if( ccy == "" ) { return;}
switch (ccy) {
case "gbp" :
startCell=2;
endCell=3;
break;
case "eur" :
startCell=4;
endCell=5;
break;
case "usd" :
startCell=6;
endCell=7;
break;
}
var tables = document.getElementsByTagName("table");
for(tcnt=0;tcnt<tables.length;tcnt++){
var tbody = tables[tcnt].childNodes[0];
for (var row=0 ; row<tbody.childNodes.length ; row++){
for (var col=2 ; col<tbody.childNodes[0].childNodes.length ; col++){
tbody.childNodes[row].childNodes[col].style.display="none";
}
}
for (var r=0;r<tbody.childNodes.length ; r++){
for (var c=startCell;c<endCell+1;c++){
tbody.childNodes[r].childNodes[c].style.display="inline";
}
}
}
}
any ideas much appreciated..
nat
I have a few ideas for you. Since I don't have the HTML that all this applies to, I can't really test to see what changes bring practical efficiency gains (and which ones don't).
Also, I haven't attempted to change the code structurally, as I can't see the whole picture. I have made a lot of 'shortcut' substitutions - and hope that I haven't introduced any errors by doing so.
Here are the issues. These are most likely to be of increasing importance as we go down the list:
1. Make vars local
Local variables can be accesses much more quickly than globals, and should be used whenever possible - especially when it comes to loop counters. I have made these vars local:
[blue]selects, ccy, currentSelected, x, startCell, endCell, tcnt[/blue] 2. Put length in a local variable for looping
Although the 'set' returned by eg:
[blue].childNodes[/blue] behaves like an array, it is in fact a collection. Actual efficiency issues are dependent on the browsers implementation of the interface, but I think the following is generally true. [more about this later] If a loop is bounded by the length of a collection, it has to reference the
[blue]length[/blue] property every iteration, this may even involve some 'hidden' dynamic process. Put the length of the collection into a local var at the top of the loop, and use that. 3. Make shortcut substitutions for reference chains
..especially if used in a loop.
I have done this a lot to the code. Here's the most important one:
[blue]var tbodyChildnodes = tbody.childNodes[/blue] ..since this statement is embedded 2 loops deep, so it executes
[blue]rows*columns[/blue] times. [blue]tbody.childNodes[r].childNodes[c].style.display="inline";[/blue] OTHER APPROACHES
1. Collect the elements into custom arrays at initialization.
With this approach, when the page has loaded you use a similar methods to collect up all the relevant elements into arrays which more closely reflect the order that you are going to attack them later.
A couple of benefits:
a] You don't need to search down rows, and across each time to find the cells you want to display. You just run through 'flat' arrays, toggling the current one 'off', and the new one 'on'.
b] It's known that looping through arrays is much faster than looping collections (unless an 'iterator' object is available).
2. Change the CSS, on the table, or 'at source' in the stylesheet
[blue]----- just testing this ----[/blue] ...
thanks bernard for that mammoth post..
some things to mull over there certainly.. as u may have seen before, my js code is a little bit ropey. so those practices will help me pull my socks up.
the report page is generated via asp, which in turn is taking too long atm.. but thats another post ;)
so i guess i could generate a js array with all the cell inforamtion in it , and then use js to fill the page. but as i have said its a pretty big page mbe 2500-3000 table rows - although there is a seperate table for each year of stuff.
but is that going to take just as long if the array is humungous(sp?)?
as for the html bernard, just simple tables with anything from 4-14 cells per row, with total lines etc.
[edit]
just timed it 15 seconds!
2025 table rows
thats a long time
[/edit]
tia
just timed it 15 seconds!
15 seconds for what? The entire process, or just the query?
There are four main areas that can be of concern when generating a long report like this over the web:
1) DB query performance
2) Server script performance (ASP in this case)
3) Size of resulting HTML page sent to browser
4) Javascript performance on the client
If your query is taking 12 or 13 seconds all by itself, then, given the number of rows in your resulting HTML table, 15 seconds is pretty good. You just need to fix your query. Bottom line is that you will never get the entire process to take less time than the query takes :)
If you have a closed user base (like an intranet) and can dictate that Javascript must be enabled, then you could use some power javascript to drastically reduce the amount of HTML sent to the browser.
If the site needs to be more forgiving of disabled javascript, then you need to deliver HTML with the completed table and manipulate the cells (the point of your original post).
I just finished working on a project that included some long reports. It seems that the main bottlenecks were always in the SQL and we were able to reduce all of these longer reports to about a 5 second load time.
no im afraid the asp report takes more like 40 seconds its the JS that took 15 seconds.. which is why i was a little shocked..
yes you assume correct this(these) reports are on the intranet, so i have only IE to deal with and js is on .. i use stylesheets to put in page breaks etc for printing.
power JS?!?!?
sounds nice, anything further..?!
you mean like generating a JS array with the ASP and fiddling it all from there?
also its access not sql server for the moment. i think the query on the db takes mbe 3 seconds.
im doing a formatnumber on most of the outputted fields, i suspect that this is slowing everything a little.!?
Even though most the work is done internally, it could still be slower than running through an array - perhaps not in this case. Anyway...
!IMPORTANT: swap ¦ for an unbroken pipe
<html><head><title>TEST</title>
<style>
.togglecol{ display:none;}
table.showfrench .french {display:block}
table.showenglish .english{display:block}
/* continue..*/
</style>
<script>
var currLanguage = "french"function toggleColumn(select)
{
var language = select.options[select.options.selectedIndex].value
var tables = document.getElementsByTagName("table")
var classAdd = " show" + language
var classRemove = new RegExp("\\bshow"+currLanguage+"\\b¦\\b\\sshow"+currLanguage+"\\b")for(var iTab=0;iTab<tables.length;iTab++)
{
var table = tables[iTab]
var className = table.className
table.className = className.replace(classRemove,"") + classAdd
}
currLanguage = language
}</script>
</head>
<body><select onchange="toggleColumn(this)">
<option value="french">french
<option value="english">english
</select>
<!--
Tables must have the initial visible language (if there is one) as last class.'someclass' ,and 'otherclass', in cols, can be used for pure styling.
They can be safely removed (or name changed).
-->
<table class="showfrench" border="1" style="width: 200px; table-layout: fixed;" id="table1">
<col span="1" width="200" />
<!--
all toggling columns must include class: togglecol
and have their language as last class.
'togglecol' can be used for styling too.
-->
<col span="1" width="100" class="otherclass togglecol french" />
<col span="1" width="100" class="otherclass togglecol english"/>
<tr>
<th>VISIBLE</th>
<th>French</th>
<th>English</th>
</tr>
<tr>
<td>perm</td>
<td>perm</td>
<td>perm</td>
</tr>
<tr>
<td>french</td>
<td>french</td>
<td>french</td>
</tr>
<tr>
<td>english</td>
<td>english</td>
<td>english</td>
</tr>
</table></body>
</html>
(My RegExp could be better!)
power JS?!?!?
sounds nice, anything further..?!
you mean like generating a JS array with the ASP and fiddling it all from there?
In my original post, I was referring to a narrower version of this wherein only the amounts would go into three parallel js arrays. I think using the js arrays to rewrite the .innerText property of the cells would be faster than showing and hiding the various cells.
One other thought just occurred (light bulb!): I know that there is a columns property of the table object (at least in IE). So you might be able to simply show/hide the entire column. I've never used it for this, but it should be easy enough for you to check into.
This one goes directly to the stylesheet in question, and changes the rule spec.
Benefits.
1. No 'special' classes, other than each col has class="french" etc.
2. Doesn't actually require <col> elements, can put the class in each cell - not so good, considering the cost of ASPing this stuff.
3. Fun
Requires:
1. Don't put an inline display setting on any cell/column - it'll override the styleSheet rule change.
<html><head><title>TEST</title>
<style>.french {display:block;}
.english{display:none;}
/* continue..*/
</style>
<script>var languages = {french:1,english:1}
var styleObjects ;
var SSi = 0 // index of stylesheet holding language CSS rulesonload = function()
{
// create styles map
// this *might* be done before load
// esp. if stylesheet on-page.
styleObjects = getTogglerRuleStyles(SSi, languages)
// add handler to selector
document.getElementById("langselector").onchange = function()
{
var value = this.options[this.options.selectedIndex].value
toggleDisplay(styleObjects[value])
}
}//
function toggleDisplay(style)
{
if(styleObjects.current)
styleObjects.current.display = "none"
styleObjects.current = style
style.display = "block"}
// Returns object 'map' of languages to
// appropriate stylesheet rule style object
function getTogglerRuleStyles(SSi,selectors)
{
var obj = {}
var SS = document.styleSheets[SSi]
var SR = SS.rulesfor(var k=0;k<SR.length;k++)
{
var rule = SR[k]
var text = rule.selectorText
var style = rule.style
//
if(languages[text.replace('.','')])
{
obj[text.replace('.','')] = rule.style
// set current visible
if(style.display=="block")
obj.current = style
}
}
return obj
}</script>
</head>
<body><select id="langselector">
<option value="french">french
<option value="english">english
</select><table border="1" style="width: 200px; table-layout: fixed;" id="table1">
<col span="1" width="200" />
<!-- 'otherclass' not needed -->
<col span="1" width="100" class="otherclass french" />
<col span="1" width="100" class="otherclass english"/>
<tr>
<th>VISIBLE</th>
<th>French</th>
<th>English</th>
</tr>
<tr>
<td>perm</td>
<td>french</td>
<td>english</td>
</tr>
<tr>
<td>perm</td>
<td>french</td>
<td>english</td>
</tr>
<tr>
<td>perm</td>
<td>french</td>
<td>english</td>
</tr>
</table></body>
</html>