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!
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);
});
You can use
$(el).children().length
which would return '0' or a positive number, then loop through if it's a positive number which evaluates to true. You could also use a while loop to do this recursively, and re-set the reference handler however I'm not quite sure that would work out because your nodeNames for each subsequent child differ ( or do they? ) .. What's the most nested example you can provide?
THanks so much Keith, that was the ticket - well almost, I had to make one MINOR change and then it worked perfectly!
My code is below.
$tocOutput="";
$.get(tocURL,function(toc){
function makeToc($xml) {
// worker function local to makeToc
function processXml() {
console.log($(this));
$tocOutput += "<li><a href='" + $(this).attr("url") + "'>" + $(this).attr("title") + "</a>";
if (this.nodeName == "BOOK") {
$tocOutput += "<ul>";
// recurse on book children
$(this).children().each(processXml);
$tocOutput += "</ul>";
}
$tocOutput += "</li>";
}
// call worker function on all children
$xml.children().each(processXml);
}
var tocOutput = makeToc($(toc));
$("#toc").html($tocOutput);
completed($("#toc"));
});
You'll notice all I'm doing is declaring the variable outside the $.get()
and then I use $xml.children().each(processXml);
instead of $(this).find("page").each(processXml);
that you had.
The reason for this is that the children could be pages OR books, but what you had was limiting it to only pages.
Thanks again!
This link provides a good example for use of iterating through xml http://anasthecoder.blogspot.in/2012/02/looping-through-xml-with-jquery.html
xml.find('result').find('permissionDetails').each(function(){
$(this).children().each(function(){
var tagName=this.tagName;
var val=$(this).text();
if(val==1){
$('input:checkbox[name='+tagName+']').attr('checked',true);
}
else if(val==0){
$('input:checkbox[name='+tagName+']').removeAttr('checked');
}
})
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With