0

I'm creating a series of filters and it's currently "ugly code that works". I'd like to output all of the filters from an array, but I can't figure out how to assign a variable to an element's dataset attribute. (You'll see that the code is the exact same for every filter except for what they're filtering).

Can anyone help show me how I could achieve this?

function filterList() {
  const itemsNode = document.querySelectorAll(".js-filterable");
  const items = Array.from(itemsNode);

  const filterBrand = document.querySelector(".js-filter-brand");
  const filterState = document.querySelector(".js-filter-state");
  const filterCity = document.querySelector(".js-filter-city");
  const filterOwner = document.querySelector(".js-filter-owner");
  const filtered = document.querySelector(".js-filtered");

  let filterValue;
  let results;

  // Listen for filter changes

  if (filterBrand) {
    filterBrand.addEventListener("input", function(filtered, filterValue) {
      filterValue = this.value;

      if (filterValue === "all") {
        let results = items;
        outputResults(results);
      } else {
        let results = items.filter(item => item.dataset.brand === filterValue);
        outputResults(results);
      }
    });
  }

  if (filterState) {
    filterState.addEventListener("input", function(filtered, filterValue) {
      filterValue = this.value;

      if (filterValue === "all") {
        let results = items;
        outputResults(results);
      } else {
        let results = items.filter(item => item.dataset.state === filterValue);
        outputResults(results);
      }
      
    });
  }

  if (filterCity) {
    filterCity.addEventListener("input", function(filtered, filterValue) {
      filterValue = this.value;

      if (filterValue === "all") {
        let results = items;
        outputResults(results);
      } else {
        let results = items.filter(item => item.dataset.city === filterValue);
        outputResults(results);
      }
    });
  }

  if (filterOwner) {
    filterOwner.addEventListener("input", function(filtered, filterValue) {
      filterValue = this.value;

      if (filterValue === "all") {
        let results = items;
        outputResults(results);
      } else {
        let results = items.filter(item => item.dataset.owner === filterValue);
        outputResults(results);
      }
    });
  }

  // Update filtered list

  function outputResults(results) {
    while (filtered.firstChild)
      filtered.removeChild(filtered.firstChild);
    results.forEach(function(result) {
      filtered.appendChild(result);
    });
  }
}
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
Dan Denney
  • 290
  • 2
  • 14
  • 1
    This should help: [Dynamically access object property using variable](https://stackoverflow.com/q/4244896/218196) – Felix Kling Mar 03 '18 at 19:57
  • Please don't post your code to 3rd party links as those links can die over time. Just insert a "code snippet" right here in your question. Also, you should include enough of the relevant HTML and CSS as well so that we can have a running example of what you are working with and can provide you with a working answer. – Scott Marcus Mar 03 '18 at 20:00

2 Answers2

0

I imagine you could begin with something like this:

const passThru = (x) => true;

// Return a filter function using the given property name 
const filterPropName = (propName, filterValue) => filterValue === "all" ?
        passThru : 
        ({dataset}) => dataset[propName] === filterValue;

// ...

function createListener (propName) {
    return () => {
      const filterValue = this.value;
      const results = items.filter(filterPropName(propName, filterValue));
      outputResults(results);       
    }
}

if (filterBrand) {
    filterBrand.addEventListener("input", createListener("brand"));
}

if (filterState) {
    filterState.addEventListener("input", createListener("state"));
}

// Etc.
mbojko
  • 13,503
  • 1
  • 16
  • 26
0

It seems like you just want it to be DRYer:

function filterList() {

  const itemsNode     = document.querySelectorAll(".js-filterable");
  const items         = Array.from(itemsNode);
  const filterClasses = ['brand', 'state', 'city', 'owner']

  filterClasses.forEach(filterClass => {
    const filtered = document.querySelector(`.js-filter-${filterClass}`);

    if (filtered) {
      filtered.addEventListener("input", function(filtered, filterValue) {

        filterValue = this.value;         // <-- why overwrite the param?
        let results;
        if (filterValue === "all")
          results = items;
        else
          results = items.filter(item => item.dataset[filterClass] === filterValue);
          
        outputResults(results);
      });
    }
  })
}


// Update filtered list
function outputResults(results) {
  const filteredItems = document.querySelector(".js-filtered");

  while (filteredItems.firstChild)
    filteredItems.removeChild(filteredItems.firstChild);

  results.forEach(result => filteredItems.appendChild(result));
}

In particular notice:

  • filterClasses = ['brand', 'state', 'city', 'owner'] is used to iterate over the known class names
  • with item.dataset[filterClass] a variable is used to set the data value
vol7ron
  • 40,809
  • 21
  • 119
  • 172