List.js not working when using an ampersand (&) in value

703 views Asked by At

When using an ampersand, either explicitly as & or using HTML character codes as &, List.js breaks. Here's the code, working for the first filter, but not for the second:

$(function() {

  var options = {
    valueNames: ["name", "category"]
  };

  var userList = new List("search-results", options);
  var activeFilters = [];

  // filter
  $(".filter").change(function() {
    var isChecked = this.checked;
    var value = $(this).data("value");

    if (isChecked) {
      // add to list of active filters
      activeFilters.push(value);
    } else {
      // remove from active filters
      activeFilters.splice(activeFilters.indexOf(value), 1);
    }
    userList.filter(function(item) {
      if (activeFilters.length > 0) {
        return activeFilters.indexOf(item.values().category) > -1;
      }
      return true;
    });
  });

});
* {
  margin: 0;
  padding: 0;
  font-family: monaco;
}

body {
  margin: 0;
  padding: 10px;
  background: lightgreen;
}

ul {
  padding: 10px;
  list-style: none;
  color: white;
  background: blue;
}

ul span {
  margin-left: 4px
}

li {
  margin: 10px 0
}

.list {
  padding: 10px;
  background: steelblue;
}

.package {
  margin-bottom: 10px;
  background: pink;
}

.package .text {
  display: block;
}

.package .category {
  background: salmon;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/list.js/1.1.0/list.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<ul>
  <li class="checkbox-btn">
    <label>
      <input class="filter" type="checkbox" name="history" data-value="History" /><span>History</span>
    </label>
  </li>
  <li class="checkbox-btn">
    <label>
      <input class="filter" type="checkbox" name="arts & crafts" data-value="Arts & Crafts" /><span>Arts & Crafts</span>
    </label>
  </li>
</ul>

<div class="results-container">
  <div id="search-results">
    <div class="list">

      <div class="package">
        <span class="text">Placeholder Text</span>
        <span class="category">History</span>
      </div>
      
      <div class="package">
        <span class="text">Placeholder Text</span>
        <span class="category">Arts & Crafts</span>
      </div>

      <div class="package">
        <span class="text">Placeholder Text</span>
        <span class="category">History</span>
      </div>
      
      <div class="package">
        <span class="text">Placeholder Text</span>
        <span class="category">Arts & Crafts</span>
      </div>
      
    </div>
  </div>
</div>

Any ideas on how to allow an ampersand as the filtered category name? Thanks.

2

There are 2 answers

0
ariel On BEST ANSWER

The browser replaces & for &amp;, so you need to replace back on the filter function:

return activeFilters.indexOf(item.values().category.replace(/&amp;/g, "&")) > -1;
0
traktor On

It appears to be an issue with the way List.js is implemented: Searching through list.js source on GitHub one finds lots of references to element innerHTML and outerHTML properties, but no references to textContent or innerHTML properties.

Logging the item argument passed to the userList.filter function, item._values appears as

 _values: Object { name: "", category: "Arts &amp; Crafts" }

so clearly list.js is using HTML content to implement filtering, and getting (or calculating) Arts &amp; Crafts as the HTML content it is looking for in category class elements being used to filter the list.

A solution predicted to solve the problem works in practice: when processing filter button input changes, convert the text held in the data-value attribute to the "text/hmtl" format that list.js is using (instead of leaving it as "text/plain") before pushing it into the activeFilters array:

  // filter
  $(".filter").change(function() {
    var isChecked = this.checked;
    var value = $(this).data("value");
    value = value.replace("&", "&amp;");  // filter value as "text/html" 

    if (isChecked) {
      // add to list of active filters
      activeFilters.push(value);
    } else {
      // remove from active filters
      activeFilters.splice(activeFilters.indexOf(value), 1);
    }
    userList.filter(function(item) {
      if (activeFilters.length > 0) {
        return activeFilters.indexOf(item.values().category) > -1;
      }
      return true;
    });
  });