Создание сетки с использованием JavaScript без display:grid

В настоящее время я работаю над эскизом проекта Odin. Вот код, который я придумал.

const container = document.querySelector(".container");
const input = document.querySelector("input");
const submitBtn = document.querySelector("button");

function createGrid() {
    let value=input.value;
    for (let i = 0; i < value; i++) {

        
        const createDivRow = document.createElement("div");
        createDivRow.classList.add("gridrow")
         createDivRow.textContent = i;
        container.appendChild(createDivRow);
    }
    
}

function createGridItems() {
    let value = input.value;
    for (let i = 0; i < value; i++) {
        const divRow = document.querySelectorAll(".gridrow");
        const createDivItem = document.createElement("div");
        createDivItem.textContent = "aa";
        divRow.appendChild(createDivItem);
    }
}

submitBtn.addEventListener("click", createGrid);
submitBtn.addEventListener("click", createGridItems);

В функции createGrid() я стремлюсь создать n элементов div (n — это пользовательский ввод из текстового поля ввода), что мне удалось. Результатом стало n элементов div с class="gridrow" в контейнере.

В функции createGridItems() я хотел бы поместить одинаковое количество элементов div "n" в каждую строку .gridrow (которую я задал переменной divRow).

Я попробовал объявить const divRow = document.querySelector(".gridRow"), и в результате в ПЕРВОМ div .gridrow появилось n элементов div, что почти то, что я хотел.

Итак, я решил объявить const divRow = document.querySelectorAll(".gridrow") таргетингом на все созданные мной .gridrow и поместить n элементов div в КАЖДЫЙ div .gridrow. В результате все, что я получил, это просто пустое количество элементов .gridrow без нужных элементов.

Может ли кто-нибудь сказать мне, где я ошибся? Я был уверен, что приведенный выше код будет работать, потому что я уже нацелился на все элементы div .divrow, когда объявлял divRow = document.querySelectorAll(".gridrow").

Спасибо!

document.querySelectorAll возвращает NodeList (коллекцию элементов). Вы проверяли консоль JavaScript? ваш код выдает ошибку при попытке .appendChild перебрать коллекцию.
Diego D 27.05.2024 12:04
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
1
67
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Это потому, что querySelectorAll() возвращает статический (не живой) NodeList:

Согласно MDN:

Метод Document querySelectorAll() возвращает статический (не живой) NodeList представляет собой список элементов документа, соответствующих указанная группа селекторов.

Хотя NodeList не равен массиву, он называется array-alike, но когда он статичен, любые изменения в DOM не повлияют на содержимое коллекции узлов.

Согласно MDN:

Статические списки узлов

В других случаях NodeList является статическим, и любые изменения в DOM не влияют на содержимое коллекции. Вездесущий метод document.querySelectorAll() возвращает статический Список узлов.

Тем не менее, вам нужно сначала преобразовать NodeList в массив, затем внести изменения и, наконец, внедрить свой html, и самый простой способ преобразовать его в массив — использовать оператор распространения, например:

function createGridItems() {
  let value = input.value;
  for (let i = 0; i < value; i++) {
    // wrap with brackets, then use spread
    const divRow = [...document.querySelectorAll(".gridrow")];
    const createDivItem = document.createElement("div");
    createDivItem.textContent = "aa";
    // use push instead of appendChild
    divRow.push(createDivItem);
    // now loop throgh divrow
    let html = "";
    divRow.forEach(el=> html+=el.outerHTML);
    document.querySelector("WHATEVER_SELECTOR_YOU_WANT").innerHTML = html
  }
}

вы все еще вызываете appendChild через массив

Diego D 27.05.2024 12:07

Я опубликовал свой собственный ответ, потому что вы не поняли суть... теперь вы просто помещаете элементы в массив, не связанный с dom... какова его цель??

Diego D 27.05.2024 12:14

привет, я попробовал запустить ваш ответ, но он все равно не работает. Я попробовал погуглить, как превратить список узлов в массив, и нашел метод Array.from().

Andrey 27.05.2024 12:27

я попытался превратить divRow в массив с помощью const arrayDivRow = Array.from(divRow), а затем попытался добавить arrayDivRow с помощью createDivItem, но это все равно не сработало. есть идеи, где что-то пошло не так?

Andrey 27.05.2024 12:29

благодаря @DiegoD отметил, что да, я создал массив, но затем не вставил его в html, я отредактировал свой ответ, чтобы это исправить.

MHD Alaa Alhaj 27.05.2024 12:33
Ответ принят как подходящий

document.querySelectorAll возвращает NodeList (набор элементов).

Если вы проверили консоль JavaScript, ваш код выдает ошибку при попытке .appendChild перебрать коллекцию.

Вместо этого вам нужно было перебрать эту коллекцию и для каждого элемента (являющегося .gridrow) добавить туда несколько элементов.

https://developer.mozilla.org/en-US/docs/Web/API/NodeList

Примечание. Хотя NodeList не является массивом, его можно перебирать. над ним с помощью forEach(). Его также можно преобразовать в настоящий массив, используя Массив.из().

Это быстрая демонстрация:

const container = document.querySelector(".container");
const input = document.querySelector("input");
const submitBtn = document.querySelector("button");

function createGrid() {
    const value = input.value;
    for (let i = 0; i < value; i++) {        
        const createDivRow = document.createElement("div");
        createDivRow.classList.add("gridrow")
         createDivRow.textContent = i;
        container.appendChild(createDivRow);
    }
    
}

