jQuery :has() Selector

Beginner
⏱️ 9 min read
📚 Updated: Jul 2026
🎯 5 Examples
🚀 5 Try-it labs
jQuery extension · jQuery 1.1.4+

What You’ll Learn

The :has() selector matches elements that contain at least one descendant matching the inner selector — at any nesting depth. jQuery extension since 1.1.4; official docs recommend the .has() method for performance.

01

Syntax

div:has(p)

02

Descendants

Any depth

03

Official

div + p

04

.has()

Preferred

05

vs :contains

Structure

06

Parent pick

Filter up

Introduction

Often you need the container, not the inner element — highlight the card that holds an error message, style list items that contain sublists, or flag sections missing required fields. The jQuery :has() pseudo-class selects parents based on what they contain inside the DOM tree.

jQuery has supported :has() since version 1.1.4. Official documentation states that it matches elements containing at least one element that matches the specified selector — descendants at any depth, not just direct children. For better performance, use $("div").has("p") instead of embedding :has() in the selector string.

Understanding the :has() Selector

Think of :has() as “keep this element if something matching lives inside”:

  • <div><p>Hello</p></div> + div:has(p) → matches.
  • <div>Hello again!</div> (no p) + div:has(p) → no match.
  • <div><section><p>…</p></section></div> → still matches — p is a descendant.
  • Inner argument can be any selector — li:has(ul), article:has(.error).
💡
Beginner Tip

Do not confuse jQuery :has() with attribute presence [name] or text matching :contains(). :has() checks for nested elements matching a selector.

📝 Syntax

Official jQuery API form (since 1.1.4):

jQuery
jQuery( ":has(selector)" )

// typical usage — prefix with element:
$( "div:has(p)" )
$( "li:has(ul)" )
$( "article:has(.error)" )

// recommended — .has() method:
$( "div" ).has( "p" )

Parameters

  • selector — any jQuery selector; at least one matching descendant must exist inside the candidate element.

Return value

  • A jQuery object containing every outer element that has a matching descendant.
  • Empty collection when no containers match.

Official jQuery API example

jQuery
$( "div:has(p)" ).addClass( "test" );

⚡ Quick Reference

Goal:has().has() method
Divs with a paragraphdiv:has(p)$("div").has("p")
Nested depthAny descendantSame
Direct child onlyUse > combinator.children()
Match by textUse :contains()Not :has()
Native CSSjQuery extension in stringsjQuery method

📋 :has() vs .has(), :contains(), and child combinator

Descendant filter vs method vs text vs direct children.

:has(sel)
div:has(p)

Has descendant

.has(sel)
$("div").has("p")

Recommended

:contains
div:contains("Hi")

Text substring

> combinator
div > p

Direct child

Examples Gallery

Example 1 follows the official jQuery div:has(p) demo. Examples 2–5 cover the .has() method, nested lists, contrast with :contains(), and error-state parent selection.

📚 Official jQuery Demo

Add class test to divs that contain a paragraph.

Example 1 — Official Demo: div:has(p)

Official jQuery demo — div with a paragraph gets red inset border; plain div text does not.

jQuery
$( "div:has(p)" ).addClass( "test" );
Try It Yourself

How It Works

The inner p can be anywhere among descendants — official docs emphasize any depth, not just direct children.

Example 2 — Modern Code: .has() Method

Official performance guidance — separate the CSS selector from the descendant filter.

jQuery
// legacy selector string:
// $( "div:has(p)" ).addClass( "test" );

// recommended:
$( "div" ).has( "p" ).addClass( "test" );
Try It Yourself

How It Works

.has() is a traversing method that returns elements containing matching descendants — equivalent filtering, better performance pattern.

📈 Practical Patterns

Nested markup, text vs structure, validation UI.

Example 3 — Nested Lists: li:has(ul)

Style top-level list items that contain a nested sublist.

jQuery
$( "li:has(ul)" ).css( "fontWeight", "bold" );
Try It Yourself

How It Works

Only li elements with a ul descendant match — leaf items are skipped.

Example 4 — :has() vs :contains()

Structure filter vs visible text substring — different matching rules.

jQuery
console.log( ":has(p) →", $( "div:has(p)" ).length );
console.log( ":contains('Hello') →", $( "div:contains('Hello')" ).length );
Try It Yourself

How It Works

Plain-text divs can match :contains() but not :has(p) when no p element exists.

Example 5 — Validation UI: form:has(.error)

Highlight entire forms that contain at least one field with an error class.

jQuery
$( "form:has(.error)" ).addClass( "has-errors" );
Try It Yourself

How It Works

