Проблемы с созданием вложенных узлов DOM в JavaScript - PullRequest
2 голосов
/ 17 ноября 2010

У меня есть функция, которая принимает объект в качестве параметра и использует структуру объекта для создания вложенных узлов DOM, но я получаю следующую ошибку:

http://new.app/:75NOT_FOUND_ERR: DOM Exception 8: An attempt was made to reference a Node in a context where it does not exist.

То, что я хотел бы, чтобы моя функция выполняла, - это когда в качестве параметра указывается подходящий объект, например:

var nodes = {
    tweet: {
        children: {
            screen_name: {
                tag: "h2"
            },
            text: {
                tag: "p"
            }
        },
        tag: "article"
    }
};

Это создаст следующие DOM-узлы:

<article>
    <h2></h2>
    <p></p>
</article>

Вот моя попытка:

function create(obj) {
    for(i in obj){  
        var tmp = document.createElement(obj[i].tag);  
        if(obj[i].children) {  
            tmp.appendChild(create(obj[i].children)); /* error */
        }; 
        document.getElementById("tweets").appendChild(tmp);  
    };  
};

Я уже борюсь!

В идеале я бы хотел со временем добавить больше дочерних ключей к каждому объекту, не только tag, но также id, innerHTML, class и т. Д.

Любой хел был бы очень признателен, хотя, пожалуйста: я уверен, что фреймворк или библиотека могли бы сделать это для меня всего за несколько строк кода или что-то подобное, но I ' я предпочитаю не использовать один для этого конкретного проекта.

Если бы вы тоже могли кратко объяснить свои ответы, это действительно помогло бы мне узнать, как все это работает, и где я ошибся!

Спасибо!

NB. Я изменил и пометил строку в своей функции, о которой говорится в сообщении об ошибке.

Я изменил его с:

mp.appendChild(obj[i].children);

до:

mp.appendChild(create(obj[i].children));

Это потому, что я хочу, чтобы любые вложенные ключи в дочернем объекте также создавались, поэтому screen_name имел дочерний ключ, они тоже были бы созданы. Извините , надеюсь, вы понимаете это!

Я смотрю на http://jsperf.com/create-nested-dom-structure для некоторых указателей, это может вам тоже помочь!

Ответы [ 3 ]

2 голосов
/ 17 ноября 2010

Ваша функция "create" должна быть написана рекурсивно.

Чтобы создать узел из ваших данных (в целом), вам необходимо:

  1. Найдите свойство "tag" и создайте новый элемент
  2. Дайте элементу значение "id" элемента (взято из данных)
  3. Для каждого элемента в "children" создайте узел и добавьте его

Таким образом:

function create(elementDescription) {
  var nodes = [];
  for (var n in elementDescription) {
    if (!elementDescription.hasOwnProperty(n)) continue;
    var elem = elementDescription[n];
    var node = document.createElement(elem.tag);
    node.id = n; // optional step
    var cnodes = create(elem.children);
    for (var c = 0; c < cnodes.length; ++c)
      node.appendChild(cnodes[c]);
    nodes.push(node);
  }
  return nodes;
}

Возвращает массив элементов документа, созданных из исходного объекта «спецификации». Таким образом, из вашего примера вы бы позвонили:

var createdNodes = create(nodes);

и "creatNodes" будут массивом из одного элемента, тегом <article> с идентификатором "твиты". У этого элемента будет два дочерних элемента: тег <h2> с идентификатором "screen_name" и тег <p> с идентификатором "text". (Теперь, когда я думаю об этом, вы можете пропустить присвоение «id», если в описании узла нет явной записи «id», или что-то в этом роде.)

Таким образом, если у вас есть <div> на вашей странице, называемой «твиты» (для использования вашего примера, хотя, если это так, вы определенно захотите вырезать часть настройки «id» моей функции), вы добавите результаты как это:

var createdNodes = create(nodes), tweets = document.getElementById('tweets');

for (var eindex = 0; eindex < createdNodes.length; ++eindex)
  tweets.appendChild(createdNodes[eindex]);
0 голосов
/ 17 ноября 2010

Опираясь на предыдущий ответ: Я думаю, вам все еще нужно создать элемент, который вы хотите добавить:

tmp.appendChild (дети [проп] .tag);

должно быть

tmp.appendChild (document.createElement (дети [проп] .tag));

function create(obj) {
    for(i in obj){  
        var tmp = document.createElement(obj[i].tag);  
        var children;
        if(children = obj[i].children) {  
            for(var prop in children)
                tmp.appendChild(document.createElement(children[prop].tag));   
        }
        document.getElementById("tweets").appendChild(tmp);  
    };  
};
0 голосов
/ 17 ноября 2010

Я добавил функцию appendList, которая принимает список элементов и контейнер для добавления.Я удалил часть добавления "твитов" из функции создания, чтобы более эффективно отделить ваш код.

function create(obj) {
    var els = [];
    for(i in obj){  
        var tmp = document.createElement(obj[i].tag);  
        var children;
        if(children = obj[i].children) {  
            var childEls = create(children);
            appendList(childEls, tmp);
        }
        els.push(tmp);
    }; 
    return els;
};
function appendList(list, container){
    for(var i = 0, el; el = list[i]; i++){
        container.appendChild(el);
    }
};
// gets an array of root elements populated with children
var els = create(nodes); 
// appends the array to "tweets"
appendList(els, document.getElementById("tweets")); 
...