Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Can't grab dynamically created elements via querySelector using insertAdjacentHTML

I’m trying to grab an element from page and add an addEventHandler to it. Element (link with class .catalog__link) was dynamically created in another external function when the page was loaded using insertAdjacentHTML. I invoke them both in third js file via import keyword. Everything loads perfectly on the page, the elements are created for me, but I can’t grab them from another function connected to the page. Here’s examples of codes. I have two external function, combined in one js file and simple html page

This is fillCatalog.js (First file)


const row = document.querySelector('.catalog__row');

export const fill = function (brand) {
  fetch(`./data/${brand}.json`)
    .then(function (response) {
      return response.json();
    })
    .then(function (data) {
      let products = [...data.products];

      products.forEach(product => {
       
        row.insertAdjacentHTML(
          'afterbegin',
          ` <a class="catalog__link" href="#" >
              <div class="catalog__product">
              <div class="catalog__product-img">
              <img class="catalog__productImg" src=${product['img-src']} alt="" srcset="" />
              </div>
              <h3 class="catalog__product-model">${product['model']}</h3>
              <p class="catalog__product-brand">${product['brand']}</p>
              <span class="catalog__product-price">${product['price']}</span>
              
          </div></a>`
        );
      });
    });
};

This is productSave.js (Second file)

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel


class Product {
  constructor(cardImg, cardName, cardBrand = '', cardPrice) {
    this.cardImg = cardImg;
    this.cardName = cardName;
    this.cardBrand = cardBrand;
    this.cardPrice = cardPrice;
  }
}

const links = document.querySelectorAll('.catalog__link');

export const productSave = function () {
  window.addEventListener('DOMContentLoaded', () => {
    console.log(links);
    links.forEach(link => {
      link.addEventListener('click', e => {
        productItem = link.querySelector('.catalog__product');
        const newProduct = new Product(
          productItem.querySelector('catalog__productImg').src,
          productItem.querySelector('.catalog__product-model').textContent,
          productItem.querySelector('.catalog__product-brand').textContent,
          productItem
            .querySelector('.catalog__product-price-price')
            .textContent.replace(/\D/g, '')
        );
        localStorage.setItem('newCard', JSON.stringify(newProduct));
        console.log(card.querySelector('.card__name').textContent);
      });
    });
  });
};

This is third file where I invoke external function

loadContent.js

import { fill } from './fillCatalog.js';
import { productSave } from './productSave.js';
fill('jordans');
productSave();

Simple HTML

<html lang="ru">
  <head>
    <!--=include head.html-->
    </style>
  </head>
  <body>
<div class="catalog">
  <div class="catalog__content">
    <div class="catalog__row"></div>
  </div>
</div>

  </body>

  <script type="module" src="../js/goodscart.js"></script>
  <script type="module" src="../js/loadContent.js"></script>
</html>

I’ve tried use beforeend afterent and etc. Tried also use getElementsByTag, it returns empty HtmlCollection[]. After insertAdjacentHTML, can’t select like usually this links.

Could anyone help me please with this issue? I can’t find solution for that. Thank you

>Solution :

You need to define links inside the function to avoid it being initialized before the elements are rendered.

class Product {
...
}

// since this is not in a function it is initialized on import so 
// before you render the `catalog__link` 
const links = document.querySelectorAll('.catalog__link');

export const productSave = function () {
...
};

You can also implement a detector that waits for at least one link to be rendered. This Promise would resolve after at least 1 item with catalog__link can be found. Then you can chain your logic with .then(...). This one also includes a timeout of 10 seconds which executes .catch(...) (if present otherwise throws) if the element was not found in time.

new Promise((res, rej) => {
    const timeout = setTimeout(() => {
        clearTimeout(timeout);
        clearInterval(interval);
        rej("Not Found")
    }, 10000);
    const interval = setInterval(() => {
        const links = document.querySelectorAll('.catalog__link');
        if (links.length == 0) {
            return;
        }
        clearInterval(interval);
        clearTimeout(timeout);
        res(links)
    },200);
});
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading