Forum Moderators: open

Message Too Old, No Replies

tables tables tables

showing and hiding data..

         

natty

10:32 am on Jun 23, 2004 (gmt 0)

10+ Year Member



hi all,

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";
}
}
}
}

unfortunately as the report is large this takes a while..
is there another option that would use less cycles..?

any ideas much appreciated..
nat

Bernard Marx

12:53 pm on Jun 23, 2004 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Greetings again, natty.

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.
[This doesn't seem to apply to arrays]

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]

...

john_k

1:40 pm on Jun 23, 2004 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Rather than hiding/showing table cells, you might want to just have one column for the values and then change the cell contents based upon the currency selection. Populate a javascript array for each currency type, then set the cell.innerHTML or cell.innerText to the appropriate symbol + value.

natty

1:50 pm on Jun 23, 2004 (gmt 0)

10+ Year Member



hi there again

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

john_k

2:18 pm on Jun 23, 2004 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



The most obvious thing to consider is breaking it up among several pages. However, since you said it is a "report," I am guessing that this isn't an option and it all needs to be on one page. (correct me if that's not right!)

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.

natty

2:56 pm on Jun 23, 2004 (gmt 0)

10+ Year Member



hi john_k,

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.!?

Bernard Marx

3:02 pm on Jun 23, 2004 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Well here's the sneaky script version. This relies on swapping the className (or part of it) on each table. The effect is generated by contextual selectors in CSS (affecting) <col> tags.
(I haven't taken my own advice with length properties; it wasn't worth it here.)

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!)

natty

3:10 pm on Jun 23, 2004 (gmt 0)

10+ Year Member



thanks again bernard.

im fiddling with the asp atm, a quick ciggy then back on the JS.. ill igve that a go.

john_k

3:20 pm on Jun 23, 2004 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



power JS?!?!?
sounds nice, anything further..?!

Not sure why I used that term. Guess I was thinking along the lines of js objects, but I don't think you need to do that.

you mean like generating a JS array with the ASP and fiddling it all from there?

Yes - your table would initially just have the headings. Then you could loop through the array to add the data rows. So now that is talking about something different than just changing the value when they select a different currency. There might not be much of a performance boost, so you should probably test with some dummy data before trying to put it all together. No sense in spending 4 hours to go from 15 seconds to 14.6 seconds.

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.

natty

4:23 pm on Jun 23, 2004 (gmt 0)

10+ Year Member



yay IE col element / object..
now i wish id known about that a month ago ;)

thanks as always all.

Bernard Marx

4:33 pm on Jun 23, 2004 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



...I always forget.
There may be some special requirements vis-a-vis table-layout: fixed; - I dunno.
Just for fun, and seeing as we're in IE territory (IE does this next one very reliably), there is another.

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 rules

onload = 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.rules

for(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>