-1

I am working on a to-do list project. The problem is if someone wants to filter the list by clicking on the buttons active, completed and all applications will not work until you click on the refresh button in the browser before that. In this app, I'm also using localStorage. Can someone check the code and tell me why is this happening, please?

'use strict';

const tabInput = document.querySelector('.tab-input');
const input = document.getElementById('input');
const todosUl = document.querySelector('.todos');
const clearBtn = document.querySelector('.span-right');
const counterItem = document.querySelector('.number');
const body = document.getElementById('body');
const light = document.querySelector('.light');
const icon = document.querySelector('.todo-icon');


// filter 1

const buttons = document.querySelector('.btn-grup');
const filters = document.querySelectorAll('.button--filter');

function btnClicked() {
  buttons.addEventListener('click', (e) => {
    const clicked = e.target;

    if (!clicked) return;

    filters.forEach((filter) => filter.classList.remove('active'));

    clicked.classList.add('active');
  });
}

// filter

let todosEl = document.querySelectorAll('li');
// completed
function filterCompleted() {
  todosEl.forEach((el) => {
    if (!el.classList.contains('completed')) {
      el.style.display = 'none';
    } else {
      el.style.display = 'block';
    }
  });
}

// active
function filterActive() {
  todosEl.forEach((el) => {
    if (el.classList.contains('completed')) {
      el.style.display = 'none';
    } else {
      el.style.display = 'block';
    }
  });
}
// all
function filterAll() {
  let todosEl = document.querySelectorAll('li');
  todosEl.forEach((el) => {
    if (el || el.classList.contains('completed')) {
      el.style.display = 'block';
    }
  });
}
// btn clear
clearBtn.addEventListener('click', (e) => {
  localStorage.clear();
  location.reload();
});

HTML

<form class="tab-input" id="tab-input">
          <input
            type="text"
            class="input"
            id="input"
            placeholder="Create a new todo..."
            autocomplete="off"
          />
          <ul class="todos" id="todos"></ul>
          <div class="item-info">
            <span class="span-left"
              ><span class="number"></span> items left</span
            >
            <span class="span-right">Clear</span>
          </div>
          <div class="btn-grup">
            <button
              class="button--filter active"
              onclick="filterAll(); btnClicked()"
              id="filter-all"
              type="button"
            >
              All
            </button>
            <button
              class="button--filter"
              onclick="filterActive(); btnClicked()"
              id="filter-active"
              type="button"
            >
              Active
            </button>
            <button
              class="button--filter"
              onclick="filterCompleted(); btnClicked()"
              id="filter-completed"
              type="button"
            >
              Completed
            </button>
          </div>
        </form>
  • 3
    This is a lot of JS-code without any HTML and no clear explanation of where the problem is. You can increase your chances of getting some help if you put together a https://stackoverflow.com/help/minimal-reproducible-example in which you focus on *one* problem at a time. – Carsten Massmann Jan 09 '22 at 13:51
  • 1
    Chances are, a large portion of the code you've shared is not relevant to reproducing the bug: see how to create a [mcve]. – Terry Jan 09 '22 at 13:58
  • You write all the tasks, put them on the list and mark one or two of them. When you click the active button, it should only select tasks that are still active without completed ones. This function will not be performed until the refresh button in the bronzer is clicked. And that is my only problem here for which I asked. – Nikola Maricic Jan 09 '22 at 14:00
  • Is it better code now? – Nikola Maricic Jan 09 '22 at 14:04

2 Answers2

1

The problem is with your todosEl object. You initialize it at the start of the program as a global variable and it's never updated again, so your filterActive and filterCompleted functions work from that empty list forever more. The filterAll function declares the variable again in its own scope, which is what all of the filter functions should do. Since the list items get changed as you use the program (adding or setting complete/active) you must get the list each time you're filtering. Change your filter functions like this and it should work:

