A recent project at work gave me an excuse to play with the Yahoo User Interface Library. I thought about implementing it my taffysoft.com pages for my digest page, but after putting it together and playing with it, I eventually went another way. The code below may be useful to others however.
The page design uses nested HTML lists to form an outline of topics. Then, if the user has JavaScript enabled, the onoload() handler automatically replaces the static outline with a YUI TreeView. My code looks for lists of the form:
<UL id="arbitraryName">
<LI>Item 1 <UL tv=1>
<LI>Item 1.1 <UL tv=1>
<LI>Item 1.1.1</LI>
<LI>Item 1.1.2</LI>
</UL></LI>
<LI>Item 1.2 <UL tv=1>
<LI>Item 1.2.1</LI>
<LI>Item 1.2.2</LI>
</UL></LI>
</UL></LI>
<LI>Item 2 <UL tv=1>
<LI>Item 2.1 <UL tv=1>
<LI>Item 2.1.1</LI>
<LI>Item 2.1.2</LI>
</UL></LI>
<LI>Item 2.2 <UL tv=1>
<LI>Item 2.2.1</LI>
<LI>Item 2.2.2</LI>
</UL></LI>
</UL></LI>
</UL>
The body element onload() handler calls 'makeTreeFromUL(document.getElementById("arbitraryName"));' to replace the contents of the topmost list. Each sublist with an attribute "tv=1" will be recursively processed. (Using an attribute flag allows you to embed HTML lists within the tree as part of YUI HTMLNodes.) The JavaScript code is:
function notWhitespace(s) {
// Return true if a string contains any printable characters.
for (var i = 0; i < s.length; i++) {
if (s.charAt(i) > " ") return true;
}
return false;
}
function itemHasSublists(oItem) {
// Return true if any top-level child is a UL node with a non-zero "tv" attribute.
for (var i = 0; i < oItem.childNodes.length; i++) {
var e = oItem.childNodes[i];
if (e.nodeName.toLowerCase() == "ul" && e.getAttribute("tv")) return true;
}
return false;
}
function makeTreeNodesFromList(oList,oRoot) {
var kids = oList.childNodes;
if (kids.length == 0) return;
for (var i = 0; i < kids.length; i++) {
var e = kids[i];
if (e.nodeName.toLowerCase() != "li") {
// Any non-list-item childen (typically whitespace) are discarded.
continue;
}
if (!itemHasSublists(e)) {
// If a list item has no embedded lists, make the raw html content a leaf node.
new YAHOO.widget.HTMLNode(e.innerHTML,oRoot,false,true);
continue;
}
var sb = []; // string buffer to collect segments of parent node's label
var grandkids = kids[i].childNodes;
var node = null; // new parent node
for (var j = 0; j < grandkids.length; j++) {
e = grandkids[j];
if (e.nodeName.toLowerCase() == "ul" && e.getAttribute("tv")) {
if (sb.length > 0) {
// save leading text as label of new parent node
node = new YAHOO.widget.TextNode(sb.join(""),oRoot,false,true);
sb = []
} else {
node = new YAHOO.widget.TextNode("MENU",oRoot,false,true);
}
makeTreeNodesFromList(e,node); // recursive call
} else if (e.nodeType != 8) { // not a comment
if (e.nodeType != 3) {
// save tags, attributes, and contents
sb[sb.length] = e.outerHTML;
} else if (notWhitespace(e.data)) {
// save raw text (outerHTML does not work here)
sb[sb.length] = e.data;
}
}
}
}
}
function makeTreeFromUL(oList) {
var tree = new YAHOO.widget.TreeView(oList.id);
makeTreeNodesFromList(oList,tree.getRoot());
tree.draw();
}
I know I have seen a simpler version of this on the internet somewhere, but I cannot find it on Google.