function createGridItems() {  
    const value = input.value;
    //here you select the rows as .gridrow elements
    const divRows = document.querySelectorAll(".gridrow");    
    //iterate over each one of them
    divRows.forEach(divRow =>{
        //and append to it a new div for as many times as the value fetched from the input element
        for(let i=0;i < value; i++){
          const createDivItem = document.createElement("div");
          createDivItem.textContent = "aa";
          divRow.appendChild(createDivItem);
        }        
    });       
}

submitBtn.addEventListener("click", createGrid);
submitBtn.addEventListener("click", createGridItems);
.gridrow{
  border: solid 1px;  
  margin-top: 1em;
}

.gridrow > div {
  border: solid 1px red;
  margin-left: 1em;
  display: inline-block;
}
<div class = "container"></div>

<input type = "text">
<button>submit</button>

И, наконец, поскольку в комментариях возникла проблема с производительностью, вот тест производительности, показывающий разницу во времени для каждого решения:

  • На добавлениеDivsOneByOne ушло 2,5999999046325684 миллисекунды.
  • addDivsInBatch заняло 2 миллисекунды
  • addDivsUsingInnerHTML занял 1 миллисекунду

const numElements = 1000;

function addDivsOneByOne(containerId) {
  const container = document.getElementById(containerId);
  for (let i = 0; i < numElements; i++) {
    const div = document.createElement('div');
    div.textContent = `${i + 1}`;
    container.appendChild(div);
  }
}

function addDivsInBatch(containerId) {
  const container = document.getElementById(containerId);
  const fragment = document.createDocumentFragment();
  for (let i = 0; i < numElements; i++) {
    const div = document.createElement('div');
    div.textContent = `${i + 1}`;
    fragment.appendChild(div);
  }
  container.appendChild(fragment);
}

function addDivsUsingInnerHTML(containerId) {
  const container = document.getElementById(containerId);
  let html = '';
  for (let i = 0; i < numElements; i++) {
    html += `<div>${i + 1}</div>`;
  }
  container.innerHTML = html;
}

function runBenchmark() {
  document.getElementById('container1').innerHTML = '';
  document.getElementById('container2').innerHTML = '';
  document.getElementById('container3').innerHTML = '';

  const results = [];

  // Measure addDivsOneByOne
  let start = performance.now();
  addDivsOneByOne('container1');
  let end = performance.now();
  results.push(`addDivsOneByOne took ${end - start} milliseconds.`);

  // Measure addDivsInBatch
  start = performance.now();
  addDivsInBatch('container2');
  end = performance.now();
  results.push(`addDivsInBatch took ${end - start} milliseconds.`);

  // Measure addDivsUsingInnerHTML
  start = performance.now();
  addDivsUsingInnerHTML('container3');
  end = performance.now();
  results.push(`addDivsUsingInnerHTML took ${end - start} milliseconds.`);

  // Display results
  document.getElementById('results').innerHTML = results.join('<br>');
}
.container {
  border: 1px solid #ccc;
  padding: 10px;
  margin: 10px 0;
}

.container div {
  margin: 2px 0;
}
<div id = "results"></div>

<div class = "container" id = "container1"></div>
<div class = "container" id = "container2"></div>
<div class = "container" id = "container3"></div>
<div>
  <button onclick = "runBenchmark()">Run Benchmark</button>
</div>

Привет! Большое спасибо, это сработало! Для ясности: когда я запускаю divRows с помощью forEach(), он рассматривает divRows как массив, а не как nodeList? Вот почему это сработало?

Andrey 27.05.2024 12:26

Здесь нужно добавить только одну вещь: вы добавляете дочерний элемент на каждой итерации, что может повлиять на производительность в зависимости от количества элементов. Вместо этого создайте временную переменную, которая собирает все элементы, а затем добавьте ее один раз.

MHD Alaa Alhaj 27.05.2024 12:39

@AndreyWidjaja Я отредактировал ответ, добавив ссылку на страницу NodeList mdn... буквально заявив, что, несмотря на то, что он не является массивом, он все еще доступен для повторения. И, кстати, при добавлении таких дочерних элементов в цикл проблем с производительностью не возникает. Чтобы увидеть разницу в добавлении их всех сразу, вместо того, чтобы делать это по одному, потребуется эталон, но на самом деле это звучит как стрижка волос пополам, когда величина порядка сотен.

Diego D 27.05.2024 15:39

@DiegoD ах, я вижу, я почитаю эту страницу mdn! еще немного туманно в отношении концепции..

Andrey 27.05.2024 16:15

@MHDLaaAlhaj Я пока не совсем уверен, как это сделать. Я работаю над проектом Odin и только что закончил практиковаться в циклах, так что я использовал именно это. В любом случае спасибо за вклад!

Andrey 27.05.2024 16:16

@DiegoD Я не говорил, что NodeList не является итеративным, я даже упомянул в своем ответе ту же ссылку, которую вы уже прокомментировали. мой комментарий был о выступлении. Вы должны знать, что всякий раз, когда есть шанс повысить производительность, даже если этот шанс составляет всего лишь 1%, мы должны пойти на это, особенно если для достижения этой цели не будет внесено никаких огромных изменений. Если вы действительно не знаете о влиянии этого на производительность, я рекомендую вам прочитать больше о Repaint и Reflow.

MHD Alaa Alhaj 27.05.2024 23:32

да, вы правы, и я знал, что есть разница в производительности... но, честно говоря, я был уверен, что включение парсера html при использовании innerHTML замедлит процесс... Я ошибался. В любом случае я добавил тест, который покажет, учитывая произвольное количество добавляемых элементов (по умолчанию 1000), покажет время, необходимое для каждого из этих трех разных подходов.

Diego D 28.05.2024 09:46

@DiegoD Вы приложили огромные усилия, спасибо за объяснение!

MHD Alaa Alhaj 28.05.2024 10:38

Другие вопросы по теме