مشكلة في إنشاء عقد DOM المتداخلة في JavaScript
-
25-09-2019 - |
سؤال
لدي وظيفة تأخذ كائنًا كمعلمة ، وتستخدم بنية الكائن لإنشاء عقد 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
إلخ.
أي HEL سيكون موضع تقدير كبير ، على الرغم من من فضلك: أنا متأكد إطار أو مكتبة يمكن أن أفعل هذا من أجلي في بضعة أسطر من التعليمات البرمجية ، أو شيء مشابه ، ولكن أفضل عدم استخدام واحدة لهذا المشروع بالذات.
إذا تمكنت من شرح إجاباتك بإيجاز أيضًا ، فستساعدني حقًا في تعلم كيفية عمل كل هذا ، وأين أخطأت!
شكرًا لك!
NB: لقد قمت بتغيير الخط في وظيفتي التي تتحدث عنها رسالة الخطأ.
لقد غيرته من:
mp.appendChild(obj[i].children);
ل:
mp.appendChild(create(obj[i].children));
هذا لأنني أريد إنشاء أي مفاتيح متداخلة في كائن الأطفال ، لذلك screen_name
لو كان مفتاح الأطفال ، سيتم إنشاءهم أيضًا. آسف, ، أتمنى أنك تستطيع فهم هذا!
أنا أنظر إلى http://jsperf.com/create-sned-domructure بالنسبة لبعض المؤشرات ، قد يساعدك هذا أيضًا!
المحلول
يجب كتابة وظيفة "إنشاء" الخاص بك بشكل متكرر.
لإنشاء عقدة من بياناتك (بشكل عام) ، تحتاج إلى:
- ابحث عن خاصية "العلامة" وإنشاء عنصر جديد
- امنح العنصر قيمة "المعرف" للعنصر (مأخوذة من البيانات)
- لكل عنصر في "الأطفال" ، اصنع عقدة وإلحاقها
هكذا:
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);
و "CreatedNodes" ستكون مجموعة من عنصر واحد ، <article>
علامة مع معرف "التغريدات". هذا العنصر سيكون له طفلان ، <h2>
علامة مع معرف "screen_name" و <p>
علامة مع معرف "النص". (الآن بعد أن فكرت في الأمر ، قد ترغب في تخطي مهمة "المعرف" ما لم يكن وصف العقدة "معرف" صريح ، أو شيء ما.)
وهكذا إذا كان لديك ملف <div>
في صفحتك تسمى "Tweets" (لاستخدام مثالك ، على الرغم من أنك إذا كنت ترغب بالتأكيد في قطع جزء إعداد "ID" من وظيفتي) ، فستضيف نتائج مثل هذه:
var createdNodes = create(nodes), tweets = document.getElementById('tweets');
for (var eindex = 0; eindex < createdNodes.length; ++eindex)
tweets.appendChild(createdNodes[eindex]);
نصائح أخرى
أضفت قائمة ملحق وظيفة تقبل قائمة العناصر ، والحاوية التي يجب إلحاقها. لقد أزلت إلحاق "التغريدات" جزءًا من وظيفة إنشاء لفصل الكود بشكل أكثر فعالية.
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"));
البناء على الإجابة السابقة: أعتقد أنك لا تزال بحاجة إلى إنشاء العنصر الذي تحاول إلحاقه:
tmp.appendchild (الأطفال [prop] .tag) ؛
يجب ان يكون
tmp.appendchild (document.createElement (الأطفال [prop] .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);
};
};