1

I'm working on my to do list app. Most of it works fine, but I still get this issue (Uncaught TypeError: Cannot read property of undefined). For some reason I cannot understand yet, I cannot pass the array toDoList to the searchList function in order to filter the relevant elements for my dynamic search feature. Would anybody be willing to help?

const button = document.querySelector("button");
let toDoList = [];
const input = document.querySelector(".add");
const search = document.querySelector(".search");
const ul = document.querySelector("ul");
const removeBtn = document.getElementsByClassName("remove");
const span = document.querySelector("h1 span");
const key = 0

const addItem = (e) => {
  e.preventDefault()
  ul.innerHTML = ""
  toDoList.push(input.value)
  toDoList.forEach((toDoElement, key) => {
    const task = document.createElement("li")
    task.dataset.key = key;
    key++
    task.className = "task";
    task.textContent = toDoElement
    task.innerHTML += ` <button>usuń</button>`
    ul.appendChild(task);
    span.textContent = toDoList.length;
    task.querySelector("button").addEventListener("click", removeItem)
  })

}

const searchList = (e, toDoList) => {
  ul.innerHTML = ""
  if (search.value === "") {
    toDoList.forEach((toDoElement, key) => {
      const task = document.createElement("li")
      task.dataset.key = key;
      key++
      task.className = "task";
      task.textContent = toDoElement
      task.innerHTML += ` <button>usuń</button>`
      ul.appendChild(task);
      span.textContent = toDoList.length;
      task.querySelector("button").addEventListener("click", removeItem)
    })
  } else {
    const searchText = e.target.value.toLowerCase();
    console.log(searchText);
    console.log(toDoList);
    let result = toDoList.filter(li => li.textContent.toLowerCase().includes(searchText))
    console.log(result)
  }
}

const removeItem = (e) => {
  e.target.parentNode.remove();
  const index = e.target.parentNode.dataset.key;
  toDoList.splice(index, 1)
  span.textContent = toDoList.length;
}


button.addEventListener("click", addItem);
search.addEventListener("input", searchList)
<form>
  <input type="text" class="add">
  <button>Dodaj zadanie</button>
</form>
<form>
  <input type="text" class="search">
</form>
<h1>Liczba zadań: <span></span></h1>
<ul></ul>

3 Answers 3

1

Remove toDoList from argument in searchList const searchList = (e, toDoList) => { and remove textContent from li.textContent.toLowerCase()

const button = document.querySelector("button");
let toDoList = [];
const input = document.querySelector(".add");
const search = document.querySelector(".search");
const ul = document.querySelector("ul");
const removeBtn = document.getElementsByClassName("remove");
const span = document.querySelector("h1 span");
const key = 0

const addItem = (e) => {
  e.preventDefault()
  ul.innerHTML = ""
  toDoList.push(input.value)
  toDoList.forEach((toDoElement, key) => {
    const task = document.createElement("li")
    task.dataset.key = key;
    key++
    task.className = "task";
    task.textContent = toDoElement
    task.innerHTML += ` <button>usuń</button>`
    ul.appendChild(task);
    span.textContent = toDoList.length;
    task.querySelector("button").addEventListener("click", removeItem)
  })

}

const searchList = (e) => {
  ul.innerHTML = ""
  if (search.value === "") {
    toDoList.forEach((toDoElement, key) => {
      const task = document.createElement("li")
      task.dataset.key = key;
      key++
      task.className = "task";
      task.textContent = toDoElement
      task.innerHTML += ` <button>usuń</button>`
      ul.appendChild(task);
      span.textContent = toDoList.length;
      task.querySelector("button").addEventListener("click", removeItem)
    })
  } else {
    const searchText = e.target.value.toLowerCase();
    console.log(searchText);
    console.log(toDoList);
    let result = toDoList.filter(li =>li.toLowerCase().includes(searchText));
    console.log(result)
  }
}

const removeItem = (e) => {
  e.target.parentNode.remove();
  const index = e.target.parentNode.dataset.key;
  toDoList.splice(index, 1)
  span.textContent = toDoList.length;
}


button.addEventListener("click", addItem);
search.addEventListener("input", searchList)
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>toDoList</title>

</head>

<body>
  <form>
    <input type="text" class="add">
    <button>Dodaj zadanie</button>
  </form>
  <form>
    <input type="text" class="search">
  </form>
  <h1>Liczba zadań: <span></span></h1>
  <ul></ul>
  <script src="main.js"></script>
</body>

</html>

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks! So the error was caused by passing the toDoList array? Should TextContent also be deleted because it also caused the error?
@Jeknar, yes, you can directly access the textContent, you don't have to use it, if you see the structure of your toDoList while filtering
1

Remove toDoList from function arguments here


const searchList = (e, toDoList) >>>> const searchList = (e) => ...


that will rise another exception when you are trying to filter lis based on search string which is not in scope of this question answer;

Comments

0

You stored toDoList globally, so defining a function variable named toDoList overwrote it in that scope with whatever was passed in (which was nothing, so it was undefined). As others said, just removing the parameter from searchList was sufficient.

I tried to tidy the code up as well so it is more readable, and I added missing semicolons. They are not optional! ;-)