// let todosEl = document.querySelectorAll('li'); <--- delete this global var
// completed
function filterCompleted() {
  const todosEl = document.querySelectorAll('li');  <--- get it fresh each time
  todosEl.forEach((el) => {
    if (!el.classList.contains('completed')) {
      el.style.display = 'none';
    } else {
      el.style.display = 'block';
    }
  });
}
// active
function filterActive() {
  const todosEl = document.querySelectorAll('li');  <--- get it fresh each time
  todosEl.forEach((el) => {
    if (el.classList.contains('completed')) {
      el.style.display = 'none';
    } else {
      el.style.display = 'block';
    }
  });
}
// all
function filterAll() {
  const todosEl = document.querySelectorAll('li');  <--- get it fresh each time
  todosEl.forEach((el) => {
    if (el || el.classList.contains('completed')) {
      el.style.display = 'block';
    }
  });
}

I also declared todoesEl as const since it doesn't need to change.

Always Learning
  • 5,510
  • 2
  • 17
  • 34
  • OK to be more specific, here are the URLs for my code: https://github.com/dzoni19/todo and URLs for how it looks like: https://dzoni19.github.io/todo/ so if you clicked on the buttons completed and active nothing works. but when you click on the refresh button on the browser everything works fine. That is the problem. click event works fine you can see in console but on the screen no. after refreshing the browser everythings works fine – Nikola Maricic Jan 09 '22 at 14:44
  • ok - I see the problem now - giving it some thought – Always Learning Jan 09 '22 at 15:01
  • Always Learning thx for understanding and your patience. – Nikola Maricic Jan 09 '22 at 15:08
  • I've changed my answer - hope that does the trick for you – Always Learning Jan 09 '22 at 15:10
  • Always Learning I don't know how to mention your nickname here but anyway thank you a lot for your answer and help, it works like that. Thx! – Nikola Maricic Jan 09 '22 at 15:19
  • cool - glad it helped. To mention a nickname type the at-sign and start typing the name. A popup should show and you can click on it to insert. @NikolaMaricic – Always Learning Jan 09 '22 at 15:27
  • once again thank you a lot for your time and patience, you helped me a lot :) – Nikola Maricic Jan 09 '22 at 15:33
-2

HTML

 <button
         class="button--filter active"
         onclick="filterAll(); btnClicked()"
         id="filter-all"
         type="button"
>

Javascript

function btnClicked() {
  buttons.addEventListener('click', (e) => {
    const clicked = e.target;

    if (!clicked) return;

    filters.forEach((filter) => filter.classList.remove('active'));

    clicked.classList.add('active');
  });
}

This above looks weird. Every time you click on a button, you create a eventlistener on buttons (a collection of nodes). Try onclick="btnClicked(this)" on the button and rewrite the method to the following:

function btnClicked(clicked) {
    filters.forEach((filter) => filter.classList.remove('active'));

    clicked.classList.add('active');
  });
}
Rickard Elimää
  • 7,107
  • 3
  • 14
  • 30
  • [Why is using onClick() in HTML a bad practice?](https://stackoverflow.com/questions/5871640/why-is-using-onclick-in-html-a-bad-practice) – jabaa Jan 09 '22 at 14:07
  • @jabaa I'm not going into length of explaining why certain code is bad, when there is that much code. It would just make the question more complex than it's already is. – Rickard Elimää Jan 09 '22 at 14:10
  • You had two options to solve the problem. You could completely remove the `onclick` in the markup and add the click listeners on page load, or you could remove the click listeners. IMHO, you choose the much worse option. – jabaa Jan 09 '22 at 14:13
  • @jabaa Agreed. However, just read my previous comment. – Rickard Elimää Jan 09 '22 at 14:14
  • So you prefer to give a simple, but bad answer instead of a less simple, but good answer? IMHO the other solution isn't even more complex. OP already knows about event listeners and uses them. You could show them how to use them correctly. – jabaa Jan 09 '22 at 14:16
  • 1
    @jabaa I prefer to give a simple suggestion to a total newbie. I also don't explain how microwave works when someone asks me how to set it's clock. – Rickard Elimää Jan 09 '22 at 14:18
  • 1
    OP has event listeners in the question. You replaced the good (but wrong) code in the question with bad code. – jabaa Jan 09 '22 at 14:18
  • @jabaa When the OP is more experienced, that person will know about why you shouldn't use onClick. In due time... – Rickard Elimää Jan 09 '22 at 14:23