2011-08-20 28 views
6

que tienen una serie de elementos de menú, cada una conteniendo Nombre y URL como esta:Crear un menú UL anidado basado en la estructura ruta URL de elementos de menú

var menuItems = [ 
    { 
     name : "Store", 
     url : "/store" 
    }, 
    { 
     name : "Travel", 
     url : "/store/travel" 
    }, 
    { 
     name : "Gardening", 
     url : "/store/gardening" 
    }, 
    { 
     name : "Healthy Eating", 
     url : "/store/healthy-eating" 
    }, 
    { 
     name : "Cook Books", 
     url : "/store/healthy-eating/cook-books" 
    }, 
    { 
     name : "Single Meal Gifts", 
     url : "/store/healthy-eating/single-meal-gifts" 
    }, 
    { 
     name : "Outdoor Recreation", 
     url : "/store/outdoor-recreation" 
    }, 
    { 
     name : "Hiking", 
     url : "/store/outdoor-recreation/hiking" 
    }, 
    { 
     name : "Snowshoeing", 
     url : "/store/outdoor-recreation/hiking/snowshoeing" 
    }, 
    { 
     name : "Skiing", 
     url : "/store/outdoor-recreation/skiing" 
    }, 
    { 
     name : "Physical Fitness", 
     url : "/store/physical-fitness" 
    }, 
    { 
     name : "Provident Living", 
     url : "/store/provident-living" 
    } 
] 

He estado tratando sin éxito de hacer esto como una lista desordenada con una estructura anidada UL que sigue la estructura ruta URL así:

<ul> 
    <li><a href="/store">Store</a> 
     <ul> 
     <li><a href="/store/travel">Travel</a></li> 
     <li><a href="/store/gardening">Gardening</a></li> 
     <li><a href="/store/healthy-eating">Healthy Eating</a> 
      <ul> 
      <li><a href="/store/healthy-eating/cook-books">Cook Books</a></li> 
      <li><a href="/store/healthy-eating/single-meal-gifts">Single Meal Gifts</a></li> 
      </ul> 
     </li> 
     <li><a href="/store/outdoor-recreation">Outdoor Recreation</a> 
      <ul> 
      <li><a href="/store/outdoor-recreation/hiking">Hiking</a> 
       <ul> 
       <li><a href="/store/outdoor-recreation/hiking/snowshoeing">Snowshoeing</a></li> 
       </ul> 
      </li> 
      <li><a href="/store/outdoor-recreation/skiing">Skiing</a></li> 
      </ul> 
     </li> 
     <li><a href="/store/physical-fitness">Physical Fitness</a></li> 
     <li><a href="/store/provident-living">Provident Living</a></li> 
     </ul> 
    </li> 
</ul> 

Todos los ejemplos que he visto comenzar con una estructura de datos que refleja la relación entre padres e hijos (por ejemplo, XML o JSON), pero estoy teniendo un momento muy difícil sacando esto de la URL y usando para renderizar la nueva estructura.

Si alguien puede por favor dirigirme en la dirección correcta sobre cómo hacer esto con jQuery, realmente lo agradecería. Me doy cuenta de que probablemente necesite usar algunas funciones recursivas o plantillas jQuery, pero estas cosas aún son un poco nuevas para mí.
Gracias

Respuesta

6

Creo que la mejor solución es, en primer lugar para convertir la estructura de datos de un solo árbol, con las relaciones padres/hijos. Renderizar esta estructura será más fácil, ya que la UL tiene una estructura de árbol.

Puede convertir MenuItems usando este par de funciones

// Add an item node in the tree, at the right position 
function addToTree(node, treeNodes) { 

    // Check if the item node should inserted in a subnode 
    for (var i=0; i<treeNodes.length; i++) { 
     var treeNode = treeNodes[i]; 

     // "/store/travel".indexOf('/store/') 
     if (node.url.indexOf(treeNode.url + '/') == 0) { 
      addToTree(node, treeNode.children); 

      // Item node was added, we can quit 
      return; 
     } 
    } 

    // Item node was not added to a subnode, so it's a sibling of these treeNodes 
    treeNodes.push({ 
     name: node.name, 
     url: node.url, 
     children: [] 
    }); 
} 