const button = document.querySelector("button");
const input = document.querySelector(".add");
const search = document.querySelector(".search");
const ul = document.querySelector("ul");
const removeBtn = document.getElementsByClassName("remove");
const span = document.querySelector("h1 span");

var toDoList = [];
const key = 0;

const addItem = (e) => {
  e.preventDefault();
  ul.innerHTML = "";
  toDoList.push(input.value);
  toDoList.forEach((toDoElement, key) => {
    const task = document.createElement("li");
    task.dataset.key = key++;
    task.className = "task";
    task.innerHTML = toDoElement + "<button>Remove</button>";
    task.querySelector("button").addEventListener("click", removeItem);

    ul.appendChild(task);
    span.textContent = toDoList.length;
  });
}

const removeItem = (e) => {
  e.target.parentNode.remove();
  const index = e.target.parentNode.dataset.key;
  toDoList.splice(index, 1);
  span.textContent = toDoList.length;
}

const searchList = (e) => {
  ul.innerHTML = "";
  if (!search.value)
    toDoList.forEach((toDoElement, key) => {
      const task = document.createElement("li");
      task.dataset.key = key++;
      task.className = "task";
      task.innerHTML = toDoElement + "<button>Remove</button>";
      task.querySelector("button").addEventListener("click", removeItem);

      ul.appendChild(task);
      span.innerHTML = toDoList.length;
    });

  else {
    const searchText = e.target.value.toLowerCase();
    let result = toDoList.filter(
      li => li.toLowerCase().includes(searchText)
    );

    console.clear();
    console.log("Search:", searchText);
    console.log("ToDoList:", toDoList);
    console.log("Results:", result);
  }
}

button.addEventListener("click", addItem);
search.addEventListener("input", searchList);
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>toDoList</title>
</head>

<body>
  <form>
    <input type="text" class="add">
    <button>Add task</button>
  </form>
  <br>
  <form>
    <label for="search_input">Search</label><br>
    <input id="search_input" type="text" class="search">
  </form>
  <h1>Num tasks: <span>0</span></h1>
  <ul></ul>
  <script src="main.js"></script>
</body>

</html>

6 Comments

Thanks for feedback and fixing the code! So you mean that because I passed toDoList array along with e, I actually overwrote the original toDoList array in the global scope?
Right, so run that function in your brain. If we initialize it with no variables, searchList() { ... }, and reference toDoList in that function, it will be referencing the global variable toDoList, which is where we're managing our data. BUT, if you define it as searchList(var1, toDoList) { ... }, and you reference toDoList, that function is looking at its local variable, which isn't what we want.
To take it a step further, you actually could make that function work even when defining a local variable with the same name - a global variable myGlobalVariable is actually, behind the scenes, window.myGlobalVariable, and you can reference it that way if you need to. I'm not trying to bog you down in the details here as much as give you a better picture of variable scoping, and why your call to toDoList returned undefined.
Copy that! What I did not understand was the whole "passing idea". I just thought that it makes the variable more accessible in the function (but it's still the same variable declared in the global scope). Now it makes sense, thanks for helping me avoiding potential mistakes in the future.
I totally get it. Time to revise where to put semicolons, then!
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.