【问题标题】:jQuery Filter Show Data Attribute Any or AlljQuery过滤器显示数据属性任意或全部
【发布时间】:2016-11-29 11:13:39
【问题描述】:

我有一些下拉菜单,用户可以使用这些下拉菜单filter() 下面的 div。这些按钮引用三个单独的数据属性。单击按钮时,会将值添加到数组中,然后我根据这些数组中的值过滤内容。虽然我只有两个下拉菜单,但我运行了四个场景来测试数组是否为空:如果它是空的,我只是测试数据属性的存在,如果有一个值,那么我用它来过滤。

添加第三个过滤器后,我(我认为)有 9 种可能的场景可供测试,并想知道是否有更好的方法来做到这一点。

例如,当我只有两个过滤器时,我的数组可能如下所示:

["united-kingdom", "ireland"] //country
[] //type

var results = $(".collaborator[data-country][data-collaborator_type][data-interest]").filter(function() {
    var $this = $(this);

    if( countryArr.length !== 0 && collaborator_typeArr.length !== 0 ) {
        return countryArr.includes($this.attr("data-country")) && collaborator_typeArr.includes($this.attr("data-collaborator_type"));

    } else if( countryArr.length === 0 && collaborator_typeArr.length === 0 ) {
        return $this.attr('data-country') && $this.attr('data-collaborator_type');

    } else if( countryArr.length === 0 ) {
        console.log('any country');
        return $this.attr('data-country') && collaborator_typeArr.includes($this.attr("data-collaborator_type"));

    } else if( collaborator_typeArr.length === 0 ) {
        console.log('any type');
        return countryArr.includes($this.attr("data-country")) && $this.attr('data-collaborator_type');
    }
});

$('section.collaborator').slideUp('fast');
results.slideDown('fast');

这将在英国和爱尔兰返回任何类型的结果。

我想不出是实现“任何”价值的更好方法。有没有一种方法可以测试“任何”或通配符值,而不是检查数组是否为空。

如果我的数组看起来像这样会怎样

["united-kingdom", "ireland"] //country
["*"] //type

如何过滤以返回所有通配符过滤器?

【问题讨论】:

  • 能否显示相关的“minimal reproducible example” HTML,以便我们轻松重现您的问题?看着你发布的 jQuery,我忍不住想,一定有一个更简单的解决方案,但没有 HTML——也许还有 CSS——这是一个困难的理论练习。
  • 嗨@DavidThomas - 我做了一个小提琴(我的第一个):jsfiddle.net/nugerama/4yychd87
  • 您是否需要坚持使用相同的 HTML,或者可以对其进行修改?我正在考虑,具体来说,更改一些 data-* 属性(或者可能添加一些以使其更容易,您的用例或项目要求是否允许这样做?
  • 哦,是的,@DavidThomas - 这是我所有的代码,所以请提出任何建议,我会处理它们。

标签: javascript jquery arrays wildcard


【解决方案1】:

因为我自己的脚本风格使我能够制作一些可适度扩展的东西,而不必附加案例——无论这些案例可能是简单还是复杂——我改变了脚本中采用的方法。

我还在您的 HTML 中添加了几个组件,尽管您可以根据需要删除这些组件。

也就是说,我采用的方法如下,我尝试在 cmets 中完全解释代码(即使是原始代码中存在的那些部分,也有利于那些将来可能查看问题和答案的人) ):

