Filtering Todos

Selecting a Filter

The anchor elements in the navbar act as filters to display all todos, active todos, or completed todos. We will add an event listener to the navbar and create a function to handle the filter selection.

// Function to handle filter selection from the navbar
function handleClickOnNavbar(event) {
  // if the clicked element is an anchor tag
  if (event.target.tagName === "A") {
    const hrefValue = event.target.href;
    const action = hrefValue.split("/").pop();
    filter = action === "" ? "all" : action;
    console.log(filter);
  }
}

const todoNav = document.getElementById("todo-nav");
todoNav.addEventListener("click", handleClickOnNavbar);

Let’s break down the handleClickOnNavbar function:

  1. Checking the Clicked Element:

    if (event.target.tagName === "A") {
    

    We check if the clicked element is an anchor tag (<a>). This condition ensures that the function only runs when the user clicks on one of the filter links.

  2. Extracting the Filter Action:

    const hrefValue = event.target.href;
    const action = hrefValue.split("/").pop();
    filter = action === "" ? "all" : action;
    

    We extract the href attribute of the clicked anchor element (event.target.href) to get the URL. We then split the URL by the `/` character and extract the last part of the URL using pop(). This part corresponds to the filter action (e.g., all, active, or completed). If the action is an empty string, we set the filter to 'all'.

Aside: We could have added the click event to each anchor element and handled the filter selection for each. Instead, we chose to add the event listener to the parent element (todoNav) and use event delegation to handle the filter selection.

Rendering Filtered Todos

We can update the renderTodos function to filter the todos based on the current filter setting like this:

// Filter todos based on the current filter setting
let filteredTodos = [];
for (let i = 0; i < todos.length; i++) {
  const todo = todos[i];
  if (filter === "all") {
    filteredTodos.push(todo);
  } else if (filter === "completed" && todo.completed) {
    filteredTodos.push(todo);
  } else if (filter === "active" && !todo.completed) {
    filteredTodos.push(todo);
  }
}

Next we will update the loop that renders the todos to use the filteredTodos array instead of the todos array.

  // Loop through the filtered todos and add them to the DOM
- for (let i = 0; i < todos.length; i++) {
+ for (let i = 0; i < filteredTodos.length; i++) {
-   const todo = todos[i];
+   const todo = filteredTodos[i];

    // code not shown for brevity
  }

Make sure to call renderTodos after updating the filter to display the filtered todos.

  // Function to handle filter selection from the navbar
  function handleClickOnNavbar(event) {
    // if the clicked element is an anchor tag
    if (event.target.tagName === "A") {
      const hrefValue = event.target.href;
      const action = hrefValue.split("/").pop();
      filter = action === "" ? "all" : action;
-     console.log(filter);
+     renderTodos();
    }
  }

Run the app and check if the todos are filtered correctly based on the selected filter.

Styling the Active Filter

Right now the first anchor element in the navbar has an underline style, and that style is hardcoded in the HTML file. Let’s create a new function, renderTodoNavBar, to apply the style based on the selected filter instead.

// Function to update the navbar anchor elements
function renderTodoNavBar(href) {
  const elements = todoNav.children;
  for (let i = 0; i < elements.length; i++) {
    const element = elements[i];
    if (element.href === href) {
      element.classList.add(
        "underline",
        "underline-offset-4",
        "decoration-rose-800",
        "decoration-2",
      );
    } else {
      element.classList.remove(
        "underline",
        "underline-offset-4",
        "decoration-rose-800",
        "decoration-2",
      );
    }
  }
}

This function iterates over the child elements of the todoNav element (the navbar) and applies or removes the underline style based on the href value. The anchor element that matches the href value gets the underline style applied, while the others have it removed.

Now call this function after updating the filter in the handleClickOnNavbar function.

  // Function to handle filter selection from the navbar
  function handleClickOnNavbar(event) {
    // if the clicked element is an anchor tag
    if (event.target.tagName === "A") {
      const hrefValue = event.target.href;
      const action = hrefValue.split("/").pop();
      filter = action === "" ? "all" : action;
      renderTodos();
+     renderTodoNavBar(hrefValue);
    }
  }

Run the app and check if the active filter is styled correctly based on the selected filter.

Checkpoint: Commit your progress.

git add .
git commit -m "todos-08: Implement todo filtering"
git push