2009-10-15 48 views
6

Tengo un código básico que puede recorrer cierto XML generado a partir de Adobe RoboHelp (para nuestra documentación de ayuda). Esto funciona bien, pero dado que un tema puede anidarse tantas veces como lo desee el escritor, necesito una forma mejor de recorrer este XML en lugar de simplemente anidar los bucles .each().Looping a través de XML con jQuery

Esto es lo que el XML se parece

<?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> 

Un <page> es un artículo, y una <book> es una carpeta.

Su es mi código de jQuery, que sólo puede buscar un nivel de profundidad de etiquetas

//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); 

Sé que hay una mejor forma de bucle sólo a través de todos los elementos y luego determinar si el elemento tiene hijos, etc., pero me simplemente no puedo pensar en cómo hacerlo.

¡Cualquier ayuda es muy apreciada!

+1

Simplemente curioso: ¿por qué debe hacerse esto en el cliente? ¿Por qué no aplicar una transformación XSLT en el servidor y enviar el html? ¿El xml es dinámico? ¿Podría el html transformado no almacenarse en caché en el servidor? –

+0

En cualquier caso, me alegro de que hayas encontrado una solución - AHORA ACEPTA LA RESPUESTA DE KEITH :) –

+0

¡NEGúSelo! Esto fue solo una prueba de concepto por el momento ... en realidad solo quería impresionar a algunas personas. Me están lloviendo elogios ahora, así que vale la pena. Eventualmente creo que estaremos haciendo este lado del servidor sin embargo. –

Respuesta

9

Las funciones recursivas funcionan bien para esto. Cuando se crea una función que crea y utiliza un cierre recursivo interno puede envolverlo todo en un pequeño paquete:

$.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); 
}); 
1

Puede utilizar

$(el).children().length que volver '0' o un número positivo, a continuación, recorrer si es un número positivo, que se evalúa como verdadera. También podría usar un ciclo while para hacer esto de forma recursiva, y volver a establecer el controlador de referencia, pero no estoy muy seguro de que funcione, ya que los Nodos de cada hijo subsiguiente difieren (¿o no?). ¿Qué es lo más anidado? ejemplo que puede proporcionar?

+0

No, los nombres de los nodos son o . Una página puede existir dentro o fuera de un libro. Un libro puede contener otros libros o páginas. En el XML que publiqué arriba hay una página que tiene 2 padres de libros. –

1

Gracias tanto Keith, que era el billete - bueno, casi, he tenido que hacer una MENOR cambio y luego funcionó a la perfección!

Mi código está por debajo.

$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")); 
}); 

Se dará cuenta de todo lo que estoy haciendo es declarar la variable fuera del $.get() y luego utilizo en lugar de $xml.children().each(processXml);$(this).find("page").each(processXml); que tenías.

La razón de esto es que los niños podrían ser páginas O libros, pero lo que tenía era limitarlo a páginas únicas.

¡Gracias nuevamente!

+2

¡Puede agradecerme aceptando mi respuesta! Incidentemente, mi objetivo no era resolver todo tu problema, solo duplicar la lógica en tu pregunta. Además, puse la declaración de la variable dentro de makeToc() como una mejor práctica general. Hacerlo de esa manera deja en claro que la variable es solo para el uso de makeToc para acumular resultados. – keithm

0

Aquí hay algo para ganar más elogios. Lo hice una llamada de función anónima y usé los argumentos.callee para recurse. Yo mismo estaba viendo para este método, y este otro hilo en StackOverflow me ayudó a salir y quiero devolver el dinero :-)

$.get(tocURL,function(data){ 
    var markup = "<ul>"; 
    $(data).each(function(){ 
     markup += "<li><a href='" + $(this).attr("url") + "'>" + $(this).attr("title") + "</a>"; 
     if (this.nodeName == "BOOK") { 
      $markup += "<ul>"; 
      $(this).children().each(arguments.callee);  
      $markup += "</ul>"; 
     } 
     markup += "</li>"; 
    }); 
    $("#list").html(markup+"</ul>"); 
}); 
1

Este enlace es un buen ejemplo para el uso de la iteración a través de 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'); 
     } 
    }) 

}); 
Cuestiones relacionadas