Как использовать .querySelectorAll для элементов, созданных с помощью .insertAdjacentHTML

Я создаю вымышленный интернет-магазин растений и в настоящее время работаю над их фильтрацией, комбинируя различные параметры (требование света, потребление воды, токсичность и т. д.).

Используя отдельный файл JSON, я создаю карточки на основе данных после получения файла JSON:

getPlants().then((data) => {
  data.forEach((data) => {
    plantcards.insertAdjacentHTML(
      "afterbegin",
      `<article class = "plantcard ${getPlantLight(data.Light)} ${getPlantWater(
        data.WaterDemand
      )}wasser ${getPlantToxic(data.Toxic)}">
        <div class = "article-wrapper">
          <figure>
            <img src = "../images/plant_types/${data.Image}" alt = "${data.Name}" />
          </figure>
          <div class = "article-body">
            <h2>${data.Name}</h2>
            <h3>${data.NameLatin}</h3>
            <p>
               Standort: ${getPlantLight(data.Light)}<br/>
               Wasserbedarf: ${getPlantWater(data.WaterDemand)}<br/>
               Schwierigkeitsgrad: ${getPlantMaintenance(data.Maintenance)}<br/>
               Giftig: ${getPlantToxic(data.Toxic)}<br/>
            </p>
            <a href = "#" class = "read-more">
              Read more <span class = "sr-only">about this is some title</span>
              <svg xmlns = "http://www.w3.org/2000/svg" class = "icon" viewBox = "0 0 20 20" fill = "currentColor">
                <path fill-rule = "evenodd" d = "M12.293 5.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-2.293-2.293a1 1 0 010-1.414z" clip-rule = "evenodd" />
              </svg>
            </a>
          </div>
        </div>
      </article>`
    );
  });
});

let allPlantcards = Array.from(document.querySelectorAll(".plantcard"));

allPlantcards всегда пустой массив длиной 0, я думаю, потому что allPlantcards создается до или во время загрузки карточек.

Я также пытался разместить let allPlantcards = Array.from(document.querySelectorAll(".plantcard")); внутри других функций, но тогда я не смогу использовать их вне функции, верно?

🤔 А знаете ли вы, что...
JavaScript позволяет создавать собственные серверные приложения с использованием платформы Node.js.


61
2

Ответы:

Похоже, вы столкнулись с проблемой синхронизации: вы пытаетесь выбрать элементы, которые еще не добавлены в DOM, потому что ваш вызов document.querySelectorAll(".plantcard") выполняется до вставки карточек.

Вот скромное предложение рабочего кода:

Этот HTML-шаблон устанавливает базовую структуру и функциональность для отображения карточек растений и управления ими в интернет-магазине. Настройте пути к изображениям и данным в соответствии с потребностями вашего приложения.

getPlants() имитирует получение данных о заводе. Замените это фактическим вызовом API. getPlantLight(), getPlantWater(), getPlantToxic() и getPlantMaintenance() определяют классы CSS на основе атрибутов растений. displayPlantCards(data) добавляет карточки растений в DOM и затем выбирает их.

async function getPlants() {
  return [{
      Name: "Aloe Vera",
      NameLatin: "Aloe barbadensis",
      Light: "Bright",
      WaterDemand: "Low",
      Maintenance: "Easy",
      Toxic: "No",
      Image: "https://fastly.picsum.photos/id/649/200/300.jpg?hmac=3hfKZ0fzc7Ie_jSDrRCLD-bO3e71sZ_5xyZmJQXyNFg"
    },
    {
      Name: "Spider Plant",
      NameLatin: "Chlorophytum comosum",
      Light: "Indirect",
      WaterDemand: "Medium",
      Maintenance: "Easy",
      Toxic: "No",
      Image: "https://fastly.picsum.photos/id/649/200/300.jpg?hmac=3hfKZ0fzc7Ie_jSDrRCLD-bO3e71sZ_5xyZmJQXyNFg"
    }
    // Add more plant objects as needed
  ];
}

function getPlantLight(light) {
  return light === "Bright" ? "bright-light" : "indirect-light";
}

function getPlantWater(waterDemand) {
  return waterDemand === "Low" ? "low-water" : "medium-water";
}

function getPlantToxic(toxic) {
  return toxic === "Yes" ? "toxic" : "non-toxic";
}

function getPlantMaintenance(maintenance) {
  return maintenance === "Easy" ? "easy-maintenance" : "hard-maintenance";
}

