在操作DOM的时候,我们经常会遇到这种需求:找到那些"内部包含某个特定子元素"的父级元素,然后对父级做样式调整或事件绑定。比如,给所有包含图片的段落加个边框,或者高亮那些内部有错误提示 <span> 的表单字段。
原生的CSS选择器在早期很难直接表达"包含某个后代的选择器"这种父子关系。jQuery的 has() 方是填补这个空白的筛选工具——它可以在一组元素中,只保留那些内部包含匹配指定选择器的后代元素。
核心机制:过滤而非查找
有一点需要先明确:has() 是一个过滤方法,它的作用是缩减已有的jQuery对象,而不是去DOM树里查找新的元素。它检查中每个元素的后代,如果找到一个与传入选择器匹配的后代,就保留该元素,否则把它从中移除。
这个行为模式和 filter() 有相似之处,但 filter() 是检查元素自身是否匹配,而 has() 是深入检查元素的内部后代。
语法
$(selector).has(element)
-
selector:初始选中的元素,可以是一个标签名、类名、ID或者更复杂的CSS选择器。
-
element:必填参数。可以是一个选择器字符串,也可以是一个DOM元素或jQuery对象,用来匹配这些元素的内部后代。如果要匹配多种后代,用逗号分隔即可。
实战示例一:高亮包含特定标题的区块
这个例子模拟一个内容管理后台的场景。页面上有多个内容区块(用 div 表示),每个区块里的内容结构不同。现在需要找出所有包含 <h3> 标题的区块,并给它们加上醒目的高亮样式。
<html>
<head>
<meta charset="UTF-8">
<title>jQuery has() 筛选包含h3的区块</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
$("#highlightBtn").click(function(){
// 选中所有div,但只保留内部有h3后代的那一个
$("div").has("h3").css({
"border": "3px solid #e74c3c",
"background-color": "#fdf2f2",
"padding": "10px"
});
});
});
</script>
<style>
.content-block {
border: 1px solid #ccc;
margin: 8px 0;
padding: 8px;
}
</style>
</head>
<body>
<h3>jQuery has() 方法筛选演示</h3>
<p>点击按钮后,只有包含h3标题的区块会被高亮。</p>
<button id="highlightBtn">执行筛选</button>
<div class="content-block">
<strong>区块A</strong>
<p>这个区块里只有一个普通段落。</p>
</div>
<div class="content-block">
<strong>区块B</strong>
<h3>这是区块B里的三级标题</h3>
<p>这个区块里有一个h3元素,所以会被选中。</p>
</div>
<div class="content-block">
<strong>区块C</strong>
<h4>这是区块C里的四级标题</h4>
<p>这个区块里只有h4,不会被选中。</p>
</div>
</body>
</html>
点击按钮后,只有区块B会被加上红色虚线和浅红背景。$("div").has("h3") 这行代码的意思是:把页面上所有 div 拿出来,逐一检查它们的后代里有没有 h3,有就留下,没有就筛掉。
实战示例二:使用逗号匹配多种后代元素
有时候筛选条件不止一个——比如,既要关注包含 <span> 的元素,也要关注包含 <b> 的元素。has() 支持用逗号指定多个选择器,逻辑是"或"的关系:只要后代匹配其中任意一个选择器,父元素就会被保留。
<html>
<head>
<meta charset="UTF-8">
<title>jQuery has() 多选择器筛选</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
$("#multiBtn").click(function(){
// 在p、h5、h4这些元素中,筛选出内部包含span或b标签的
$("p, h5, h4").has("span, b").css({
"border": "2px dashed #2980b9",
"background-color": "#eaf2f8"
});
});
});
</script>
<style>
.item-row {
border: 1px solid #999;
margin: 6px 0;
padding: 6px;
}
</style>
</head>
<body>
<h3>多后代元素筛选</h3>
<p>点击按钮后,包含 <span> 或 <b> 标签的元素会被标记出来。</p>
<button id="multiBtn">执行多条件筛选</button>
<div class="item-row">
<p>这是一段纯文本的段落,不会有任何标记。</p>
</div>
<div class="item-row">
<h5>这个标题里有 <b>加粗文字</b>,所以会被选中。</h5>
</div>
<div class="item-row">
<h4>这个标题里有 <span>span内联元素</span>,也会被选中。</h4>
</div>
<div class="item-row">
<p>这是另一段 <em>斜体文字</em>,em不在筛选条件中,不会选中。</p>
</div>
</body>
</html>
在这个示例中,$("p, h5, h4").has("span, b") 先找出所有的 p、h5 和 h4 元素,然后只保留那些后代中有 span 或者 b 标签的。注意第三个区块中的 <em> 并没有被列入筛选条件,所以包含它的段落不会获得高亮样式。
个人经验:has() 和 :has() 选择器的区别
这里补充一点实际使用中的观察。jQuery除了 has() 方法,还有一个同名的 :has() 伪类选择器,可以直接写在选择器字符串里,比如 $("div:has(h3)")。它们的最终效果是一样的,但内部实现路径不同。
$("div:has(h3)") 是把筛选逻辑交给了Sizzle选择器引擎,在遍历DOM的早期阶段就完成过滤。而 $("div").has("h3") 是先取出所有 div,再用方法链做的后置过滤。在元素数量很大的页面上,$("div").has("h3") 这种先查全量再筛的做法可能带来不必要的开销。如果筛选条件固定且不需要动态变化,我个人的习惯是优先用 :has() 伪类选择器,把筛选逻辑和元素查找合并在一起,减少一次方法调用。
本节课程知识要点
-
has()是一个过滤方法,用于从已有的jQuery对象中筛选出那些后代元素匹配指定选择器的项,返回新的jQuery对象。 -
参数
element支持用逗号分隔多个选择器,表示"或"的关系——后代只要匹配其中任意一个即可保留父元素。 -
has()检查的是元素的后代(children、子子代),不是元素自身。如果只是要检查元素自身是否符合某条件,应该使用filter()方法。 -
has()方法与:has()伪类选择器在功能上等价,但使用场景略有差异:前者适合链式调用和动态组合,后者适合在选择器字符串中一次性完成筛选。 -
该方法返回的是筛选后的父级元素,后续可以继续用
.css()、.addClass()、.on()等方法对父级进行操作。