Looping through XML with jQuery

jquery, loops, robohelp, xml

Solution

Recursive functions work well for this. When you create a function that creates and uses an internal recursive closure you can wrap it all up in a neat little package:

    $.get(tocURL, function(toc) {
    function makeToc($xml) {
        // variable to accumulate markup
        var markup = "";
        // worker function local to makeToc
        function processXml() {
            markup += "<li><a href='" + $(this).attr("url") + "'>" + $(this).attr("title") + "</a>";
            if (this.nodeName == "BOOK") {
                markup += "<ul>";
                // recurse on book children
                $(this).find("page").each(processXml);
                markup += "</ul>";
            }
            markup += "</li>";
        }
        // call worker function on all children
        $xml.children().each(processXml);
        return markup;
    }
    var tocOutput = makeToc($(toc));
    $("#list").html(tocOutput);
});

Problem

I've got some basic code that can loop through some XML that's generated from Adobe RoboHelp (for our help documentation). This works fine, but since a topic could be nested as many time as the writer wants, i need a better way to loop through this XML rather than just nesting `.each()` loops. Here's what the XML looks like ``` <?xml version="1.0" encoding="utf-8"?> <!--RoboML: Table of Content--> <roboml_toc> <page title="Welcome" url="Welcome.htm"/> <book title="Getting Started" url="Getting_Started/Initial_Setup.htm"> <page title="Initial Setup" url="Getting_Started/Initial_Setup.htm"/> <page title="Customize Settings" url="Getting_Started/Settings.htm"/> </book> <book title="Administrator Services" url="Administrator_Services/General_Administrator.htm"> <book title="Portal Workspace" url="Administrator_Services/Portal_Workspace/AdminHome.htm"> <page title="Home" url="Administrator_Services/Portal_Workspace/AdminHome.htm"/> <page title="Portal Accounts" url="Administrator_Services/Portal_Workspace/Portal_Accounts.htm"/> </book> <book title="SpamLab" url="Administrator_Services/SpamLab/SpamLab_Admin_General.htm"> <page title="Alerts" url="Administrator_Services/SpamLab/Alerts.htm"/> <page title="Spam Quarantine" url="Administrator_Services/SpamLab/Admin_Spam_Quarantine_.htm"/> </book> </book> <book title="User Services" url="User_Services/General_User.htm"> <book title="Portal Workspace" url="User_Services/Portal_Workspace/Home.htm"> <page title="Home" url="User_Services/Portal_Workspace/Home.htm"/> <page title="Self Help" url="User_Services/Portal_Workspace/Self_Help.htm"/> </book> <book title="SpamLab" url="User_Services/SpamLab/SpamLab_General.htm"> <page title="Spam Quarantine" url="User_Services/SpamLab/Spam_Quarantine.htm"/> <page title="Virus Quarantine" url="User_Services/SpamLab/Virus_Quarantine.htm"/> </book> <book title="Encryption" url="User_Services/Encryption/Encryption_General.htm"> <page title="Outlook Plug-in" url="User_Services/Encryption/Encryption_Outlook_Plug_in.htm"/> </book> </book> </roboml_toc> ``` A `<page>` is an article, and a `<book>` is a folder. Her's my jQuery code, which only can look one level deep of tags ``` //Get the TOC $tocOutput=""; $.get(tocURL,function(toc){ $(toc).children().each(function(){ $tocOutput+="<li><a href='"+$(this).attr("url")+"'>"+$(this).attr("title")+"</a>"; if(this.tagName=="BOOK"){ $tocOutput+="<ul>"; $(this).find("page").each(function(){ $tocOutput+="<li><a href='"+$(this).attr("url")+"'>"+$(this).attr("title")+"</a></li>"; }); $tocOutput+="</ul>"; } $tocOutput+="</li>"; }); $("#list").html($tocOutput); ``` I know there's a better way to just loop through all elements and then determine if the element has children, etc. but I just can't think of how to do it. Any help is greatly appreciated!

Original source