0

I have this piece of code wherein I am making an API call and fetching some data and then inserting it onto the page so as to create an accordion style display panel. The rendering works fine but I am not able to select any element which I have inserted onto the page , for example console.log(btns) gives me an empty nodelist, hence I am not able to apply the event listener to it.

'use strict';
const quesContainer = document.querySelector('.container');

const renderData = function(mainTitle, quesTitle, tags, link1, link2, ytlink) {
  const html = `<button class="btn">hi</button>
      <div class="panel">
        <div class="topic">
          <div class="question">
            <p>
                <div class="mainTitle"><h1>${mainTitle}</h1></div>
                <div class="ques-title">${quesTitle}</div>
                <div class="link-1"><a href="${link1}">${link1}</a></div>
                <div class="link-2"><a href="${link2}">${link2}</a></div>
                <div class="tags">${tags}</div>
                <div class="yt-link"><a href="${ytlink}">Youtube Reference</a></div>
            </p>
          </div>
        </div>
      </div><br><br>`;
  quesContainer.insertAdjacentHTML('beforeend', html);
};

const getData = async function() {
  const data = await fetch("https://test-data-gules.vercel.app/data.json");
  const res = await data.json();
  const finalData = res.data;
  finalData.forEach(element => {
    element.ques.forEach(function(el) {
      //console.log(element.title);
      renderData(element.title, el.title, el.tags, el.p1_link, el.p2_link, el.yt_link);
    })
  });
}


//renderData();

getData();

const btns = document.querySelectorAll(".btn");
console.log(btns);
btns.forEach(function(element) {
  element.addEventListener("click", function() {
    console.log("clicked");
    element.classList.toggle("active");
    var panel = element.nextElementSibling;
    if (panel.style.maxHeight) {
      panel.style.maxHeight = null;
    } else {
      panel.style.maxHeight = panel.scrollHeight + "px";
    }
  });
})
<div class="container"></div>
mplungjan
  • 169,008
  • 28
  • 173
  • 236
sjaini
  • 1
  • 1
  • `getData` is async so it hasn't completed when you call `const btns = document.querySelectorAll(".btn");`. Either `await` it or put your following queries in a `then` – pilchard Aug 05 '23 at 15:42
  • But this looks like a good case for [event delegation](https://stackoverflow.com/questions/1687296/what-is-dom-event-delegation#:~:text=Event%20delegation%20is%20handling%20an,on%20elements%20within%20the%20container.) – pilchard Aug 05 '23 at 15:47

1 Answers1

0

Your event handlers are assigned in a place where the elements are not (yet) available.

You need to delegate. Here is a way to so do.

I also took the time to simplify the render

'use strict';
const quesContainer = document.querySelector('.container');



const renderData = (mainTitle, quesTitle, tags, link1, link2, ytlink) => `<button class="btn">Show</button>
      <div class="panel" hidden>
        <div class="topic">
          <div class="question">
            <p>
                <div class="mainTitle"><h1>${mainTitle}</h1></div>
                <div class="ques-title">${quesTitle}</div>
                <div class="link-1"><a href="${link1}">${link1}</a></div>
                <div class="link-2"><a href="${link2}">${link2}</a></div>
                <div class="tags">${tags}</div>
                <div class="yt-link"><a href="${ytlink}">Youtube Reference</a></div>
            </p>
          </div>
        </div>
      </div><br><br>`;



const getData = async function() {
  const data = await fetch("https://test-data-gules.vercel.app/data.json");
  const res = await data.json();
  const finalData = res.data;
  //  console.log(finalData)
  quesContainer.innerHTML = finalData
    .map(element => `<div class="q">${element.ques
      .map(el => renderData(element.title, el.title, el.tags, el.p1_link, el.p2_link, el.yt_link))
      .join('')}</div>`)
    .join('');

}


quesContainer.addEventListener("click", (e) => {
  const element = e.target.closest('button');
  element.classList.toggle("active");
  quesContainer.querySelectorAll('.btn').forEach(btn => {
    btn.nextElementSibling.hidden = !btn.matches('.active');
  });
});
getData();
.active {
  color: green;
}
<div class="container"></div>
mplungjan
  • 169,008
  • 28
  • 173
  • 236
  • You haven't addressed the actual problem with the OP's code which is not waiting for async (I agree delegation is a better solution thus my comment above) – pilchard Aug 05 '23 at 16:58
  • Already mentioned by you in comments. But you were REALLY fast to comment on that!!! I updated while you were commenting – mplungjan Aug 05 '23 at 16:58