假设您有一个包含 100 个名称的列表
<ul>
<li>Randy Hilpert</li>
<li>Peggie Jacobi</li>
<li>Ethelyn Nolan Sr.</li>
<!-- and then some -->
</ul>
…或者文件名、电话号码,或任何其他内容。您想在客户端对它们进行过滤,这意味着您不会向服务器发出请求来搜索数据并返回结果。您只需键入“rand”,它就会将列表过滤为包含“Randy Hilpert”和“Danika Randall”,因为它们都包含该字符串。列表中所有其他名称都不会出现在结果中。
让我们看看如何使用不同的技术来做到这一点。
CSS 可以做到,不过需要一些帮助。
CSS 无法根据内容选择元素,但它可以根据属性及其值进行选择。因此,我们将名称也移到属性中。
<ul>
<li data-name="Randy Hilpert">Randy Hilpert</li>
<li data-name="Peggie Jacobi">Peggie Jacobi</li>
<li data-name="Ethelyn Nolan Sr.">Ethelyn Nolan Sr.</li>
...
</ul>
现在,要过滤包含“rand”的名称列表,这非常容易
li {
display: none;
}
li[data-name*="rand" i] {
display: list-item;
}
请注意第 4 行的 i
。它表示“不区分大小写”,这对这种情况非常有用。
要使用过滤器 <input>
使其动态工作,我们需要让 JavaScript 参与进来,不仅要对过滤器输入进行响应,还要生成与搜索内容匹配的 CSS。
假设我们在页面上有一个 <style>
块
<style id="cssFilter">
/* dynamically generated CSS will be put in here */
</style>
我们可以监视过滤器输入的变化并生成该 CSS
filterElement.addEventListener("input", e => {
let filter = e.target.value;
let css = filter ? `
li {
display: none;
}
li[data-name*="${filter}" i] {
display: list-item;
}
` : ``;
window.cssFilter.innerHTML = css;
});
请注意,当过滤器为空时,我们清空了样式块,因此显示所有结果。
查看 CodePen 上的示例
过滤技术:CSS,由 Chris Coyier (@chriscoyier) 创建。
在 CodePen 上。
我承认利用 CSS 来做这件事有点奇怪,但 Tim Carry 曾经把它做得更进一步,如果您有兴趣了解这个概念,可以看看。
jQuery 使其变得更加容易。
既然我们无论如何都需要 JavaScript,jQuery 可能是一个可接受的工具。这里有两个显著的变化
- jQuery 可以根据内容选择项目。它有一个专门用于此的 selector API。我们不再需要额外的属性了。
- 这将所有过滤操作集中在单一技术中。
我们仍然监视输入的键入操作,然后如果有过滤项,我们将隐藏所有列表项并显示包含过滤项的项。否则,我们将再次显示所有项。
const listItems = $("li");
$("#filter").on("input", function() {
let filter = $(this).val();
if (filter) {
listItems.hide();
$(`li:contains('${filter}')`).show();
} else {
listItems.show();
}
});
与 CSS 相比,使过滤器不区分大小写需要更多调整,但我们可以通过覆盖默认方法来实现。
jQuery.expr[':'].contains = function(a, i, m) {
return jQuery(a).text().toUpperCase()
.indexOf(m[3].toUpperCase()) >= 0;
};
查看 CodePen 上的示例
过滤技术:jQuery,由 Chris Coyier (@chriscoyier) 创建。
在 CodePen 上。
React 可以通过状态和仅渲染所需内容来实现。
在 React 中没有一种绝对正确的方法来做到这一点,但我认为,将名称列表作为数据(例如数组)保存,并对它们进行映射,然后仅渲染所需内容是 React 式的做法。输入中的更改会过滤数据本身,React 会根据需要重新渲染。
如果我们有 names = [array, of, names]
,我们可以轻松地对其进行过滤。
filteredNames = names.filter(name => {
return name.includes(filter);
});
这次,不区分大小写的操作可以这样完成
filteredNames = names.filter(name => {
return name.toUpperCase().includes(filter.toUpperCase());
});
然后,我们将在 JSX 中使用典型的 .map()
方法遍历数组并输出名称。
查看 CodePen 上的示例
过滤技术:React,由 Chris Coyier (@chriscoyier) 创建。
在 CodePen 上。
我没有特别的偏好
这不是您选择技术的地方。您将在您已经使用的任何技术中使用它。我认为,就技术债务而言,任何一种方法都没有明显比其他方法更重。
在本文的 CSS 部分,您有类似以下内容
Li[data-content… {
}
这能否用于更改 textarea HTML 标签中某些词语的颜色?
(代码突出显示)
很棒的面对面比较!
但是,您在这里使用 JS 生成 CSS。=> CSS-in-JS(我讨厌它)
在 CSS 示例中,我更愿意使用 JS 仅更新用作样式定义(保持不变)中的过滤器的自定义属性。
我承认这会将效果限制在支持的浏览器中,但看起来每个元素都在完成自己的工作。
如果您指的是用自定义属性参数化 CSS 的属性选择器(例如:
li[data-name*=var(--whatever) i] ...
),那么不幸的是,这是不可能的。我认为对 CSS 样式表进行元编程并没有错,我不会称其为“CSS-in-JS”。一个更好的名称可能是“JIT CSS”。
如果您使用带有适当 polyfill 的 DataList 元素,您将在 HTML 中获得上述所有功能。它显示为文本框建议而不是列表,但您还可以在非 Firefox 浏览器中获得部分匹配,所以这很酷。
实际上,某些方法比其他方法更重。不过,这取决于您需要过滤多少数据和元素。
在构建我的 Mass Effect: Andromeda 角色规划工具时,我首先使用了 JQuery 方法。对于较小的数据集,它运行良好。但对于武器来说,武器有 2360 条记录,全部使用相对较大的模板进行渲染(许多元素),性能非常糟糕。
然后我切换到 CSS 方法,并将过滤时间缩短到 JQuery 方法的几乎 1/10。我对结果感到惊讶,但在思考之后,这似乎是有道理的。我只修改了 DOM 一次来提供新的 CSS 过滤器,而 JQuery 方法修改了数百次。浏览器在应用 CSS 方面速度非常快,远远超过 JS…
也许 React 方法可以产生接近 CSS 方法的结果,但我从未测试过(不是我技术栈的一部分)。不过,我怀疑任何方法都能超越 CSS,因为 CSS 的优化程度非常高。
我会使用 Vanilla JS。此解决方案应该在任何现代浏览器中都能正常工作,
RegExp
将负责不区分大小写。如果您仍然需要支持 IE11,此方法也能正常工作。
很高兴看到链接过滤。假设您有一个对象数组,并且该数据以表格格式显示。然后您有一个搜索字段和一个例如排序下拉菜单。
很棒的文章,Chris!我不知道属性过滤器中的“i”,很酷!
有没有任何速度测试比较 CSS 过滤和基于 JS 的过滤?
我实际上刚在一个项目中使用了这种技术,所以您可以在生产环境中使用它。
哦,我实际上在 6 年前写了一个示例,它使用了您在文章中提到的动态 CSS 过滤:https://codepen.io/netsi1964/pen/sInao