1

How to declare const and find element on the page, that was dynamically rendered after fetch?

Let's say I have navigation with first element hard coded in html and the rest dynamically rendered after fetch.

If I declare it at the beginning of the script:

const navItems = document.querySelectorAll('#nav li');

it only finds the first one (hard coded). I understand why this is happening, but I'm not sure what to do with this. I need all of navItems in other functions and it's not working.

If I declare it in the last then of the fetch function. It works, but I'm not sure it's a correct way. It doesn't look 'pretty'.

mplungjan
  • 169,008
  • 28
  • 173
  • 236
Ola Kozioł
  • 95
  • 3
  • 10
  • 1
    It's is a right way. Another way is to "convert" it to function `const navItems = () => document.querySelectorAll('#nav li');` so when you need it, instead of `navItems` you call `navItems()`. – Mosh Feu Nov 16 '20 at 08:26
  • Please let us know WHY and for WHAT you want to find them all. I was guessing you need to click the LIs and need to assign event handlers. this question is a bit like an X/Y problem until you let us know what you were planning with the collection. – mplungjan Nov 16 '20 at 12:47

3 Answers3

0

You could emit a custom event at the end of your fetch (in the then clause) or you could use a MutationObserver to check for changes in the DOM in a specific subtree (your nav, for example).

eloyra
  • 519
  • 2
  • 6
0

If you want to assign event handlers to your new LIs, you need to delegate

document.querySelector('#nav').addEventListener("click",function(e) {
  const tgt = e.target;
  if (tgt.tagName==="LI") {
    console.log(tgt.textContent;
  }
  const navItems = tgt.querySelectorAll("li");
  console.log(navItems.length)
})

that will work regardless of WHEN you insert the LIs

mplungjan
  • 169,008
  • 28
  • 173
  • 236
  • Yes but that code will run every time you click a nav element which isn't really ideal. – eloyra Nov 16 '20 at 08:27
  • That VERY MUCH depends on what OP wants. If he wants to click any new LI this IS the way to do it – mplungjan Nov 16 '20 at 08:28
  • He specified he wants to select all the nav elements after a fetch, so I believe it would be better to just wait for that fetch to finish and do what you have to do, instead of having some logic repeating itself indefinetly. – eloyra Nov 16 '20 at 08:30
  • There is nothing here repeating indefinitely. Why would this logic repeat itself? It is ONE event listener that will react to ONE click at a time and not repeat anything. – mplungjan Nov 16 '20 at 08:34
  • A mutation observer is running indefinitely until you stop it. https://stackoverflow.com/questions/31659567/performance-of-mutationobserver-to-detect-nodes-in-entire-dom/39332340 – mplungjan Nov 16 '20 at 08:36
  • The code inside the event listener will be triggered every time there is a click event, we can agree on that. If you want to unsubscribe the event, you need a way to know if the dynamic elements have been created, and so you might as well just use a more direct method of doing just that, and trigger your code that one time and not on every click. – eloyra Nov 16 '20 at 08:38
  • Of course, but you detect the creation of the new elements the moment they are created, do your thing and unsubscribe. It's more reliable. – eloyra Nov 16 '20 at 08:39
  • The code inside the event listener will execute when a click occurs inside the nav container. That is VERY likely what we need. – mplungjan Nov 16 '20 at 08:42
  • In no way does OP specify that the behaviour needs to be retriggered on click, OP just wants to know when the dynamic elements are loaded so that he can select them. We can agree to disagree haha – eloyra Nov 16 '20 at 12:31
  • We have no idea WHY he wants to select them. Perhaps he wants to select them so he can add the event listener to them all again and again – mplungjan Nov 16 '20 at 12:46
  • Exactly, we don't know why, you make assumptions that really don't apply to what was asked. – eloyra Nov 16 '20 at 13:25
0

You could access that element you dynamically added with event delegation. As an example let's say you want to add an event listener to that element:

First, add a class 'example' to it. Then fire off func only when an element with that class is clicked on.

document.body.addEventListener('click', func);

const func = (e) => {
  if (e.target.className === 'example') {
    console.log('element accessed!'};
}

Not saying that's the most elegant or pretty way to go, but it is definitely an option in such cases.

Armando Guarino
  • 1,120
  • 1
  • 5
  • 16