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

时间:2017-09-26 19:00:16

标签: javascript uncaught-typeerror

提前感谢您的帮助!我正在尝试做一些递归范围标记“replaceWith”操作,用div和3个子跨度替换span。我的子对象(包含要替换的跨度)有一个巨大的问题,当第一个跨度被替换时会以某种方式更新(导致我的对象每次增长2),所以我想我会尝试制作对象恒定然后冷冻或密封它。我不确定这是否是正确的方法,但我不是一个足够好的javascript程序员知道。无论如何,我以这种方式得到我的跨度对象:

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

HTML很简单:

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

跨越作为对象返回,除了它之外的所有内容在使用Object.freeze或Object.seal时都会失败!我需要知道是否有人可以告诉我我做错了什么..跨度对象看起来与节点对象没有任何不同,节点对象冻结/密封就好了。 如果我可以将这些对象冻结,那么我的计划是对替换进行以下操作:

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();
  }
}

感谢您的任何见解!

修改 为了洞察力,这就是我想要的;

<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>

这就是我目前所得到的;

<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>

1 个答案:

答案 0 :(得分:1)

问题

  

所以我是否添加/删除类或其他东西,以便代码迭代具有某种锚点?我只是不明白我如何声明一个等于某些子元素的变量,而不是永久的。如何在不重新定义变量的情况下改变DOM中的内容会改变变量中的内容??

直播和“静态” HTMLCollections / NodeLists

使用正确的方法收集元素/节点*对DOM操作很重要。旧方法.children.getElementsByTagName().getElementsByName().getElementsByClassName()等返回DOM对象的 Live Collection 。这意味着此集合中的任何对象(即元素,即<div><span>,即非spans={})(又名 HTMLCollection ,又名 NodeList < / em>)被修改或删除,或者如果添加了新对象, 整个集合将立即更改 。这使得许多递归方式变得不可能和随机。

出于某种原因,MDN将实时集合称为HTMLCollection或NodeList,但提到如果使用诸如.querySelectorAll()之类的方法,则NodeList不是“实时”。为什么它不被称为“静态”集合,将其与不同的行为区分开来,尤其是如果实时集合更像是您遇到的常见问题的来源。

<小时/>

DOM对象(苹果)和对象文字(橙子)

首先,请放弃原贴(从此点称为OP)代码。特别是这一部分:

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不是<span></span><span></span><span></span>spans{"0":{},"1":{},"2":{}}。前者是 HTMLCollection (或 NodeList ),后者是 Object Literal ,苹果和橘子。 Object.seal()是原型属性的方法。此外,请使用var,直到您获得更多经验。如果您不注意范围,letconst可能会轻易削弱您的代码。

<小时/>

演示大纲

  1. <template>标记
  2. 复制HTML片段
  3. 克隆并附加每次迭代的<template>个组件
  4. 使用3个for循环
  5. 使用HTMLFormControlsCollection进行用户输入
  6. 使用let
  7. 声明某些值

    演示

    注意:布局保持接近OP,但以下情况除外:

    • 使用<template>

    • 目标元素隐藏在<template>

    • 没有打算创建4个嵌套级别,其中3个应该足够

    • 不会尝试使用Sierpinski三角形,因此更改了类来代表书籍

    详情在演示

    中发表

    演示

    // 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>

    参考