// delegating the event handler to the '#network-filters'
// element, binding the anonymous function of the on()
// method to act as the 'click' event-handler for the
// click events on those elements contained within that
// match the 'a.filter' selector:
$('#network-filters').on('click', 'a.filter', function(event) {

  // preventing the default action of the <a> elements,
  // preventing the page from following the clicked links:
  event.preventDefault();

  // using the 'let' statement to declare variables, this is
  // possible in ES6-supporting browsers, for older browsers
  // you may have to use 'var' instead (in this situation
  // they're interchangeable).

  // caching the clicked element:
  let clicked = $(this),

    // finding the elements that contain the elements to
    // be filtered (I added the '#results' element):
    results = $('#results .collaborator');

  // toggling the 'toggled' class, to turn it 'on' or 'off'
  // on the clicked <a>, and removing it from the sibling
  // elements:
  clicked.toggleClass('toggled').siblings().removeClass('toggled');

  // caching '.dropdown' elements:
  let dropdowns = $('#network-filters .dropdown'),

  // declaring 'empty' variables for use within
  // the up-coming 'Array.prototype.map()' call:
    filter,
    active,
    filterValue,

  // here we convert the dropdowns jQuery collection into
  // a native JavaScript Array using get(), and then we
  // iterate over that Array using Array.prototype.map():
    selector = dropdowns.get().map(function(dropdown) {
      // 'dropdown' a reference to the current element
      // in the Array of elements over which we're
      // iterating.

      // assigning values to the declared 'empty' variables:
        // here we retrieve the value of the 'data-tax'
        // custom attribute, using the Element.dataset API:
      filter = dropdown.dataset.tax;

        // here we retrieve the element matching the supplied
        // CSS selector contained within the current
        // dropdown element, querySelector returns the first
        // or no matching elements:
      active = dropdown.querySelector('a.filter.toggled');

        // if we have an 'active' element we pass the value
        // of its 'data-filter' attribute to the variable,
        // otherwise we return false:
      filterValue = active ? active.dataset.filter : false;

      // if there was an active element:
      if (active) {
        // we construct a selector to return to the Array
        // we're creating via Array.prototype.map(), that
        // selector takes the form of:
          // 'data-' plus the attribute-value of the
          // 'data-tax' attribute, followed by either an
          // empty string (if the filterValue is exactly
          // equal to 'any') or a string containing '='
          // follwed by the found filterValue; this is
          // all then followed by a ']' character to
          // close the formed attribute-selector:
        return '[data-' + filter + (filterValue === 'any' ? '' : '=' + filterValue) + ']';

      // otherwise:
      } else {
        // we simply return false, if there was no active
        // element:
        return false;
      }

    // we then use Array.prototype.filter(Boolean) to retain
    // only those true/truthy values in the Array
    }).filter(Boolean)

    // and join all array-elements together with an empty string:
    .join('');

  // those results (the '#results .collaborator' elements) are
  // then filtered; those that do not match the created selector
  // are hidden via the slideUp() method:
  results.not(selector).slideUp();

  // then we filter the results array to find those that
  // are hidden (.not(':visible') - using the ':visible'
  // jQuery/Sizzle custom selector) which match the
  // formed selector and apply slideDown() to those
  // elements; this is simply to avoid sliding elements
  // both up and down if they're already visible:
  results.not(':visible').filter(selector).slideDown();

  // here we use a comparison to discover whether or not
  // we have any results; if the number of results elements
  // filtered by the selector is greater than 0, we return
  // true (because we have found some results), otherwise
  // we return false (because there were no matching results):
  let resultsAvailable = results.filter(selector).length > 0;

  // here we find the element (added by me to your HTML) which
  // conveys the 'result-state', and toggle the 'noResult'
  // class using the switch, in which we invert the
  // resultsAvailble variable, so that the class is added if
  // there are no results available, and removed if there are
  // results available (its default state is to be hidden,
  // and shown only if there are no results, this is in the CSS):
  $('#results .resultState').toggleClass('noResult', !resultsAvailable);

});

上面的 JavaScript 加上下面的 HTML:

<div id="network-filters">
  <div class="dropdown" data-tax="country">
    <div class="dropdown-content">
      <a class="filter all" data-filter="any">Show All</a>
      <a class="filter" data-filter="ireland">Ireland</a>
      <a class="filter" data-filter="united-kingdom">United Kingdom</a>

      <!-- the following two <a> elements are added to show how
           easily this approach can be extended; note that there
           is no matching element for the 'Scotland' <a>, to
           demonstrate that the script still works despite that -->
      <a class="filter" data-filter="wales">Wales</a>
      <a class="filter" data-filter="scotland">Scotland</a>
    </div>
  </div>
  <div class="dropdown" data-tax="collaborator">
    <div class="dropdown-content">
      <a class="filter all" data-filter="any">Show All</a>
      <a class="filter" data-filter="artist">Artist</a>
      <a class="filter" data-filter="partner">Partner</a>
    </div>
  </div>
</div>

<!-- the '#results' element was added to logically group the
     elements to be searched together -->
<div id="results">

  <!-- the 'resultState' element was added to visibly inform
       the user that the search was conducted and the lack of
       results is deliberate, rather than the result of a
       broken script -->
  <div class="resultState">No results available for that choice.</div>
  <div class="collaborator" data-country="ireland" data-collaborator="artist">
    <ul>
      <li>country = ireland</li>
      <li>type = artist</li>
    </ul>
  </div>
  <div class="collaborator" data-country="united-kingdom" data-collaborator="partner">
    <ul>
      <li>country = united-kingdom</li>
      <li>type = partner</li>
    </ul>
  </div>

  <!-- the following element was effectively cloned 
       from the same format as the other elements, 
       and dropped in justas a demonstration of how 
       easily this approach can be extended -->
  <div class="collaborator" data-country="wales" data-collaborator="partner">
    <ul>
      <li>country = wales</li>
      <li>type = partner</li>
    </ul>
  </div>
</div>

