Hai Na Zheng
Hai Na Zheng

Reputation: 177

javaScript insetAdjacentHTML() not working

all I was trying to use insertAdjacentHTML() to create elements and add events by using addEventListener().I don't think there was a logical problem in my code below, but it was not working (not console.log()ing)

My code was as below:

const END_POINT = "https://swapi.dev/api/people/"


let ul = document.querySelector("ul")
fetch(END_POINT)
.then(response => response.json())
.then(data => {
    let people = data.results
    console.log(people)
    people.forEach((person, i) => {
        let html = `
        <li><a href=${person.homeworld} target="_blank">${person.name}</a></li>
        `
        ul.insertAdjacentHTML("afterbegin", html)
        function expandDetails(ev) {
            ev.preventDefault()
            console.log("fef")
            console.log("ev.target", ev.target)
        }
        let a = document.querySelectorAll("a")[i]
        a.addEventListener("click", expandDetails)

    })
})
<ul id="list"></ul>

I have a feeling it's about a delay of something and a setTimeout() should be added somewhere. Any advice?

Upvotes: 0

Views: 178

Answers (2)

Andy
Andy

Reputation: 63514

Instead of listeners on all list items use event delegation. Attach one listener to the ul element and have that listen to events from its child elements as they "bubble up" the DOM. When expandDetails is called check that the event is from an anchor in a list item, and then log the result.

const END_POINT = "https://swapi.dev/api/people/"

const ul = document.querySelector("ul")
ul.addEventListener('click', expandDetails);

function expandDetails(e) {
  if (e.target.matches('li a')) {
    e.preventDefault();
    console.log(e.target.textContent);
  }
}

fetch(END_POINT)
  .then(response => response.json())
  .then(data => {
    const people = data.results;
    people.forEach((person, i) => {
      const html = `
        <li>
          <a href=${person.homeworld}>${person.name}</a>
        </li>
        `
      ul.insertAdjacentHTML("afterbegin", html)
    });
  });
<ul id="list"></ul>

Another thing you might consider is creating an array of HTML and then joining it up before adding it to the DOM. That way you're only calling insertAdjacentHTML once after all the HTML has been compiled rather on every iteration of the loop. (Note, however, that this version prints the list items in the opposite order to the previous example which may or may not be an issue. I've also used async/await to make the code a little neater.)

const END_POINT = "https://swapi.dev/api/people/"

const ul = document.querySelector("ul")
ul.addEventListener('click', expandDetails);

function expandDetails(e) {
  if (e.target.matches('li a')) {
    e.preventDefault();
    console.log(e.target.textContent.trim());
  }
}

async function main() {
  const response = await fetch(END_POINT);
  const { results } = await response.json();
  const html = results.map(person => {
    return `
      <li>
        <a href=${person.homeworld}>
          ${person.name}
        </a>
      </li>
      `
  });
  ul.insertAdjacentHTML('beforeend', html.join(''));
}

main();
<ul id="list"></ul>

Upvotes: 1

Wimanicesir
Wimanicesir

Reputation: 5121

Just do it outside your loop:

const END_POINT = "https://swapi.dev/api/people/"


let ul = document.querySelector("ul")
fetch(END_POINT)
.then(response => response.json())
.then(data => {
    let people = data.results
    people.forEach((person, i) => {
        let html = `
        <li><a href=${person.homeworld} target="_blank">${person.name}</a></li>
        `
        ul.insertAdjacentHTML("afterbegin", html)

    })
    document.querySelectorAll("a").forEach(a => a.addEventListener('click', expandDetails))
    
})

function expandDetails(ev) {
    ev.preventDefault()
    console.log("ev.target", ev.target)
}
<ul id="list"></ul>

Upvotes: 2

Related Questions