CSS 或 JS 中的简单过滤器
在本文中,我想演示如何为网站添加一个简单灵活的过滤解决方案。用例是,我有一组文物——在我的案例中是作品集项目,但在这里我们将其简化为动物——我希望能够:
- 通过单击按钮(或 div 等)进行过滤
- 无需更改任何代码即可轻松将新项目添加到集合中。
我将探讨将相同过滤器应用于相同数据的两种不同方法,一种基于 JavaScript,另一种仅基于 CSS。
让我们首先创建过滤器和动物集合的 html,我们将过滤器表示为按钮并为每种动物创建一个 div:
<div class="filters">
<h3>Filters</h3>
<button class="filter-option">Walks</button>
<button class="filter-option">Swims</button>
<button class="filter-option">Flies</button>
<button class="filter-option">All</button>
</div>
<div class="list">
<h3>Animals</h3>
<div class="dog">Dog</div>
<div class="eagle">Eagle</div>
<div class="cow">Cow</div>
<div class="shark">Shark</div>
<div class="canary">Canary</div>
<div class="human">Human</div>
<div class="salamander">Salamander</div>
</div>
JS 过滤器 - 更传统的方式
当然,使用 JavaScript 进行过滤的方法有很多。为此,我希望确保它足够灵活,能够覆盖我之后添加的任何内容,因为我不想再回来编辑 JS 函数。为此,我知道我需要一种方法来识别每个过滤器需要包含/排除哪些动物,并且我希望 HTML 能够完成大部分繁重的工作,这样我就可以仅通过添加 HTML 来添加到集合中。
HTML
首先,我将为每个动物 div 添加一个类,并添加相应滤镜的名称。这将是我的标识符。
<div class="list">
<h3>Animals</h3>
<div class="dog walks">Dog</div>
<div class="eagle flies">Eagle</div>
<div class="cow walks">Cow</div>
<div class="shark swims">Shark</div>
<div class="canary flies">Canary</div>
<div class="human walks">Human</div>
<div class="salamander swims walks">Salamander</div>
</div>
注意,最后一个项目“蝾螈”可以行走或游泳。我们需要确保过滤函数能够处理符合多个条件的项目。
接下来,我还知道需要为每个过滤器添加一个事件监听器来调用我的 JS 函数。我还想通过某种方式将过滤器的值传递给函数。我们可以像这样编写事件监听器调用,onclick="filterAnimals('walks')"
但如果能够在其他代码中获取过滤器的值可能更好,那么我们不妨将值作为 HTMLdata-
属性,并在函数中使用它。这样做还有一个额外的好处,那就是使代码更具可读性。
<div class="filters">
<h3>Filters</h3>
<button class="filter-option" data-filter="walks" onclick=filterAnimals(event)>Walks</button>
<button class="filter-option" data-filter="swims" onclick=filterAnimals(event)>Swims</button>
<button class="filter-option" data-filter="flies" onclick=filterAnimals(event)>Flies</button>
<button class="filter-option" data-filter="*" onclick=filterAnimals(event)>All</button>
</div>
CSS
现在是时候确定如何实际获取要过滤的项目了。在 CSS 中,我们可以通过将其设置为 来从页面中删除元素display: none
。让我们创建一个具有该设置的类,这样我们的 JS 代码就可以根据需要轻松地添加/删除该类……嗯,这很简单。
.hidden {
display: none;
}
JavaScript
还剩下什么呢?当我们选择一个过滤器时,JavaScript 只需要遍历所有动物,看看它们是否包含该过滤器的类。如果包含,则不应该获取该类.hidden
;如果不包含,则添加该类。
function filterAnimals(e) {
const animals = document.querySelectorAll(".list div"); // select all animal divs
let filter = e.target.dataset.filter; // grab the value in the event target's data-filter attribute
animals.forEach(animal => {
animal.classList.contains(filter) // does the animal have the filter in its class list?
? animal.classList.remove('hidden') // if yes, make sure .hidden is not applied
: animal.classList.add('hidden'); // if no, apply .hidden
});
};
太棒了!现在我们的过滤器应该可以正常工作了,我们来看一下。
![]() |
![]() |
![]() |
![]() |
注意到我们的“麻烦制造者”蝾螈确实出现在了“散步”和“游泳”过滤器中,这真是个好消息!然而,看看“全部”过滤器……就不太妙了。我们知道有数据,所以肯定应该有数据,对吧?可惜的是,我们的任何工件中都没有.all
类,所以这个过滤器匹配不到任何东西。我们可以.all
给每种动物都添加一个类,但用 JavaScript 来处理会更简洁、更容易。我们只需要一个 if/else 语句来判断过滤器是“全部”还是更具体的类型:
// code to add:
if (filter === '*') {
animals.forEach(animal => animal.classList.remove('hidden'));
}
// full JS code:
function filterAnimals(e) {
const animals = document.querySelectorAll(".list div");
let filter = e.target.dataset.filter;
if (filter === '*') {
animals.forEach(animal => animal.classList.remove('hidden'));
} else {
animals.forEach(animal => {
animal.classList.contains(filter) ?
animal.classList.remove('hidden') :
animal.classList.add('hidden');
});
};
};
好了,现在一切就绪。如果我们以后想添加一些东西,比如鸵鸟,只需添加一行 HTML 代码即可:<div class="ostrich walks">Ostrich</div>
其他一切都像变魔术一样自动帮我们搞定了。
CSS 过滤器
让我们看看如何实现同样的事情,但完全不使用任何 JavaScript。这需要用到一个巧妙的 CSS 技巧!
HTML
首先,我们不再需要任何事件监听器了,因为没有 JS 函数可以调用,所以就先把它们删掉吧。除此之外,一切都一样。
<div class="filters">
<h3>Filters</h3>
<button class="filter-option" data-filter="walks">Walks</button>
<button class="filter-option" data-filter="swims">Swims</button>
<button class="filter-option" data-filter="flies">Flies</button>
<button class="filter-option" data-filter="*">All</button>
</div>
<div class="list">
<h3>Animals</h3>
<div class="dog walks">Dog</div>
<div class="eagle flies">Eagle</div>
<div class="cow walks">Cow</div>
<div class="shark swims">Shark</div>
<div class="canary flies">Canary</div>
<div class="human walks">Human</div>
<div class="salamander swims walks">Salamander</div>
</div>
CSS
但是 CSS 如何主动为我们过滤呢?这个问题的关键在于“主动”。当用户点击一个按钮时,该按钮会一直处于焦点状态,直到用户点击其他按钮。因此,我们可以利用这一点,:focus
为每个按钮添加一个选择器。我们还可以data-
使用 CSS 访问属性,以确定当某个按钮处于焦点状态时应用哪个滤镜。button[data-filter="walks"]:focus
我们还知道我们需要过滤掉的动物来获得该display: none
属性。
button[data-filter="walks"]:focus {
display:none;
}
但挑战在于,当按钮处于焦点时,如何真正选中~
动物而不是按钮呢?我们可以使用“同级元素之后的元素”来选择。这正式被称为“通用兄弟组合器”。更多信息请点击此处。
唯一的问题是,这需要动物和过滤器共享一个父元素,而它们目前还没有共享父元素,所以我们需要对 HTML 进行微小的更新,通过将所有内容组合在一个 div 下来实现这一点,让我们给它一个.filteredList
类。
完成这项更改后,我们现在可以使用~
来选择“与所选按钮共享同一父级的 div,其类包含按钮的 data-filter 属性值”。如下所示(*=
表示“包含”,=
需要完全匹配):
button[data-filter="walks"]:focus ~ div:not([class*="walks"]) {
display:none;
}
button[data-filter="swims"]:focus ~ div:not([class*="swims"]) {
display:none;
}
button[data-filter="flies"]:focus ~ div:not([class*="flies"]) {
display:none;
}
JavaScript
没有 JavaScript - 哇喔!
完整代码
// HTML
<div class="filteredList">
<h3>Filters</h3>
<button class="filter-option" data-filter="walks" tabindex="-1">Walks</button>
<button class="filter-option" data-filter="swims" tabindex="-1">Swims</button>
<button class="filter-option" data-filter="flies" tabindex="-1">Flies</button>
<button class="filter-option" data-filter="*" tabindex="-1">All</button>
<h3>Animals</h3>
<div class="dog walks">Dog</div>
<div class="eagle flies">Eagle</div>
<div class="cow walks">Cow</div>
<div class="shark swims">Shark</div>
<div class="canary flies">Canary</div>
<div class="human walks">Human</div>
<div class="salamander swims walks">Salamander</div>
</div>
//CSS
button[data-filter="walks"]:focus ~ div:not([class*="walks"]) {
display:none;
}
button[data-filter="swims"]:focus ~ div:not([class*="swims"]) {
display:none;
}
button[data-filter="flies"]:focus ~ div:not([class*="flies"]) {
display:none;
}
现在是关键时刻,它有效吗?
![]() |
![]() |
![]() |
![]() |
成功了!!记住,如果你点击页面上的其他任何地方,滤镜都会被移除(因为按钮失焦了)。最后,我们如何添加新的鸵鸟呢?方法一模一样:<div class="ostrich walks">Ostrich</div>
总的来说,JavaScript 函数在几乎所有情况下可能都是更好的选择,但我认为这是个很酷的 CSS 技巧,如果你想要一个轻量级的快速过滤功能,它可能会很有用。
请在评论区告诉我你的想法。