Parent selection is a common :has() pattern — style the container when any descendant matches.

🚀 Common Use Cases

  • Content blocks — style div:has(p) as in the official demo.
  • Navigation — bold li:has(ul) items with dropdown submenus.
  • Forms — flag form:has(.error) or form:has(:invalid).
  • Tables — highlight tr:has(input:checked) selected rows.
  • Cards — emphasize .card:has(img) with media.
  • Performance — prefer .has() method over :has() in strings.

🧠 How jQuery Evaluates :has()

1

Build outer set

Evaluate the selector before :has() — e.g. all div elements.

Query
2

Search descendants

For each candidate, look for at least one descendant matching the inner selector.

Tree
3

Keep matches

Return outer elements that contain a match — any nesting depth counts.

Filter
4

Return collection

Chain .addClass(), .css(), or other jQuery methods on parents.

📝 Notes

  • Available since jQuery 1.1.4 — jQuery extension in selector strings.
  • Inner selector can be any valid jQuery selector expression.
  • Matches descendants at any depth — not limited to direct children.
  • Prefer $("div").has("p") over $("div:has(p)") per official docs.
  • Not the same as attribute [name] or text :contains().
  • Modern CSS defines its own :has() — distinguish from jQuery’s historical extension.

Browser Support

The :has() pseudo-class in jQuery selector strings is a jQuery extension and works in jQuery 1.1.4+. The .has() method works in all browsers that support jQuery. Modern browsers also support native CSS :has() in stylesheets — a separate feature from jQuery selector strings.

jQuery 1.1.4+ · extension

jQuery :has() Selector

Use $("div").has("p") in new code — not div:has(p) in selector strings.

100% jQuery only (string)
Google Chrome All versions · Desktop & Mobile
Full support
Mozilla Firefox All versions · Desktop & Mobile
Full support
Apple Safari All versions · macOS & iOS
Full support
Microsoft Edge All versions · Chromium & Legacy
Full support
Internet Explorer IE 6+ · Legacy environments
Full support
Opera All modern versions
Full support
:has() Extension

Bottom line: Prefer .has() method — select with CSS, then filter descendants in jQuery.

Conclusion

The :has() selector matches elements that contain at least one matching descendant — the official div:has(p) demo adds class test to divs with paragraphs inside.

Use it for parent-selection patterns, distinguish it from :contains(), and prefer the .has() method in modern jQuery code.

💡 Best Practices

✅ Do

  • Use $("div").has("p") for better performance
  • Prefix outer selector — form:has(.error)
  • Use :has() when you need the parent container
  • Combine with class selectors to narrow scope
  • Learn .has() in the Traversing docs too

❌ Don’t

  • Confuse :has() with :contains() — elements vs text
  • Assume direct-child only — :has() searches all descendants
  • Confuse with attribute [name] presence selector
  • Overuse deep :has() chains on huge DOM trees
  • Expect :has() in bare querySelectorAll strings

Key Takeaways

Knowledge Unlocked

Five things to remember about :has()

Parent filter by descendant match.

5
Core concepts
02

Any depth

Not just >

Rule
.has 03

Method

Preferred

Tip
demo 04

Official

div:has(p)

Demo
05

:contains

Text differs

Compare

❓ Frequently Asked Questions

:has(selector) selects elements that contain at least one descendant matching the inner selector. $("div:has(p)") matches every div that has a p element anywhere inside it — not only as a direct child. Available since jQuery 1.1.4.
The child combinator matches direct children only — ul > li. :has() searches at any nesting depth among descendants — div:has(p) matches even when the p is nested several levels deep.
Official jQuery docs recommend $("div").has("p") instead of $("div:has(p)") for better performance in modern browsers. The method separates the base CSS selector from the descendant filter.
They look similar but jQuery :has() is a jQuery extension since 1.1.4. Modern CSS also defines :has(), but jQuery's version predates wide native support and works through jQuery's selector engine — prefer .has() method in new code.
:has() filters by descendant elements matching a selector — structure. :contains() filters by visible text substring — content. A div:has(p) checks for a p tag; div:contains("Hello") checks for text.
No. :has() in jQuery selector strings is a jQuery extension and does not work in native querySelectorAll the same way. Use .has() on a CSS-selected collection for modern code.
Did you know?

jQuery also provides a .has() traversing method with the same filtering behavior. Official docs recommend it over the :has() pseudo-class in selector strings because you can use a pure CSS selector first, then filter — similar to migrating from :eq() to .eq().

Continue to :header Selector

After structural :has() filters, learn how :header matches every h1–h6 heading at once.

:header selector tutorial →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

6 people found this page helpful