简体   繁体   English

需要帮助正确迭代 DOM 并替换元素 - Javascript

[英]Need help iterating over DOM properly and replacing elements - Javascript

Thanks in advance for any help!在此先感谢您的帮助! I'm trying to do some recursive span tag "replaceWith" actions, replacing the span with a div and 3 child spans.我正在尝试执行一些递归跨度标记“replaceWith”操作,用一个 div 和 3 个子跨度替换跨度。 I was having a huge problem with my child object(containing the spans to be replaced) being updated somehow when the first span is replaced (causing my object to grow by 2 every time), so I figured I'd try to make the object constant and then freeze or seal it.我的子对象(包含要替换的跨度)在替换第一个跨度时以某种方式更新(导致我的对象每次增长 2)时遇到了一个大问题,所以我想我会尝试制作对象不变,然后冷冻或密封。 I'm not sure if that is the correct way to do this but I'm not a good enough javascript programmer to know.我不确定这是否是正确的方法,但我不是一个足够好的 javascript 程序员知道。 Anyway, I get my spans object this way:无论如何,我以这种方式获取我的跨度对象:

let el = document.getElementById("container");
let nodes = el.children[0];
Object.seal(nodes);//THIS SEALS/FREEZES JUST FINE
let spans = {};//TRIED WITH AND W/O THIS JUST TO MAKE SURE THE OBJECT WAS CREATED
const spans = nodes.children;
console.log('type of spans: '+typeof spans);//RETURNS object
console.log('spans length: '+spans.length);//RETURNS spans length: 3
console.log('spans: '+JSON.stringify(spans));//RETURNS spans: {"0":{},"1":{},"2":{}}
Object.seal(spans);///RETURNS Uncaught TypeError: Cannot Seal

The HTML is simple: HTML很简单:

<body>
    <button id="fractalize">Fractalize</button>
    <br/>
    <br/>
    <div id="container">
        <div class="sierpinski">
            <span></span>
            <span></span>
            <span></span>
        </div>
    </div>
</body>

spans returns as an object and everything but it keeps failing when using Object.freeze or Object.seal! spans 作为一个对象返回,但在使用 Object.freeze 或 Object.seal 时它一直失败! I need to know if someone can tell me what I am doing wrong.. the spans object doesn't look any different to me than the nodes object and the nodes object freezes/seals just fine.我需要知道是否有人可以告诉我我做错了什么.. spans 对象在我看来与 nodes 对象没有任何不同,并且 nodes 对象冻结/密封就好了。 If I can get those objects frozen then my plan is to do the following for the replacements:如果我可以冻结这些对象,那么我的计划是为替换执行以下操作:

for( let key in spans ) {
  if( spans.hasOwnProperty(key) ) {
    console.log(key + " -> " + JSON.stringify(spans[key]));
    let nDiv = document.createElement("div");
    nDiv.className="sierpinski";
    nDiv.innerHTML="<span></span><span></span><span></span>";
    spans[key].replaceWith(nDiv.cloneNode(true));
    nDiv.remove();
  }
}

Thanks for any insight!感谢您的任何见解!

edit For insight, this is what I want;编辑为了洞察力,这就是我想要的;

<body>
    <button id="fractalize">Fractalize</button>
    <br/>
    <br/>
    <div id="container">
        <div class="sierpinski">
            <div class="sierpinski">
                <div class="sierpinski">
                    <span></span>
                    <span></span>
                    <span></span>
                </div>
                <div class="sierpinski">
                    <span></span>
                    <span></span>
                    <span></span>
                </div>
                <div class="sierpinski">
                    <span></span>
                    <span></span>
                    <span></span>
                </div>
            </div>
            <div class="sierpinski">
                <div class="sierpinski">
                    <span></span>
                    <span></span>
                    <span></span>
                </div>
                <div class="sierpinski">
                    <span></span>
                    <span></span>
                    <span></span>
                </div>
                <div class="sierpinski">
                    <span></span>
                    <span></span>
                    <span></span>
                </div>
            </div>
            <div class="sierpinski">
                <div class="sierpinski">
                    <span></span>
                    <span></span>
                    <span></span>
                </div>
                <div class="sierpinski">
                    <span></span>
                    <span></span>
                    <span></span>
                </div>
                <div class="sierpinski">
                    <span></span>
                    <span></span>
                    <span></span>
                </div>
            </div>
        </div>
    </div>