//Create the item tree starting from menuItems 
function createTree(nodes) { 
    var tree = []; 

    for (var i=0; i<nodes.length; i++) { 
     var node = nodes[i]; 
     addToTree(node, tree); 
    } 

    return tree; 
} 

var menuItemsTree = createTree(menuItems); 
console.log(menuItemsTree); 

El menuItemsTree resultante será un objeto como éste

[ 
    { 
    "name":"Store", 
    "url":"/store", 
    "children":[ 
     { 
     "name":"Travel", 
     "url":"/store/travel", 
     "children":[ 

     ] 
     }, 
     { 
     "name":"Gardening", 
     "url":"/store/gardening", 
     "children":[ 

     ] 
     }, 
     { 
     "name":"Healthy Eating", 
     "url":"/store/healthy-eating", 
     "children":[ 
      { 
      "name":"Cook Books", 
      "url":"/store/healthy-eating/cook-books", 
      "children":[ 

      ] 
      }, 
      { 
      "name":"Single Meal Gifts", 
      "url":"/store/healthy-eating/single-meal-gifts", 
      "children":[ 

      ] 
      } 
     ] 
     }, 
     { 
     "name":"Outdoor Recreation", 
     "url":"/store/outdoor-recreation", 
     "children":[ 
      { 
      "name":"Hiking", 
      "url":"/store/outdoor-recreation/hiking", 
      "children":[ 
       { 
       "name":"Snowshoeing", 
       "url":"/store/outdoor-recreation/hiking/snowshoeing", 
       "children":[ 

       ] 
       } 
      ] 
      }, 
      { 
      "name":"Skiing", 
      "url":"/store/outdoor-recreation/skiing", 
      "children":[ 

      ] 
      } 
     ] 
     }, 
     { 
     "name":"Physical Fitness", 
     "url":"/store/physical-fitness", 
     "children":[ 

     ] 
     }, 
     { 
     "name":"Provident Living", 
     "url":"/store/provident-living", 
     "children":[ 

     ] 
     } 
    ] 
    } 
] 

Usted ha mencionado que ya tiene procesador de HTML para los árboles, ¿verdad? Si necesita ayuda adicional háganoslo saber!

+2

Esto funciona muy bien, pero una 'excepción': Si hay un nodo secundario sin un nodo padre definido que se añadirá como un padre/mainnode. No sé si esto es un efecto secundario deseado, pero sería plausible. – buschtoens

+0

Lo siento pero esto no funciona, probé tu código varias veces. Funciona bien para el primer nodo, pero cuando hay niños en el siguiente hermano inmediato, los hijos del siguiente hermano se agregan a la lista de niños del primer nodo. –

+0

@Davide, ¿Cómo usar los datos HTML formados para renderizar para un árbol? –

0

intente algo como esto.

function Directory(parentNode) { 
    //Structure for directories. Subdirectories container as a generic object, initially empty 
    this.hasSubdirectories = false; 
    this.subdirectories = {}; 

    //Render in steps. Until subdirectories or a link are added, all it needs is an LI and a blank anchor 
    this.nodeLi = document.createElement("li"); 
    parentNode.appendChild(this.nodeLi); 
    this.nodeA = document.createElement("a"); 
    this.nodeLi.appendChild(this.nodeA); 

    //if a subdirectory is added, this.nodeUl will be added at the same time. 
} 

Directory.prototype.setLabel = function (sLabel) { 
    this.nodeA.innerHTML = sLabel; 
} 

Directory.prototype.setLink = function (sLink) { 
    this.nodeA.href = sLink; 
} 

Directory.prototype.getSubdirectory = function (sPath) { 
    //if there were no previous subdirectories, the directory needs a new UL node. 
    if (!this.hasSubdirectories) { 
     this.nodeUl = document.createElement("ul"); 
     this.nodeLi.appendChild(this.nodeUl); 
     this.hasSubdirectories = true; 
    } 

    //split the path string into the base directory and the rest of the path. 
    var r = /^\/?(?:((?:\w|\s|\d)+)\/)(.*)$/; 
    var path = r.exec(sPath); 

    //if the desired path is in a subdirectory, find or create it in the subdirectories container. 

    var subDirName = path[1] || path[2]; 
    var subDir; 
    if (this.subdirectories[subDirName] === undefined) this.subdirectories[subDirName] = new Directory(this.nodeUl); 
    subDir = this.subdirectories[subDirName]; 

    if (path[1] && path[2]) { 
     return subDir.getSubdirectory(path[2]); 
    } else { 
     return subDir; 
    } 
} 

