skip to Main Content

I have a couple of elements (rows) inside a table, those rows have 2 classes (ampFilter and prod-{some_product} (depending on what product it is)).

Now what I am trying to do is get all elements with a specific class (ampFilter), which works (see example before).

I also have a couple of checkboxes, each with a value of a product.

When I check 2 checkboxes, the filterType gets filled with an array of those values.

Now when I click a button, I want to loop through the elements and check if they contain the prod-.. class(es), if not hide those rows (only showing the rows that have the prod-.. class(es)).

I currently check with a console.log what the values are, but it gets shown 8 times (2x 4 of the elements)

const filterHandler = () => {
    const filterClass = document.querySelectorAll(".ampFilter"); // I have 4 rows with this class
    filterClass.forEach((clss) => {
        console.log("clss", clss); // this shows 4 items
        filterTypes.forEach((type) => {
            console.log("type", type);  // this shows 8 times the type console
            // if (!clss.classList.contains(type)) {
            //     clss.classList.add("hidden");
            // } else {
            //     clss.classList.remove("hidden");
            // }
        });
    });
};

Here is a small example: https://codesandbox.io/p/devbox/h67y36?migrateFrom=rqkkx6

Little bit more explaining
The issue I have, is that when I uncomment the classList part. And I check both the checkboxes in the example. All the rows with those "prod-.." classes get the hidden class, except the YT product. While also the SB product should not have the hidden class.

2

Answers


  1. If you use, React.Strict – the “problem” in React.Strict mode. If you don’t want to see doubled console.log’ you can remove it.

    You can read more about this future here.

    Login or Signup to reply.
  2. All the rows with those "prod-.." classes get the hidden class, except the YT product. While also the SB product should not have the hidden class.

    Now we’re getting to the actual problem. It’s not React, not logging to the console, none of that. Consider the logic being used here:

    filterClass.forEach((clss) => {
      // looping through every target element
      filterTypes.forEach((type) => {
        // looping through every filter type
        if (!clss.classList.contains(type)) {
          clss.classList.add("hidden");
        } else {
          clss.classList.remove("hidden");
        }
      });
    });
    

    So what happens when you have both the "SB" and the "YT" filters selected? When the outer loop is on the "SB" element and the inner loop iterates over the filters, if "YT" was selected second then that last iteration of the inner loop is going to hide the "prod-sb" element, because the class list of that element doesn’t contain "prod-yt".

    Take a step back.

    Instead of showing/hiding in the loop, another approach would be to initially hide everything and then conditionally show in the loop. Which would turn out to be simpler anyway. First, apply "hidden" to all target elements, then loop over the filter types and remove "hidden" from any matching elements for each filter.

    But, take a bigger step back.

    This is React. Why are you manually manipulating the DOM in the first place? These events should just update state, and state should be used in the rendering to conditionally show/hide elements.

    To that end, you don’t even need the button in the first place. The button and its handler function can just be removed. You’re already updating the state of the filter when using the checkboxes. All you need to do is use that state in your rendering. For example:

    <table>
      <tbody>
        <tr className={`ampFilter prod-sb ${(filterTypes.includes("prod-sb") ? "" : "hidden")}`}>
          <td>SB</td>
        </tr>
        <tr className={`ampFilter prod-td ${(filterTypes.includes("prod-td") ? "" : "hidden")}`}>
          <td>TD</td>
        </tr>
        <tr className={`ampFilter prod-lb ${(filterTypes.includes("prod-lb") ? "" : "hidden")}`}>
          <td>LB</td>
        </tr>
        <tr className={`ampFilter prod-yt ${(filterTypes.includes("prod-yt") ? "" : "hidden")}`}>
          <td>YT</td>
        </tr>
      </tbody>
    </table>
    

    You could re-add the button if you want that specific user experience. In that case I suppose you’d create a second state value which conditionally determines if the filter should be applied or not (a simple boolean value) and the button would just update that state, and that boolean value could be used in the ternary conditions above. But that sounds a bit clunky to be honest, I don’t think the button is needed here.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search