</body>

And this is what I'm currently getting;这就是我目前得到的;

<body>
    <button id="fractalize">Fractalize</button>
    <br/>
    <br/>
    <div id="container">
        <div class="sierpinski">
            <div class="sierpinski">
                <span></span>
                <div class="sierpinski">
                    <span></span>
                    <div class="sierpinski">
                        <span></span>
                        <div class="sierpinski">
                            <span></span>
                            <span></span>
                            <span></span>
                        </div>
                        <span></span>
                    </div>
                    <span></span>
                </div>
                <span></span>
            </div>
            <span></span>
            <span></span>
        </div>
    </div>
</body>

Question

So do I add/remove classes or something so the code iteration has some sort of anchor?那么我是否添加/删除类或其他东西以便代码迭代具有某种锚点? I just don't understand how I can declare a variable equal to some child elements and that not stay permanent.我只是不明白如何声明一个变量等于某些子元素并且不会保持永久。 How does changing the DOM down the line change what's in that variable without redefining the variable??如何在不重新定义变量的情况下更改 DOM 更改该变量中的内容?

Live and "Static" HTMLCollections / NodeLists实时和“静态” HTMLCollections / NodeLists

Using the right method to collect elements/nodes* is important for DOM manipulation.使用正确的方法来收集元素/节点*对于 DOM 操作很重要。 The older methods .children , .getElementsByTagName() , .getElementsByName() , .getElementsByClassName() , etc. return a Live Collection of DOM Objects.较旧的方法.children.getElementsByTagName().getElementsByName().getElementsByClassName()等返回 DOM 对象的实时集合 This means if any object (ie elements, ie <div> , <span> , ie not spans={} ) in this collection (aka HTMLCollection , aka NodeList ) is modified, or removed, or if a new object is added, the overall collection will change immediately .这意味着如果此集合(又名HTMLCollection ,又名NodeList )中的任何对象(即元素,即<div><span> ,即不是spans={} )被修改或删除,或者如果添加了新对象,则整体收藏会立即改变 This makes many ways of recursion impossible and random.这使得许多递归方式变得不可能和随机。

For some reason, the MDN refer to a live collection as a HTMLCollection or a NodeList, but mentions that if using a method such as .querySelectorAll() , the NodeList isn't "live".出于某种原因,MDN 将实时集合称为 HTMLCollection 或 NodeList,但提到如果使用诸如.querySelectorAll()类的方法,则 NodeList 不是“实时”的。 Why it isn't referred to as a "static" collection to set it apart from a different behavior eludes me, especially if live collection is more a source of common issues such as the one you are experiencing.为什么不将它称为“静态”集合以将其与不同的行为区分开来我不知道,特别是如果实时收集更多地是常见问题的来源,例如您遇到的问题。


DOM Objects (apples) and Object Literals (oranges) DOM 对象(苹果)和对象字面量(橙色)

First, please abandon the original posted (from this point referred to as OP) code.首先,请放弃原贴(从这里简称OP)代码。 In particular this portion:特别是这部分:

Object.seal(nodes);//THIS SEALS/FREEZES JUST FINE
let spans = {};//TRIED WITH AND W/O THIS JUST TO MAKE SURE THE OBJECT WAS CREATED
const spans = nodes.children;
console.log('type of spans: '+typeof spans);//RETURNS object
console.log('spans length: '+spans.length);//RETURNS spans length: 3
console.log('spans: '+JSON.stringify(spans));//RETURNS spans: {"0":{},"1":{},"2":{}}
Object.seal(spans);///RETURNS Uncaught TypeError: Cannot Seal