function main(whichNode, aMenuItems) { 
    //whichNode is the node that is to be the parent of the directory listing. 
    //aMenuItems is the array of menu items. 
    var i; 
    var l = aItems.length; 
    var topDir = new Directory(whichNode); 

    //for each menu item, add a directory and set its properties. 
    var dirToAdd; 
    for (i = 0; i < l; i++) { 
     dirToAdd = topDir.getSubdirectory(aMenuItems[i].url); 
     dirToAdd.setLabel(aMenuItems[i].name); 
     dirToAdd.setLink(aMenuItems[i].url); 
    } 

    //and that's it. 
} 

¿cómo es eso?

0

O tal vez completa plugin de jQuery http://jsfiddle.net/9FGRC/

(EDIT)

una actualización a la versión anterior http://jsfiddle.net/9FGRC/1/

Esta versión es compatible siguiente caso

var menuItems = [ 
    { 
     name : "Store", 
     url : "/store" 
    }, 
    { 
     name : "Cook Books", 
     url : "/store/healthy-eating/cook-books" 
    }, 
    { 
     name : "Single Meal Gifts", 
     url : "/store/healthy-eating/single-meal-gifts" 
    } 
] 

Puesto que no se omite

{ 
     name : "Healthy Eating", 
     url : "/store/healthy-eating" 
    }, 

Producirá siguiente html

<ul> 
    <li><a href="/store">Store</a> 
     <ul> 
      <li><a href="/store/healthy-eating/cook-books">Cook Books</a></li> 
      <li><a href="/store/healthy-eating/single-meal-gifts">Single Meal Gifts</a></li> 
     </ul> 
    </li> 
</ul> 

supongo que no será el caso, pero podría ser útil a alguien

2

12 líneas simples de código:

var rootList = $("<ul>").appendTo("body"); 
var elements = {}; 
$.each(menuItems, function() { 
    var parent = elements[this.url.substr(0, this.url.lastIndexOf("/"))]; 
    var list = parent ? parent.next("ul") : rootList; 
    if (!list.length) { 
     list = $("<ul>").insertAfter(parent); 
    } 
    var item = $("<li>").appendTo(list); 
    $("<a>").attr("href", this.url).text(this.name).appendTo(item); 
    elements[this.url] = item; 
}); 

http://jsfiddle.net/gilly3/CJKgp/

2

Aunque me gusta el guión de gilly3, el guión produce una lista con diferentes el Anidación de nidos de <li> y <ul> de lo que se solicitó originalmente. Por lo tanto, en lugar de


    <li><a href="/store">Store</a> 
    <ul> 
     <li><a href="/store/travel">Travel</a></li> 
     ... 
    </ul> 
    </li> 
produce

    <li><a href="/store">Store</a> 
    </li> 
    <ul> 
     <li><a href="/store/travel">Travel</a></li> 
     ... 
    </ul>
Esto puede causar incompatibilidades para las utilidades o los marcos que trabajan con dicho menú generado y producir un menú interactivo con animación (por ejemplo, superfish.js). Así que actualizan el guión 12 líneas

var rootList = $("<ul>").appendTo("body"); 
var elements = {}; 
$.each(menuItems, function() { 
    var parent = elements[this.url.substr(0, this.url.lastIndexOf("/"))]; 
    var list = parent ? parent.children("ul") : rootList; 
    if (!list.length) { 
     list = $("<ul>").appendTo(parent); 
    } 
    var item = $("<li>").appendTo(list); 
    $("<a>").attr("href", this.url).text(this.name).appendTo(item); 
    elements[this.url] = item; 
}); 

http://jsfiddle.net/tomaton/NaU4E/

Cuestiones relacionadas