Search code examples
javascriptcallbackdom-eventsinnerhtml

Callback function that creates elements return element with empty innerHTML


I have a function that creates a li element with buttons. But when I call it via an eventListener, the element created in return was just a list without the innerHTML content.

What's happening and how do I fix that?
Here's my JS script:

const inputField = document.querySelector("input.inputField");
const addToList = document.querySelector("input.addToList");
const list = document.querySelector("div.list");

createTodo = () => {
  var li = document.createElement("li");
  let up = "<button class='up'>Up</button>";
  li.innerHTML += up;
  let down = "<button class='down'>Down</button>";
  li.innerHTML += down;
  let remove = "<button class='remove'>Remove</button>";
  li.innerHTML += remove;
  return li;
};

addToList.addEventListener("click", () => {
  let todo = inputField.value;
  var li = createTodo();
  console.log(li);
  li.textContent = todo;
  list.appendChild(li);
  inputField.value = "";
});

Solution

  • The problem is that you're destroying the existing element nodes in the li when you do this:

    li.textContent = todo;
    

    What you could instead do is use .insertAdjacentText() to append new text nodes without losing the element nodes.

    li.insertAdjacentText("beforeend", todo);
    

    Also, generally using += with .innerHTML can cause problems. It's not too bad in your code, but there are better ways, for example using .insertAdjacentHTML().

    And the multiple insertions seems more verbose than needed. I'd do this instead:

    const createTodo = () => {
      const li = document.createElement("li");
      li.insertAdjacentHTML("beforeend", `
        <button class='up'>Up</button>
        <button class='down'>Down</button>
        <button class='remove'>Remove</button>
      `;
      return li;
    };
    

    And then why not have your function receive the todo text as an argument? You can then use string interpolation to make it really simple:

    const inputField = document.querySelector("input.inputField");
    const addToList = document.querySelector("input.addToList");
    const list = document.querySelector("div.list");
    
    const createTodo = (todo) => {
      const li = document.createElement("li");
      li.insertAdjacentHTML("beforeend", `
        <button class='up'>Up</button>
        <button class='down'>Down</button>
        <button class='remove'>Remove</button>
        ${todo}
      `;
      return li;
    };
    
    addToList.addEventListener("click", () => {
      list.appendChild(createTodo(inputField.value));
      inputField.value = "";
    });