function displayPlantCards(data) {
  const plantcards = document.getElementById("plantcards");

  data.forEach((plant) => {
    plantcards.insertAdjacentHTML(
      "beforeend",
      `<article class = "plantcard ${getPlantLight(plant.Light)} ${getPlantWater(
        plant.WaterDemand
      )} ${getPlantToxic(plant.Toxic)}">
                        <div class = "article-wrapper">
                            <figure>
                                <img src = "${plant.Image}" alt = "${plant.Name}" />
                            </figure>
                            <div class = "article-body">
                                <h2>${plant.Name}</h2>
                                <h3>${plant.NameLatin}</h3>
                                <p>
                                   Standort: ${getPlantLight(plant.Light)}<br/>
                                   Wasserbedarf: ${getPlantWater(
                                     plant.WaterDemand
                                   )}<br/>
                                   Schwierigkeitsgrad: ${getPlantMaintenance(
                                     plant.Maintenance
                                   )}<br/>
                                   Giftig: ${getPlantToxic(plant.Toxic)}<br/>
                                </p>
                                <a href = "#" class = "read-more">
                                  Read more
                                  
                                  <svg xmlns = "http://www.w3.org/2000/svg" class = "icon" viewBox = "0 0 20 20" fill = "currentColor">
                                    <path fill-rule = "evenodd" d = "M12.293 5.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-2.293-2.293a1 1 0 010-1.414z" clip-rule = "evenodd" />
                                  </svg>
                                </a>
                            </div>
                        </div>
                      </article>`
    );
  });

  // After inserting the plant cards, you can select them
  const allPlantcards = Array.from(document.querySelectorAll(".plantcard"));

  // Example: Add your logic to filter or manipulate plant cards here
  console.info(allPlantcards); // Check if cards are correctly selected
}

// Fetch data and display plant cards
getPlants().then((data) => {
  displayPlantCards(data);
});
#plantcards {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
  padding: 20px;
  justify-content: center;
}

.plantcard {
  width: 200px;
  background-color: #f0f0f0;
  border: 1px solid #ddd;
  border-radius: 8px;
  overflow: hidden;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  display: flex;
  flex-direction: column;
}

.article-wrapper {
  display: flex;
  flex-direction: column;
  flex: 1;
}

.article-body {
  padding: 10px;
  flex: 1;
  display: flex;
  flex-direction: column;
}

.article-body h2 {
  margin: 0;
  font-size: 1.2em;
}

.article-body h3 {
  margin: 5px 0;
  color: #555;
  font-size: 1em;
}

.article-body p {
  margin: 10px 0;
  font-size: 0.9em;
  color: #333;
}

.read-more {
  display: flex;
  align-items: center;
  text-decoration: none;
  color: #007bff;
  font-weight: bold;
  margin-top: auto;
  padding: 10px;
  border-top: 1px solid #ddd;
  background-color: #fff;
}

.read-more .icon {
  margin-left: 5px;
  width: 16px;
  height: 16px;
}

.plantcard img {
  width: 100%;
  height: auto;
  display: block;
}
<div id = "plantcards">
  <!-- Plant cards will be dynamically inserted here -->
</div>

Решено

Проблема, с которой вы столкнулись, на самом деле связана с тем, что querySelectorAll вызывается до того, как элементы были добавлены в DOM с помощью InsertAdjacentHTML. Чтобы решить эту проблему, нужно убедиться, что код выбора элементов выполняется после добавления всех карточек растений в DOM.

Просто переместите вызов querySelectorAll в конец getPlants().then(...), чтобы он выполнялся после добавления всех карточек в DOM:

getPlants().then((data) => {
  data.forEach((data) => {
    plantcards.insertAdjacentHTML(
      "afterbegin",
      `<article class = "plantcard ${getPlantLight(data.Light)} ${getPlantWater(
        data.WaterDemand
      )}wasser ${getPlantToxic(data.Toxic)}">
        <div class = "article-wrapper">
          <figure>
            <img src = "../images/plant_types/${data.Image}" alt = "${data.Name}" />
          </figure>
          <div class = "article-body">
            <h2>${data.Name}</h2>
            <h3>${data.NameLatin}</h3>
            <p>
               Standort: ${getPlantLight(data.Light)}<br/>
               Wasserbedarf: ${getPlantWater(data.WaterDemand)}<br/>
               Schwierigkeitsgrad: ${getPlantMaintenance(data.Maintenance)}<br/>
               Giftig: ${getPlantToxic(data.Toxic)}<br/>
            </p>
            <a href = "#" class = "read-more">
              Read more <span class = "sr-only">about this is some title</span>
              <svg xmlns = "http://www.w3.org/2000/svg" class = "icon" viewBox = "0 0 20 20" fill = "currentColor">
                <path fill-rule = "evenodd" d = "M12.293 5.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-2.293-2.293a1 1 0 010-1.414z" clip-rule = "evenodd" />
              </svg>
            </a>
          </div>
        </div>
      </article>`
    );
  });

  // Now the cards have already been added to the DOM and we can select them
  let allPlantcards = Array.from(document.querySelectorAll(".plantcard"));
  console.info(allPlantcards); // Now allPlantcards will not be empty
});