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-group wrapper 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.