2014-01-10 2 views
1

Я пытаюсь создать объект JSON из элемента UL в Javascript.Преобразование неупорядоченного списка в JSON

У меня есть этот список структуру:

<ul id="trainingmenu"> 
    <li class="tmnode"><span id="M5lx9n" class="tmname">menu 1</span> 
     <ul> 
      <li class="tmnode"><span id="M66p48" class="tmname">menu 2</span> 
       <ul> 
        <li class="tmnode"><span class="tmname" id="Mschr">menu 3</span> 
        </li> 
       </ul> 
      </li> 
      <li class="tmnode"><span id="Mu03e0" class="tmname">menu 4</span></li> 
     </ul> 
    </li> 
</ul> 

Это JS:

var treeObject = {}; 

    function treeHTML(element, object) { 
     if (element.tagName=="UL") { element=element.firstChild; } 
     object["id"] = element.firstChild.id; 
     object["name"] = element.firstChild.innerHTML; 
     var nodeList = element.getElementsByTagName('LI'); 
     if (nodeList[0]) { 
      object["nodes"] = []; 
      for (var i = 0; i < nodeList.length; i++) { 
       object["nodes"].push({}); 
       treeHTML(nodeList[i], object["nodes"][object["nodes"].length - 1]); 
      } 
     } 
    } 
    treeHTML(document.getElementById("trainingmenu"), treeObject); 
    alert(JSON.stringify(treeObject)); 

..и это выход я получаю:

{"id":"M5lx9n","name":"menu 1","nodes":[{"id":"M66p48","name":"menu 2","nodes":[{"id":"Mschr","name":"menu 3"}]},{"id":"Mschr","name":"menu 3"},{"id":"Mu03e0","name":"menu 4"}]} 

Это часть и это формат, который я хочу - выбирая поля id и name из childNode каждого элемента LI. Но это повторяющиеся узлы - меню 3 появляется дважды

Вот fiddle

Я, наверное, облажался рекурсия, но уставившись на него в течение 2 дней не работали, как. Может ли кто-нибудь помочь?

+1

FWIW, я немного удивлен, что код работает на всех. Если код HTML действительно написан так, то 'element.firstChild' на внешней' ul' _should_ возвращает текстовый узел пробела между 'ul' и' li'. В новых браузерах вы должны использовать '.firstElementChild', чтобы игнорировать текстовые узлы. – Alnitak

+0

True - это просто для удобства чтения; Я снял перерывы в скрипке. Однако я не знал о .firstElementChild (!), Поэтому я кое-что узнал. Спасибо за это. – WindsorAndy

+0

@WindsorAndy не забывайте, что 'firstElementChild' является умеренно недавним дополнением к спецификации DOM - он может не существовать в старых браузерах. – Alnitak

ответ

1

Как упоминает Alnitak, вы должны использовать .children или .childNodes, а не метод .getElementsByTagName(). Следующий фрагмент использует метод прототипа Array .filter() для фильтрации детей ul и .forEach() для итерации.

function treeHTML(element) { 
    var o = {}; 
    o.id = element.firstElementChild.id; 
    o.name = element.firstElementChild.innerHTML; 
    o.nodes = []; 
    [].slice.call(element.children).filter(function(e) { 
     return e.tagName.toLowerCase() === 'ul'; 
    }).forEach(function(ul) { 
     [].slice.call(ul.children).forEach(function(li) { 
      o.nodes.push(treeHTML(li)); 
     }); 
    }); 
    return o;  
} 
var treeObj = treeHTML(document.getElementById("trainingmenu") 
           .firstElementChild); 

Вот альтернативное решение, которое должно работать в старой версии IE:

function children(node, selector) { 
    var res = [], nodes = node.childNodes, l = nodes.length; 
    for (var i = 0; i < l; i++) { 
     if (nodes[i].nodeType === 1) { 
      if (selector && nodes[i].tagName.toLowerCase() === selector) { 
       res.push(nodes[i]); 
      } else if (!selector) { 
       res.push(nodes[i]); 
      } 
     } 
    } 
    return res; 
} 

function treeHTML(element) { 
    var o = {}; 
    var span = children(element, 'span'); 
    o.id = span[0].id; 
    o.name = span[0].innerHTML; 
    o.nodes = []; 
    var ul = children(element, 'ul'), n = ul.length; 
    for (var i = 0; i < n; i++) { 
     var li = children(ul[i]), l = li.length; 
     for (var j = 0; j < l; j++) { 
      o.nodes.push(treeHTML(li[j])); 
     } 
    } 

    return o;  
} 

var root = document.getElementById("trainingmenu"); 
var treeObj = treeHTML(children(root)[0]); 

http://jsfiddle.net/XZ54G/

+0

Очень элегантное решение, BlackSheep. Этот вопрос превращается в мастер-класс JS для меня. – WindsorAndy

+0

@WindsorAndy Спасибо, я рад, что это может помочь. – undefined

+0

Вы можете подгонять функции массива ES5, но вы не можете подделать 'firstElementChild' и' children'. – Alnitak

1

Это повторяющиеся узлы, потому что element.getElementsByTagName находит все потомки, а не только дети. Вы можете протестировать это nodeList[i].parentNode == element.

В качестве альтернативы можно просто перебрать все дочерние узлы элемента (element.childNodes или element.children) и найти их с помощью tagName == 'li'. В хорошо сформированном HTML вы должны находить только текстовые узлы (whitespace) и li элементов в дочернем списке элемента ul.

+0

спасибо за это .. это звучит многообещающе. Тестирование его. – WindsorAndy

+0

... и комментарии узлов. –

+0

@JanDvorak да, это тоже! – Alnitak

Смежные вопросы