Page 1 of 3 123 LastLast
Results 1 to 10 of 27

Thread: Looping (recusing) through a HTML List

  1. #1
    Join Date
    Feb 2007
    Location
    England
    Posts
    254
    Thanks
    0
    Thanked 5 Times in 5 Posts

    Default Looping (recusing) through a HTML List

    What is the best (Or at least a better) way to recurse through a HTML OL UL Element.

    So far I have this, but it doesn't work and I don't know why.
    Code:
    function navNode(obj) {
    	var results = [];
    	var x=0;
    	while(obj.childNodes[x]) {
    		if(obj.childNodes[x].tagName == 'li') {
    			results[x] = "li" 
    		}
    		else if(obj.childNodes[x].tagName == 'ul' || obj.childNodes[x].tagName == 'ol') {
    			results[x] = navNode(obj.childNodes[x])
    		}
    		x++
    	}
    	return results;
    }
    Any help would be appreciated.

  2. #2
    Join Date
    May 2007
    Location
    USA
    Posts
    373
    Thanks
    2
    Thanked 4 Times in 4 Posts

    Default

    EDIT: Added brace

    Here's some code that fetches all the UL, OL, LI descendents of an element in no particular order. I'll batch up one that will go through the descendents in branching order in a couple minutes.
    Code:
    function navNode(obj) {
    	var results = [];
    	var ol = obj.getElementsByTagName("ol");
    	for(var i=0; i<ol.length; i++) {
    		results[results.length] = ol[i];
    		}
    	var ul = obj.getElementsByTagName("ul");
    	for(var i=0; i<ul.length; i++) {
    		results[results.length] = ul[i];
    		}
    	var li = obj.getElementsByTagName("li");
    	for(var i=0; i<li.length; i++) {
    		results[results.length] = li[i];
    		}
    	return results;
    	}
    Last edited by Trinithis; 06-01-2007 at 05:00 PM.

  3. #3
    Join Date
    Mar 2005
    Location
    SE PA USA
    Posts
    30,495
    Thanks
    82
    Thanked 3,449 Times in 3,410 Posts
    Blog Entries
    12

    Default

    Disclaimer: This is not offered as a solution, just as an explanation of some of the factors involved. Trinithis' idea of getting the elements by tagName is a better approach overall. But, Trinithis forgot the opening brace on the function.

    The original approach might work out if an additional test is added to avoid text nodes:

    Code:
    if(obj.childNodes[x].tagName && obj.childNodes[x].tagName == 'li')
    This would also need to be done with the other test. And, the incrementing length of the results array would need to be broken out from the incrementing childNodes length. Oh, and when testing tagName, you must allow for variations in case:

    Code:
    function navNode(obj) {
    	var results = [];
    	var x=0;
    	while(obj.childNodes[x]) {
    		if(obj.childNodes[x].tagName && obj.childNodes[x].tagName.toLowerCase() == 'li') {
    			results[results.length] = "li" 
    		}
    		else if(obj.childNodes[x].tagName && obj.childNodes[x].tagName.toLowerCase() == 'ul' || obj.childNodes[x].tagName.toLowerCase() == 'ol') {
    			results[results.length] = navNode(obj.childNodes[x])
    		}
    		x++
    	}
    	return results;
    }
    But, this still doesn't take into account that it appears (can't be 100&#37; sure without seeing the markup), at least as though some of the elements you are looking for are the original element's children's children and you haven't set it up to look for/deal with those until after they are found, which they never would be.
    - John
    ________________________

    Show Additional Thanks: International Rescue Committee - Donate or: The Ocean Conservancy - Donate or: PayPal - Donate

  4. #4
    Join Date
    May 2007
    Location
    USA
    Posts
    373
    Thanks
    2
    Thanked 4 Times in 4 Posts

    Default

    jscheuer1: node.tagName always returns uppercase tags

    I finished making my improved navNode. It walks down the node in a sequential fashion.
    Code:
    function navNode(obj) {
    	var results = [];
    	for(var i=0; i<obj.childNodes.length; i++) {
    		if(obj.childNodes[i].tagName=="OL" || obj.childNodes[i].tagName=="UL" || obj.childNodes[i].tagName=="LI") {
    			results[results.length] = obj.childNodes[i];
    			var temp = navNode(obj.childNodes[i]);
    			if(temp.length) results = results.concat(temp);
    			}
    		}
    	return results;
    	}
    For something like:
    Code:
    <ol id="list1">
    	<li>
    		<ol>
    			<li>
    			0
    			</li>
    			<ul>
    				<li>
    				1
    				</li>
    				<li>
    				2
    				</li>
    				<li>
    				3
    				</li>
    			</ul>
    		</ol>
    	</li>
    	<li>
    	4
    	</li>
    </ol>
    my new navNode returns: [object HTMLLIElement],[object HTMLOListElement],[object HTMLLIElement],[object HTMLUListElement],[object HTMLLIElement],[object HTMLLIElement],[object HTMLLIElement],[object HTMLLIElement]

    vs.

    my old one returning: [object HTMLOListElement],[object HTMLUListElement],[object HTMLLIElement],[object HTMLLIElement],[object HTMLLIElement],[object HTMLLIElement],[object HTMLLIElement],[object HTMLLIElement]
    Last edited by Trinithis; 06-01-2007 at 05:09 PM.

  5. #5
    Join Date
    Mar 2005
    Location
    SE PA USA
    Posts
    30,495
    Thanks
    82
    Thanked 3,449 Times in 3,410 Posts
    Blog Entries
    12

    Default

    Nice looking function, Trinithis.

    Can you be 100&#37; sure of the case in all browsers? I can't. I don't have enough browsers around to know for sure. It is a simple matter to convert to one case or the other for testing purposes. And, I still wonder, what about text nodes? When looping through children, these can be pesky in Mozilla based browsers, possibly others.
    - John
    ________________________

    Show Additional Thanks: International Rescue Committee - Donate or: The Ocean Conservancy - Donate or: PayPal - Donate

  6. #6
    Join Date
    May 2007
    Location
    USA
    Posts
    373
    Thanks
    2
    Thanked 4 Times in 4 Posts

    Default

    EDIT: jscheuer1, theroetically tagName returns uppercase tag names. I read this in a book called "JavaScript Bible", and it says tagName is supported in Mac/WinIE4+, NN6+, Moz1+, Safari1+.

    It seems as if text nodes have a tagName of undefined in both my IE6 and FF1, causing no issue there. And yeah, I suppose my function won't work if the browser does not support DOM nodes or whatever.
    Last edited by Trinithis; 06-01-2007 at 10:48 PM.

  7. #7
    Join Date
    Feb 2007
    Location
    England
    Posts
    254
    Thanks
    0
    Thanked 5 Times in 5 Posts

    Default

    Hey, thank you both.

    Text nodes are definitely undefined and Mozilla says .tagName is lowercase for XHTML (Or any XML based language) and uppercase for HTML. Wierd.

    http://developer.mozilla.org/en/docs...lement.tagName

  8. #8
    Join Date
    Feb 2007
    Location
    England
    Posts
    254
    Thanks
    0
    Thanked 5 Times in 5 Posts

    Default

    I'm having problems with IE (Suprise! )
    It doesn't seem to recognise UL and OL within other OL ULs?


    So far I have...

    Code:
    function navNode(obj) {
    	var results = [];
    	for(var i=0; i<obj.childNodes.length; i++) {
    		var tgNm = obj.childNodes[i].tagName? obj.childNodes[i].tagName.toUpperCase(): undefined;
    		alert(tgNm);
    		if(tgNm=="OL" || tgNm=="UL") {
    			results[results.length] = obj.childNodes[i];
    			var temp = this.navNode(obj.childNodes[i]);
    			if(temp.length) results = results;
    		}
    		else if(tgNm=="LI") {
    			results[results.length] = obj.childNodes[i];
    		}
    	}
    	return results;
    }
    Acting on...
    Code:
    <ul id="makeRadial">
    	<li><a href="http://www.robertgarford.com" target="_self"></a><img src="crystalclear/x40/house.png" title="Home" rollover="crystalclear/x40/house.png"></a></li>
    	<li><a href="http://www.robertgarford.com" target="_self"></a><img src="crystalclear/x40/house.png" title="Home2" rollover="crystalclear/x40/house.png"></a></li>
    	<li><a href="http://www.robertgarford.com" target="_self"></a><img src="crystalclear/x40/house.png" title="Home" rollover="crystalclear/x40/house.png"></a></li>
    	<li><a href="http://www.robertgarford.com" target="_self"></a><img src="crystalclear/x40/house.png" title="Home2" rollover="crystalclear/x40/house.png"></a></li>
    	<ul>
    		<li><a href="http://www.robertgarford.com" target="_self"></a><img src="crystalclear/x40/house.png" title="Home3" rollover="crystalclear/x40/house.png"></a></li>
    		<li><a href="http://www.robertgarford.com" target="_self"></a><img src="crystalclear/x40/house.png" title="Home4" rollover="crystalclear/x40/house.png"></a></li>
    		<li><a href="http://www.robertgarford.com" target="_self"></a><img src="crystalclear/x40/house.png" title="Home3" rollover="crystalclear/x40/house.png"></a></li>
    		<li><a href="http://www.robertgarford.com" target="_self"></a><img src="crystalclear/x40/house.png" title="Home4" rollover="crystalclear/x40/house.png"></a></li>
    		<ol>
    			<li><a href="http://www.robertgarford.com" target="_self"></a><img src="crystalclear/x40/house.png" title="Home3" rollover="crystalclear/x40/house.png"></a></li>
    			<li><a href="http://www.robertgarford.com" target="_self"></a><img src="crystalclear/x40/house.png" title="Home4" rollover="crystalclear/x40/house.png"></a></li>
    			<li><a href="http://www.robertgarford.com" target="_self"></a><img src="crystalclear/x40/house.png" title="Home3" rollover="crystalclear/x40/house.png"></a></li>
    			<li><a href="http://www.robertgarford.com" target="_self"></a><img src="crystalclear/x40/house.png" title="Home4" rollover="crystalclear/x40/house.png"></a></li>
    		</ol>
    	</ul>
    	<ul>
    		<li><a href="http://www.robertgarford.com" target="_self"></a><img src="crystalclear/x40/house.png" title="Home3" rollover="crystalclear/x40/house.png"></a></li>
    		<li><a href="http://www.robertgarford.com" target="_self"></a><img src="crystalclear/x40/house.png" title="Home4" rollover="crystalclear/x40/house.png"></a></li>
    		<li><a href="http://www.robertgarford.com" target="_self"></a><img src="crystalclear/x40/house.png" title="Home3" rollover="crystalclear/x40/house.png"></a></li>
    	</ul>
    	<li><a href="http://www.robertgarford.com" target="_self"></a><img src="crystalclear/x40/house.png" title="Home3" rollover="crystalclear/x40/house.png"></a></li>
    	<li><a href="http://www.robertgarford.com" target="_self"></a><img src="crystalclear/x40/house.png" title="Home4" rollover="crystalclear/x40/house.png"></a></li>
    	<li><a href="http://www.robertgarford.com" target="_self"></a><img src="crystalclear/x40/house.png" title="Home3" rollover="crystalclear/x40/house.png"></a></li>
    	<li><a href="http://www.robertgarford.com" target="_self"></a><img src="crystalclear/x40/house.png" title="Home4" rollover="crystalclear/x40/house.png"></a></li>
    </ul>
    IE only sees the top level LI items of the first UL.

  9. #9
    Join Date
    May 2007
    Location
    USA
    Posts
    373
    Thanks
    2
    Thanked 4 Times in 4 Posts

    Default

    With your code, FF returns an array of length 10, IE returns length 8.

    With my code, both return length 22.

    Code:
    var tgNm = obj.childNodes[i].tagName? obj.childNodes[i].tagName.toUpperCase(): undefined;
    //No point in making tgNm undefined, just make it an empty string.
    var tgNm = obj.childNodes[i].tagName? obj.childNodes[i].tagName.toUpperCase(): "";
    Looking at your original first-post code, it looks like you only want the LI elements. If that is the case, just use
    Code:
    navNode(obj){return obj.getElementsByTagName("li");}
    If you want to filter out some of the tags, try
    Code:
    function navNode(obj) {
    	var results = [];
    	for(var i=0; i<obj.childNodes.length; i++) {
    		var tgNm = obj.childNodes[i].tagName?obj.childNodes[i].tagName.toUpperCase():"";
    		if(tgNm=="OL" || tgNm=="UL" || tgNm=="LI") {
    			if(tgNm!="OL") results[results.length] = obj.childNodes[i]; //Or something like that if you don't want OL for instance.
    			results = results.concat(navNode(obj.childNodes[i])); //I took out the temp because errors don't seem to arise when concatenating an empty array to another.
    			}
    		}
    	return results;
    	}
    If you need more help, I guess you can clarify what exactly you want this function to return and/or do.
    Last edited by Trinithis; 06-01-2007 at 08:54 PM. Reason: beautification

  10. #10
    Join Date
    Jun 2005
    Location
    英国
    Posts
    11,876
    Thanks
    1
    Thanked 180 Times in 172 Posts
    Blog Entries
    2

    Default

    Text nodes are definitely undefined and Mozilla says .tagName is lowercase for XHTML (Or any XML based language) and uppercase for HTML. Wierd.
    Not weird at all. SGML (and thus HTML) is case-insensitive, and traditionally .tagName has been upper-case. XML, however, is all lower-case, so .tagName (or .nodeName) must be lower-case.

    As Trinithis says, all that's necessary is:
    Code:
    obj.getElementsByTagName("li");
    That will get any <li> elements under obj, even if they're in another tag.
    Twey | I understand English | 日本語が分かります | mi jimpe fi le jbobau | mi esperanton komprenas | je comprends français | entiendo español | tôi ít hiểu tiếng Việt | ich verstehe ein bisschen Deutsch | beware XHTML | common coding mistakes | tutorials | various stuff | argh PHP!

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •