jQuery has vs find: When to Use the has Selector
While both jQuery's :has() selector (and its equivalent
.has() method) and the .find() method evaluate
descendant elements in the DOM, they serve opposite purposes regarding
what elements are actually selected. This article explains the
fundamental differences between these two tools and outlines the
specific scenarios where the has selector is preferred over
find, focusing on element targeting, selector composition,
and method chaining efficiency.
Understanding the Fundamental Difference
The primary distinction between has and
find lies in the resulting jQuery collection:
:has()/.has()returns the parent or ancestor element if it contains descendants matching the given selector..find()traverses down the DOM tree and returns the descendant elements themselves.
// Returns the <div class="card"> element if it contains a <button>
$("div.card").has("button");
// Returns the <button> elements located inside <div class="card">
$("div.card").find("button");1. Styling or Modifying the Parent Container Based on Its Contents
Use has whenever your goal is to apply classes, styles,
event listeners, or attributes to the containing element rather than the
child elements inside it.
Common use cases include:
- Form validation: Highlighting a
.form-groupwrapper that contains an input with an error. - UI states: Applying an active or highlighted state to a navigation card that contains an active badge or link.
// Preferred: Directly targets the parent container
$(".form-group").has(".is-invalid").addClass("has-error");
// Less optimal: Requires navigating down and back up
$(".form-group").find(".is-invalid").closest(".form-group").addClass("has-error");2. Writing Single Declarative Selectors
The :has() pseudo-class can be embedded directly within
a selector string. When you need to retrieve elements via a single query
without breaking into method chains, :has() is the ideal
approach.
// Select all list items that contain an expandable submenu
$("li:has(ul.submenu)").addClass("accordion-parent");This approach is concise and keeps the selection logic self-contained in a single selector expression.
3. Avoiding Redundant DOM Traversal Backtracking
When using .find(), the selection context shifts down to
the descendants. If you subsequently need to manipulate the ancestor,
you must backtrack using .closest(),
.parent(), or .end().
Using has keeps the context on the parent elements
throughout the entire chain, eliminating unnecessary DOM traversal steps
and improving code readability:
// Using .has() keeps the target clean and direct
$("article")
.has("img.featured")
.addClass("with-media")
.show();
// Using .find() introduces unnecessary context shifts
$("article")
.find("img.featured")
.closest("article")
.addClass("with-media")
.show();4. Filtering an Existing Collection of Elements
When you already have a jQuery object containing elements and want to
reduce the set based on child conditions, the .has() method
acts as an expressive filter.
const $tableRows = $("tbody tr");
// Filter existing rows down to only those with checked checkboxes
const $selectedRows = $tableRows.has("input[type='checkbox']:checked");Using .find() here would give you the checkboxes, not
the rows, requiring additional steps to retrieve the original row
references.
Summary Rule
Choose has when you need to act on the
container based on what is inside it. Choose
find when you need to act on the
inner elements themselves.