spans is not <span></span><span></span><span></span> , spans is {"0":{},"1":{},"2":{}} . spans不是<span></span><span></span><span></span>spans{"0":{},"1":{},"2":{}} The former is a HTMLCollection (or NodeList ), the latter an Object Literal , apples and oranges.前者是一个HTMLCollection (或NodeList ),后者是一个Object Literal ,apples and oranges。 Object.seal() is method for prototypical properties. Object.seal()是原型属性的方法。 Also, use var until you have more experience.此外,在您有更多经验之前,请使用var let and const can be easily cripple your code if you are not mindful of scope.如果您不注意范围, letconst很容易削弱您的代码。


Demo Outline演示大纲

  1. Copy HTML fragment from <template> tag<template>标签复制 HTML 片段
  2. Clone and append the <template> components for each iteration为每次迭代克隆并附加<template>组件
  3. Use of 3 for loops使用 3 for循环
  4. Using HTMLFormControlsCollection for user input使用 HTMLFormControlsCollection 进行用户输入
  5. Declaring certain values with letlet声明某些值

Demo演示

Note: The layout was kept close to OP, except:注意:布局保持接近 OP,除了:

  • the use of <template> <template>的使用

  • target elements kept hidden inside <template>目标元素隐藏在<template>

  • didn't bother to create 4 nested levels there's 3 that should be enough没有费心创建 4 个嵌套级别,有 3 个应该就足够了

  • not going to try Sierpinski triangles so changed classes to represent books不打算尝试谢尔宾斯基三角形所以改变了类来代表书籍

Details commented in demo演示中评论的详细信息

Demo演示

 // Refer to HTMLFormControlsCollection var UI = document.forms.ui.elements; // Register click event to button UI.btn.addEventListener('click', generate); function generate() { // Reference to #main var main = document.getElementById('main'); // Refer to Template Tag var library = document.querySelector('.library'); var lib = library.content.cloneNode(true); /* Refer to HTMLFormControlsCollection || The user data is collected in a live collection || Note that these values are outside of the loops */ var ct = UI.ct.value; var bk = UI.bk.value; var pg = UI.pg.value; /* let declaration limits it's value to the block. || var limit's its value to the function. || In this example let declares the initial value || inside each FOR loop. If a var was used then it || would be declared outside of the loop. == || Recursion is nested 2 levels deep and on each || iteration, a component from template.library || is cloned and appended. */ for (let l = 0; l < ct; l++) { // Reference lib .category let cat = lib.querySelector('.category'); // Create a shallow clone of .category (sec) var sec = cat.cloneNode(false); // Append sec it to #main main.appendChild(sec); for (let b = 0; b < bk; b++) { // Reference lib .category .book let book = lib.querySelector('.book'); // Create shallow clone of .book (pub) var pub = book.cloneNode(false); // Append it to .category (sec) sec.appendChild(pub); for (let p = 0; p < pg; p++) { // Reference lib .category .book .page let page = lib.querySelector('.page'); // Create a deep clone of.page (copy) var copy = page.cloneNode(true); // Append it to .book (pub) pub.appendChild(copy); } // Continue to add a cloned copy to pub [pg] times } // Continue to add cloned pub to sec [bk] times } // Continue to add cloned sec to #main [ct] times }
 input { font: inherit; width: 4ch; } button { font: inherit; width: 10ch; } #main { border: 6px dotted grey; display: table } .category { background: rgba(0, 0, 0, .6); display: table-row } .category::before { content: '\\1f4da'; } .book { border: 3px solid red; display: table-cell } .book::before { content: '\\1f4d8'; } .page { border: 1px solid gold; display: inline-block; } .page::before { content: '\\1f4c3'; }
 <!doctype html> <html> <head> </head> <body> <form id='ui'> <label>Categories:&nbsp; <input id='ct' type='number' min='1' max='10' value='1'> &nbsp;Books:&nbsp; <input id='bk' type='number' min='1' max='10' value='1'> &nbsp;Pages:&nbsp; <input id='pg' type='number' min='1' max='10' value='1'> </label> <button id="btn" type='button'>Generate</button> <br/> <br/> <!-- Refer to Template Tag--> <template class='library'> <section class='category'> <article class='book'> <span class='page'></span> </article> </section> </template> <main id="main"> </main> </form> </body> </html>

References参考

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM