Hi monks,
Today I take responsible to tune a web application in JS. it fetch data from oracle and generate a dynamic menu tree on left side. Below is the part of code:
tree.js
// Node object function Node(id, pid, name, url, title, target, icon, iconOpen, open) + { this.id = id; this.pid = pid; this.name = name; this.url = url; this.title = title; this.target = target; this.icon = icon; this.iconOpen = iconOpen; this._io = open || false; this._is = false; this._ls = false; this._hc = false; this._ai = 0; this._p; }; // Tree object function Tree(objName) { this.config = { target : null, folderLinks : true, useSelection : true, useCookies : true, useLines : true, useIcons : true, useStatusText : false, closeSameLevel : false, inOrder : false } this.icon = { root : '../../images/tree/base.gif', folder : '../../images/tree/folder.gif', folderOpen : '../../images/tree/folderopen.gif', node : '../../images/tree/page.gif', empty : '../../images/tree/empty.gif', line : '../../images/tree/line.gif', join : '../../images/tree/join.gif', joinBottom : '../../images/tree/joinbottom.gif', plus : '../../images/tree/plus.gif', plusBottom : '../../images/tree/plusbottom.gif', minus : '../../images/tree/minus.gif', minusBottom : '../../images/tree/minusbottom.gif', nlPlus : '../../images/tree/nolines_plus.gif', nlMinus : '../../images/tree/nolines_minus.gif' }; this.obj = objName; this.aNodes = []; this.aIndent = []; this.root = new Node(-1); this.selectedNode = null; this.selectedFound = false; this.completed = false; }; // Adds a new node to the node array Tree.prototype.add = function(id, pid, name, url, title, target, icon, + iconOpen, open) { this.aNodes[this.aNodes.length] = new Node(id, pid, name, url, tit +le, target, icon, iconOpen, open); }; // Open/close all nodes Tree.prototype.openAll = function() { this.oAll(true); }; Tree.prototype.closeAll = function() { this.oAll(false); }; // Outputs the tree to the page Tree.prototype.toString = function() { var str = '<div class="tree">\n'; if (document.getElementById) { if (this.config.useCookies) this.selectedNode = this.getSelect +ed(); str += this.addNode(this.root); } else str += 'Browser not supported.'; str += '</div>'; if (!this.selectedFound) this.selectedNode = null; this.completed = true; return str; }; // Creates the tree structure Tree.prototype.addNode = function(pNode) { var str = ''; var n=0; var cn; if (this.config.inOrder) n = pNode._ai; for (n; n<this.aNodes.length; n++) { if (this.aNodes[n].pid == pNode.id) { cn = this.aNodes[n]; cn._p = pNode; cn._ai = n; this.setCS(cn); if (!cn.target && this.config.target) cn.target = this.con +fig.target; if (cn._hc && !cn._io && this.config.useCookies) cn._io = +this.isOpen(cn.id); if (!this.config.folderLinks && cn._hc) cn.url = null; if (this.config.useSelection && cn.id == this.selectedNode + && !this.selectedFound) { cn._is = true; this.selectedNode = n; this.selectedFound = true; } str += this.node(cn, n); if (cn._ls) break; } } return str; }; // Creates the node icon, url and text Tree.prototype.node = function(node, nodeId) { var str = '<div class="treeNode">' + this.indent(node, nodeId); if (this.config.useIcons) { if (!node.icon) node.icon = (this.root.id == node.pid) ? this. +icon.root : ((node._hc) ? this.icon.folder : this.icon.node); if (!node.iconOpen) node.iconOpen = (node._hc) ? this.icon.fol +derOpen : this.icon.node; if (this.root.id == node.pid) { node.icon = this.icon.root; node.iconOpen = this.icon.root; } else // Ronnie add this line, we don't like the root node to d +isplay str += '<img id="i' + this.obj + nodeId + '" src="' + ((no +de._io) ? node.iconOpen : node.icon) + '" alt="" />'; } if (node.url) { str += '<a id="s' + this.obj + nodeId + '" class="' + ((this.c +onfig.useSelection) ? ((node._is ? 'nodeSel' : 'node')) : 'node') + ' +" href="' + node.url + '"'; if (node.title) str += ' title="' + node.title + '"'; if (node.target) str += ' target="' + node.target + '"'; if (this.config.useStatusText) str += ' onmouseover="window.st +atus=\'' + node.name + '\';return true;" onmouseout="window.status=\' +\';return true;" '; if (this.config.useSelection && ((node._hc && this.config.fold +erLinks) || !node._hc)) str += ' onclick="javascript: ' + this.obj + '.s(' + nodeI +d + ');"'; str += '>'; } else if ((!this.config.folderLinks || !node.url) && node._hc && no +de.pid != this.root.id) str += '<a href="javascript: ' + this.obj + '.o(' + nodeId + ' +);" class="node">'; str += node.name; if (node.url || ((!this.config.folderLinks || !node.url) && node._ +hc)) str += '</a>'; str += '</div>'; if (node._hc) { str += '<div id="d' + this.obj + nodeId + '" class="clip" styl +e="display:' + ((this.root.id == node.pid || node._io) ? 'block' : 'n +one') + ';">'; str += this.addNode(node); str += '</div>'; } this.aIndent.pop(); return str; }; // Adds the empty and line icons Tree.prototype.indent = function(node, nodeId) { var str = ''; if (this.root.id != node.pid) { for (var n=0; n<this.aIndent.length; n++) str += '<img src="' + ( (this.aIndent[n] == 1 && this.conf +ig.useLines) ? this.icon.line : this.icon.empty ) + '" alt="" />'; (node._ls) ? this.aIndent.push(0) : this.aIndent.push(1); if (node._hc) { str += '<a href="javascript: ' + this.obj + '.o(' + nodeId + + ');"><img id="j' + this.obj + nodeId + '" src="'; if (!this.config.useLines) str += (node._io) ? this.icon.n +lMinus : this.icon.nlPlus; else str += ( (node._io) ? ((node._ls && this.config.useLi +nes) ? this.icon.minusBottom : this.icon.minus) : ((node._ls && this. +config.useLines) ? this.icon.plusBottom : this.icon.plus ) ); str += '" alt="" /></a>'; } else str += '<img src="' + ( (this.config.useLines) ? ((node +._ls) ? this.icon.joinBottom : this.icon.join ) : this.icon.empty) + +'" alt="" />'; } return str; }; // Checks if a node has any children and if it is the last sibling Tree.prototype.setCS = function(node) { var lastId; for (var n=0; n<this.aNodes.length; n++) { if (this.aNodes[n].pid == node.id) node._hc = true; if (this.aNodes[n].pid == node.pid) lastId = this.aNodes[n].id +; } if (lastId==node.id) node._ls = true; }; // Returns the selected node Tree.prototype.getSelected = function() { var sn = this.getCookie('cs' + this.obj); return (sn) ? sn : null; }; // Highlights the selected node Tree.prototype.s = function(id) { if (!this.config.useSelection) return; var cn = this.aNodes[id]; if (cn._hc && !this.config.folderLinks) return; if (this.selectedNode != id) { if (this.selectedNode || this.selectedNode==0) { eOld = document.getElementById("s" + this.obj + this.selec +tedNode); eOld.className = "node"; } eNew = document.getElementById("s" + this.obj + id); eNew.className = "nodeSel"; this.selectedNode = id; if (this.config.useCookies) this.setCookie('cs' + this.obj, cn +.id); } }; // Toggle Open or close Tree.prototype.o = function(id) { var cn = this.aNodes[id]; this.nodeStatus(!cn._io, id, cn._ls); cn._io = !cn._io; if (this.config.closeSameLevel) this.closeLevel(cn); if (this.config.useCookies) this.updateCookie(); }; // Open or close all nodes Tree.prototype.oAll = function(status) { for (var n=0; n<this.aNodes.length; n++) { if (this.aNodes[n]._hc && this.aNodes[n].pid != this.root.id) +{ this.nodeStatus(status, n, this.aNodes[n]._ls) this.aNodes[n]._io = status; } } if (this.config.useCookies) this.updateCookie(); }; // Opens the tree to a specific node Tree.prototype.openTo = function(nId, bSelect, bFirst) { if (!bFirst) { for (var n=0; n<this.aNodes.length; n++) { if (this.aNodes[n].id == nId) { nId=n; break; } } } var cn=this.aNodes[nId]; if (cn.pid==this.root.id || !cn._p) return; cn._io = true; cn._is = bSelect; if (this.completed && cn._hc) this.nodeStatus(true, cn._ai, cn._ls +); if (this.completed && bSelect) this.s(cn._ai); else if (bSelect) this._sn=cn._ai; this.openTo(cn._p._ai, false, true); }; // Closes all nodes on the same level as certain node Tree.prototype.closeLevel = function(node) { for (var n=0; n<this.aNodes.length; n++) { if (this.aNodes[n].pid == node.pid && this.aNodes[n].id != nod +e.id && this.aNodes[n]._hc) { this.nodeStatus(false, n, this.aNodes[n]._ls); this.aNodes[n]._io = false; this.closeAllChildren(this.aNodes[n]); } } } // Closes all children of a node Tree.prototype.closeAllChildren = function(node) { for (var n=0; n<this.aNodes.length; n++) { if (this.aNodes[n].pid == node.id && this.aNodes[n]._hc) { if (this.aNodes[n]._io) this.nodeStatus(false, n, this.aNo +des[n]._ls); this.aNodes[n]._io = false; this.closeAllChildren(this.aNodes[n]); } } } // Change the status of a node(open or closed) Tree.prototype.nodeStatus = function(status, id, bottom) { eDiv = document.getElementById('d' + this.obj + id); eJoin = document.getElementById('j' + this.obj + id); if (this.config.useIcons) { eIcon = document.getElementById('i' + this.obj + id); eIcon.src = (status) ? this.aNodes[id].iconOpen : this.aNodes[ +id].icon; } eJoin.src = (this.config.useLines)? ((status)?((bottom)?this.icon.minusBottom:this.icon.minus):((botto +m)?this.icon.plusBottom:this.icon.plus)): ((status)?this.icon.nlMinus:this.icon.nlPlus); eDiv.style.display = (status) ? 'block': 'none'; }; // [Cookie] Clears a cookie Tree.prototype.clearCookie = function() { var now = new Date(); var yesterday = new Date(now.getTime() - 1000 * 60 * 60 * 24); this.setCookie('co'+this.obj, 'cookieValue', yesterday); this.setCookie('cs'+this.obj, 'cookieValue', yesterday); }; // [Cookie] Sets value in a cookie Tree.prototype.setCookie = function(cookieName, cookieValue, expires, +path, domain, secure) { document.cookie = escape(cookieName) + '=' + escape(cookieValue) + (expires ? '; expires=' + expires.toGMTString() : '') + (path ? '; path=' + path : '') + (domain ? '; domain=' + domain : '') + (secure ? '; secure' : ''); }; // [Cookie] Gets a value from a cookie Tree.prototype.getCookie = function(cookieName) { var cookieValue = ''; var posName = document.cookie.indexOf(escape(cookieName) + '='); if (posName != -1) { var posValue = posName + (escape(cookieName) + '=').length; var endPos = document.cookie.indexOf(';', posValue); if (endPos != -1) cookieValue = unescape(document.cookie.subst +ring(posValue, endPos)); else cookieValue = unescape(document.cookie.substring(posValue +)); } return (cookieValue); }; // [Cookie] Returns ids of open nodes as a string Tree.prototype.updateCookie = function() { var str = ''; for (var n=0; n<this.aNodes.length; n++) { if (this.aNodes[n]._io && this.aNodes[n].pid != this.root.id) +{ if (str) str += '.'; str += this.aNodes[n].id; } } this.setCookie('co' + this.obj, str); }; // [Cookie] Checks if a node id is in a cookie Tree.prototype.isOpen = function(id) { var aOpen = this.getCookie('co' + this.obj).split('.'); for (var n=0; n<aOpen.length; n++) if (aOpen[n] == id) return true; return false; }; // If Push and pop is not implemented by the browser if (!Array.prototype.push) { Array.prototype.push = function array_push() { for(var i=0;i<arguments.length;i++) this[this.length]=arguments[i]; return this.length; } }; if (!Array.prototype.pop) { Array.prototype.pop = function array_pop() { lastElement = this[this.length-1]; this.length = Math.max(this.length-1,0); return lastElement; } };
containersTreeForMeteringData.jsp
<%@ page contentType="text/html; charset=UTF-8" %> <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %> <html:html> <head> <title></title> <link rel="stylesheet" type="text/css" href="<%=request.getContext +Path()%>/css/tree.css"> <script type="text/javascript" src="<%=request.getContextPath()%>/ +script/tree.js"></script> <script language="javascript"> function changeContainerForMeteringData(id) { meteringDataContainersTree.openTo(id, false); } function changeMeterProxy(containerId, containerName, proxyMet +erId, proxyMeterName, adasId) { top.RightFrame.contentFrame.document.meteringDataQueryCond +itionForm.containerId.value = containerId; top.RightFrame.contentFrame.document.meteringDataQueryCond +itionForm.containerName.value = containerName; top.RightFrame.contentFrame.document.meteringDataQueryCond +itionForm.proxyMeterId.value = proxyMeterId; top.RightFrame.contentFrame.document.meteringDataQueryCond +itionForm.proxyMeterName.value = proxyMeterName; top.RightFrame.contentFrame.document.meteringDataQueryCond +itionForm.adasId.value = adasId; top.RightFrame.contentFrame.document.meteringDataQueryCond +itionForm.dataSourceType.value = "MP"; top.RightFrame.contentFrame.viewMeteringDataItems("CLICK_T +REE"); } function changeActiveElement(containerId, containerName, activ +eElementId, activeElementName, activeElementType) { top.RightFrame.contentFrame.document.meteringDataQueryCond +itionForm.containerId.value = containerId; top.RightFrame.contentFrame.document.meteringDataQueryCond +itionForm.containerName.value = containerName; top.RightFrame.contentFrame.document.meteringDataQueryCond +itionForm.activeElementId.value = activeElementId; top.RightFrame.contentFrame.document.meteringDataQueryCond +itionForm.activeElementName.value = activeElementName; top.RightFrame.contentFrame.document.meteringDataQueryCond +itionForm.activeElementType.value = activeElementType; top.RightFrame.contentFrame.document.meteringDataQueryCond +itionForm.dataSourceType.value = "AE"; top.RightFrame.contentFrame.viewMeteringDataItems("CLICK_T +REE"); } </script> </head> <body bgcolor="#D3DDE3"> <script type="text/javascript"> <!-- meteringDataContainersTree = new Tree("meteringDataContainersT +ree"); meteringDataContainersTree.icon.root = "<%=request.getContextP +ath()%>/images/tree/empty.gif"; meteringDataContainersTree.icon.folder = "<%=request.getContex +tPath()%>/images/tree/folder.gif"; meteringDataContainersTree.icon.folderOpen = "<%=request.getCo +ntextPath()%>/images/tree/folderopen.gif"; meteringDataContainersTree.icon.node = "<%=request.getContextP +ath()%>/images/tree/page.gif"; meteringDataContainersTree.icon.empty = "<%=request.getContext +Path()%>/images/tree/empty.gif"; meteringDataContainersTree.icon.line = "<%=request.getContextP +ath()%>/images/tree/empty.gif"; meteringDataContainersTree.icon.join = "<%=request.getContextP +ath()%>/images/tree/empty.gif"; meteringDataContainersTree.icon.joinBottom = "<%=request.getCo +ntextPath()%>/images/tree/empty.gif"; meteringDataContainersTree.icon.plus = "<%=request.getContextP +ath()%>/images/tree/nolines_plus.gif"; meteringDataContainersTree.icon.plusBottom = "<%=request.getCo +ntextPath()%>/images/tree/nolines_plus.gif"; meteringDataContainersTree.icon.minus = "<%=request.getContext +Path()%>/images/tree/nolines_minus.gif"; meteringDataContainersTree.icon.minusBottom = "<%=request.getC +ontextPath()%>/images/tree/nolines_minus.gif"; meteringDataContainersTree.icon.nlPlus = "<%=request.getContex +tPath()%>/images/tree/nolines_plus.gif"; meteringDataContainersTree.icon.nlMinus = "<%=request.getConte +xtPath()%>/images/tree/nolines_minus.gif"; meteringDataContainersTree.config.useCookies = false; meteringDataContainersTree.add(0, -1, ""); <logic:iterate name="userContainersListForData" id="container" + type="com.landisgyr.nuwa.conweb.dto.dataPublish.ContainerDTO" scope= +"session"> meteringDataContainersTree.add("<bean:write name='containe +r' property='id'/>", "<bean:write name='containe +r' property='parentId'/>", "<bean:write name='containe +r' property='name'/>", "javascript:changeContainer +ForMeteringData('<bean:write name="container" property="id"/>');", "", "", "<%=request.getContextPath( +)%>/images/common/blueDiamond.gif", "<%=request.getContextPath( +)%>/images/common/blueDiamond.gif"); <bean:define id="proxyMetersList" name="container" propert +y="proxyMeters" /> <logic:notEmpty name="proxyMetersList"> <logic:iterate name="proxyMetersList" id="proxyMeter" +type="com.landisgyr.nuwa.conweb.dto.dataPublish.ProxyMeterDTO"> meteringDataContainersTree.add("<bean:write name=' +proxyMeter' property='id'/>", "<bean:write name +='container' property='id'/>", "<bean:write name +='proxyMeter' property='name'/> <bean:message key='global.leftParenth +esis'/><bean:message key='dataPublish.prompt.proxyMeter'/><bean:messa +ge key='global.rightParenthesis'/>", "javascript:chang +eMeterProxy('<bean:write name="container" property="id"/>', '<bean:wr +ite name="container" property="name"/>', '<bean:write name="proxyMete +r" property="id"/>', '<bean:write name="proxyMeter" property="name"/> +', '<bean:write name="proxyMeter" property="adasId"/>');", "", "", "<%=request.getCo +ntextPath()%>/images/common/redDiamond.gif", "<%=request.getCo +ntextPath()%>/images/common/redDiamond.gif"); </logic:iterate> </logic:notEmpty> <bean:define id="activeElementsList" name="container" prop +erty="activeElements" /> <logic:notEmpty name="activeElementsList"> <logic:iterate name="activeElementsList" id="activeEle +ment" type="com.landisgyr.nuwa.conweb.dto.dataPublish.ActiveElementDT +O"> <logic:equal name="activeElement" property="type" +value="auxiliary_values"> meteringDataContainersTree.add("<bean:write na +me='activeElement' property='id'/>", "<bean:write na +me='container' property='id'/>", "<bean:write na +me='activeElement' property='name'/> <bean:message key='global.leftPa +renthesis'/><bean:message key='dataPublish.prompt.auxiliaryValue'/><b +ean:message key='global.rightParenthesis'/>", "javascript:cha +ngeActiveElement('<bean:write name="container" property="id"/>', '<be +an:write name="container" property="name"/>', '<bean:write name="acti +veElement" property="id"/>', '<bean:write name="activeElement" proper +ty="name"/>', '<bean:write name="activeElement" property="type"/>');" +, "", "", "<%=request.get +ContextPath()%>/images/common/redDiamond.gif", "<%=request.get +ContextPath()%>/images/common/redDiamond.gif"); </logic:equal> <logic:equal name="activeElement" property="type" +value="persistent_values"> meteringDataContainersTree.add("<bean:write na +me='activeElement' property='id'/>", "<bean:write na +me='container' property='id'/>", "<bean:write na +me='activeElement' property='name'/> <bean:message key='global.leftPa +renthesis'/><bean:message key='dataPublish.prompt.persistentValue'/>< +bean:message key='global.rightParenthesis'/>", "javascript:cha +ngeActiveElement('<bean:write name="container" property="id"/>', '<be +an:write name="container" property="name"/>', '<bean:write name="acti +veElement" property="id"/>', '<bean:write name="activeElement" proper +ty="name"/>', '<bean:write name="activeElement" property="type"/>');" +, "", "", "<%=request.get +ContextPath()%>/images/common/redDiamond.gif", "<%=request.get +ContextPath()%>/images/common/redDiamond.gif"); </logic:equal> </logic:iterate> </logic:notEmpty> </logic:iterate> document.write(meteringDataContainersTree); // meteringDataContainersTree.openAll(); //--> </script> </body> </html:html>

When customer click start button, containersTreeForMeteringData.jsp, which is create a tree object and add relative nodes to it, will be invoked. Recently, customers complained that IE often throw a warning box said that 'A script on this page is causing Internet Explore to run slowly..... do you want to abort the script?' when nodes is upto 1000.

After studying and test the code above carefully, I found the choke point is on node function and document.write. So my question is which way to tune is the best and fastest? what I think straightforward(seem stupid?) is gernating the static html in server side other than client browser. I hope monks could lighten my dull brain!

TIA


I am trying to improve my English skills, if you see a mistake please feel free to reply or /msg me a correction


In reply to Web page display slowly!! [OT] by xiaoyafeng

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.