Home > database >  I am having problems trying to toggle an icon in javascript
I am having problems trying to toggle an icon in javascript

Time:10-11

I am trying to toggle my edit button to the class of hidden and my trash button to the class of show. but I am not able to target the elements I always am getting the error of NULL would anyone be able to help I am stuck.

I have tried many ways but I cant get the edit button to toggle to display none and my trash button to toggle to display flex thanks guys for any help

HTML ``` TO-DO LIST Today's To Do

  <div ></div>
  <button id="clear-btn"  type="button" 
     name="button">
    Clear all completed
    </button>
    </div>
   </body>
  </html>
   ```
const clear = document.querySelector(".clear-btn");
const list = document.querySelector(".todo-list");
const input = document.getElementById("add-input");
const form = document.getElementById("todoform");

let todos = [];
const LINE_THROUGH = "lineThrough";

form.addEventListener("submit", (e) => {
  e.preventDefault();
  saveTodo();
});

function saveTodo() {
  const todoValue = input.value;

  todos.push({
    value: todoValue,
    completed: false,
  });
  input.value = "";
  renderTodos();
}

function renderTodos() {
  list.innerHTML = "";

  todos.forEach((todo, index) => {

    list.innerHTML  = `
    <div  id=${index}>
   
    <i solid fa-check" : "regular fa-square"
    }"  data-action="check"
    ></i>
   
    <p class=${todo.checked ? LINE_THROUGH : ""}  data- 
    action="check">${todo.value}</p>
   
    
    
    <span class= "edit">
    <i class='fas fa-ellipsis-v edit-task  '   data-action="edit">
    </i>
    </span>
 
    <i  data- 
    action="delete">
    
    </i>

    </div>

    `;
  });
}

list.addEventListener("click", (event) => {
  const { target } = event;
  const parentElement = target.parentNode;
  if (parentElement.className !== "todo") return;

  const todo = parentElement;
  const todoId = Number(todo.id);

  const { action } = target.dataset;

  action === "check" && checkTodo(todoId);
  // action === 'edit' && checkTodo(todoId);
  // action === 'delete' && checkTodo(todoId);
});

function checkTodo(todoId) {
  todos = todos.map((todo, index) => ({
    ...todo,
    checked: index === todoId ? !todo.checked : todo.checked,
  }));

  renderTodos();
}
const threeVBtn = document.querySelector(".edit-task");
const trashBtn = document.querySelector(".trash-btnn");

threeVBtn.addEventListener("click", (e) => {
  trashBtn.classList.toggle("show");
  threeVBtn.classList.toggle("hidden");
});

.hidden {
  display: none;
}

.trash-btnn {
  cursor: pointer;
  color: rgb(204, 16, 47);
}

.show {
  display: flex;
}

CodePudding user response:

It is because on your first render there is no todo initially and the element specified by querySelector for threeVBtn returns undefined. One simple way is to check if the element is added to document before attaching event listener, like this:

if (threeVBtn) {
  threeVBtn.addEventListener("click", (e) => {
    trashBtn.classList.toggle("show");
    threeVBtn.classList.toggle("hidden");
  });
//perform any action for  once threeVBtn is added to Document
}  

CodePudding user response:

The problem is when you run any of the DOM selection methods (querySelector, getElementByClassName, etc), it only grabs what's currently in the DOM (ie. currently rendered html). When you add or remove items from the DOM, you must re-select elements to get the current items. It's kind of a pain, but there is a solution.

There is a concept called "event delegation" which is a technique that solves this problem. Delegation works by picking a parent element of the dynamic list - it can be any parent element so long as the element stays in the DOM while your list is dynamically changing. Many people just use the <body> since it's always guaranteed to be there. Whenever something inside the parent element is clicked, the event does this cool thing called "bubbling" - which is beyond the scope of this answer. You can read all about DOM events, bubbling, and delegation on MDN.

You can attach your click handlers to the <body> element and check the event.target for the different types of things you want, best shown by example:

document.body.addEventListener('click', (ev) => {
  const el = event.target;
  if (el.classList.contains("edit-task")) {
    // edit button was clicked
    ev.preventDefault();
    return;
  }

  if (el.classList.contains("trash-btnn")) {
    // trash button was clicked
    ev.preventDefault();
    return;
  }
});
  • Related