$('#network-filters').on('click', 'a.filter', function(event) {
  event.preventDefault();

  let clicked = $(this),
    results = $('#results .collaborator');

  clicked.toggleClass('toggled').siblings().removeClass('toggled');

  let dropdowns = $('#network-filters .dropdown'),
    filter,
    active,
    filterValue,
    selector = dropdowns.get().map(function(dropdown) {
      filter = dropdown.dataset.tax;
      active = dropdown.querySelector('a.filter.toggled');
      filterValue = active ? active.dataset.filter : false;

      if (active) {
        return '[data-' + filter + (filterValue === 'any' ? '' : '=' + filterValue) + ']';
      } else {
        return false;
      }
    }).filter(Boolean).join('');

  results.not(selector).slideUp();
  results.not(':visible').filter(selector).slideDown();

  let resultsAvailable = results.filter(selector).length > 0;

  $('#results .resultState').toggleClass('noResult', !resultsAvailable);

});
/* SCSS from JS Fiddle demo (https://jsfiddle.net/davidThomas/5c08shbn/1/)
compiled to CSS via 'http://www.sassmeister.com/' */

.dropdown {
  margin-bottom: 10px;
}
.dropdown a {
  display: inline-block;
  cursor: pointer;
  border: 1px solid grey;
  margin-bottom: 4px;
  padding: 4px 6px;
}
.dropdown a:hover {
  background-color: grey;
}
.dropdown a.toggled {
  /* I just prefer the brighter colour */
  background-color: limegreen;
}
.collaborator {
  background-color: red;
  border: 1px solid red;
  margin-bottom: 10px;
  padding: 4px 6px;
  color: white;
}
.resultState {
  height: 2em;
  line-height: 2em;
  opacity: 0;
  transition: all 0.3s linear;
}
.resultState.noResult {
  opacity: 1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="network-filters">
  <div class="dropdown" data-tax="country">
    <div class="dropdown-content">
      <a class="filter all" data-filter="any">Show All</a>
      <a class="filter" data-filter="ireland">Ireland</a>
      <a class="filter" data-filter="united-kingdom">United Kingdom</a>
      <a class="filter" data-filter="wales">Wales</a>
      <a class="filter" data-filter="scotland">Scotland</a>
    </div>
  </div>
  <div class="dropdown" data-tax="collaborator">
    <div class="dropdown-content">
      <a class="filter all" data-filter="any">Show All</a>
      <a class="filter" data-filter="artist">Artist</a>
      <a class="filter" data-filter="partner">Partner</a>
    </div>
  </div>
</div>
<div id="results">
  <div class="resultState">No results available for that choice.</div>
  <div class="collaborator" data-country="ireland" data-collaborator="artist">
    <ul>
      <li>country = ireland</li>
      <li>type = artist</li>
    </ul>
  </div>
  <div class="collaborator" data-country="united-kingdom" data-collaborator="partner">
    <ul>
      <li>country = united-kingdom</li>
      <li>type = partner</li>
    </ul>
  </div>
  <div class="collaborator" data-country="wales" data-collaborator="partner">
    <ul>
      <li>country = wales</li>
      <li>type = partner</li>
    </ul>
  </div>
</div>

JS Fiddle demo.

参考资料:

【讨论】:

  • 哇,谢谢@David Thomas。我现在要处理你的代码。首先,我快速浏览了一下小提琴,可以看到不可能选择多个过滤器值。要求能够选择,例如:“英国”或“爱尔兰”中的“合作伙伴”。如果你在你的例子中允许这样做,我没有说清楚,我很抱歉。
  • 你真的很受欢迎;至于你的观察:不,遗憾的是我没有做到这一点,我可能误解了你问题的几个方面。如果您可以看一下,如果我遗漏了您需要的任何其他内容,请告诉我,我会再看一下以使其正常工作:)
  • 太棒了,谢谢@David Thomas。我已经查看并调整了您的代码,看看如果我没有在单击时取消切换相邻的过滤器按钮会发生什么,但我发现它并不那么简单:o。这就是为什么我发现自己走上了为每个单独的分类添加和删除数组的路线,然后在最后将它们组合起来。例如,在您的小提琴上,如果我为国家选择“全部显示”,为角色选择“艺术家”和“合作伙伴”,那么我应该得到所有结果(“任何”国家/地区的 1 位艺术家和 2 位合作伙伴)。这有意义吗?
  • 是的,这完全可以理解(或者至少我认为我清楚地理解它......)。给我一点时间,虽然根据其他任务可能需要几个小时,以更新一些东西:)
  • 澄清一下,这些国家也一样吗?您是否可以选择爱尔兰和英国合作伙伴和艺术家?
猜你喜欢
  • 1970-01-01
  • 2013-10-02
  • 1970-01-01
  • 2018-04-21
  • 2019-06-14
  • 1970-01-01
  • 2021-09-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多