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
Fundamentals
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.
Concept
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.
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" );
Cheat Sheet
⚡ Quick Reference
Goal
:has()
.has() method
Divs with a paragraph
div:has(p)
$("div").has("p")
Nested depth
Any descendant
Same
Direct child only
Use > combinator
.children()
Match by text
Use :contains()
Not :has()
Native CSS
jQuery extension in strings
jQuery method
Compare
📋 :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
Hands-On
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.
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.
Important
📝 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.
Compatibility
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 ChromeAll versions · Desktop & Mobile
Full support
Mozilla FirefoxAll versions · Desktop & Mobile
Full support
Apple SafariAll versions · macOS & iOS
Full support
Microsoft EdgeAll versions · Chromium & Legacy
Full support
Internet ExplorerIE 6+ · Legacy environments
Full support
OperaAll modern versions
Full support
:has()Extension
Bottom line: Prefer .has() method — select with CSS, then filter descendants in jQuery.
Wrap Up
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.
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
Summary
Key Takeaways
Knowledge Unlocked
Five things to remember about :has()
Parent filter by descendant match.
5
Core concepts
⊃01
:has()
Has descendant
API
↓02
Any depth
Not just >
Rule
.has03
Method
Preferred
Tip
demo04
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().