Skip to content

jQuery Content Filters — Complete Guide to :contains, :has, :parent, :empty

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about jquery content filters. We cover key concepts, practical examples, and best practices to help you master this topic.

jQuery content filters let you select elements based on their textual content, child elements, parent relationships, or whether they are empty — expanding beyond simple ID, class, and attribute selectors.

What You'll Learn

  • Using :contains() to filter by text content
  • Using :has() to find elements with specific children
  • Selecting empty and parent elements
  • Combining content filters with other selectors
  • Performance considerations for content filters

Why It Matters

Sometimes you need to find elements by what they contain, not by their structure. Content filters let you answer questions like "which paragraphs mention security?" or "which list items have a link inside?" without manual iteration.

Real-World Use

A documentation page where :contains() highlights search results, :has() selects sections that contain images for a gallery view, and :empty marks placeholder elements for removal.

Content Filter Types

flowchart TD
    A[Content Filters] --> B[:contains(text)]
    A --> C[:has(selector)]
    A --> D[:parent]
    A --> E[:empty]
    B --> F[Match by text content]
    C --> G[Match by children]
    D --> H[Elements with children]
    E --> I[Elements without children]
    style A fill:#e6f3ff,stroke:#4a90d9,stroke-width:2px

:contains() Filter

Selects elements that contain the specified text (case-sensitive):

// Find all paragraphs mentioning "security"
$('p:contains("security")').addClass('highlight');

// Find list items with "important"
$('li:contains("important")').css('font-weight', 'bold');

// Case-sensitive: "Security" and "security" are different
$('div:contains("JavaScript")').css('border', '1px solid blue');
// This does NOT match "javascript" (lowercase)

Expected output: All elements containing the exact text (case-sensitive) receive the styling. The string "JavaScript" matches "Learning JavaScript" but not "javascript basics".

:contains() for Search Highlighting

function highlightSearch(query) {
  if (!query.trim()) return;

  // Remove existing highlights
  $('.highlight').removeClass('highlight');

  // Find matching elements and highlight
  $('.content p:contains("' + query + '")').addClass('highlight');
}

$('#search-input').on('keyup', function() {
  highlightSearch($(this).val());
});

:has() Filter

Selects elements that have at least one descendant matching the selector:

// Find list items that contain a link
$('li:has(a)').addClass('has-link');

// Find sections that contain an image
$('.section:has(img)').css('border', '2px solid green');

// Find form groups that have an error
$('.form-group:has(.error-message:visible)').addClass('has-error');

// Nested: divs containing a ul containing an li with class "active"
$('div:has(ul:has(li.active))').addClass('contains-active');

Expected output: Only elements matching the outer selector that ALSO have the specified descendant get the style. For example, .section:has(img) selects only sections that contain at least one image.

:has() for Form Validation

$('#submit').click(function() {
  // Check each form group for validation errors
  $('.form-group').each(function() {
    var $group = $(this);
    var value = $group.find('input, select, textarea').val();

    if (!value || value.trim() === '') {
      $group.find('.error-message').text('Required').show();
    } else {
      $group.find('.error-message').hide();
    }
  });

  // Highlight groups with visible errors
  $('.form-group:has(.error-message:visible)').addClass('validation-error');
});

:parent Filter

Selects elements that have at least one child node (element or text):

// Find all divs that have content
$('div:parent').css('padding', '10px');

// Find non-empty list items
$('li:parent').addClass('has-content');

// Combined: parent paragraphs inside articles
$('article p:parent').css('margin-bottom', '1em');

Expected output: Only elements that contain something (child elements or text) are selected. An empty div <div></div> is NOT selected by :parent.

:empty Filter

Selects elements that have no children at all:

// Find empty divs
$('div:empty').text('Placeholder');

// Remove empty list items
$('li:empty').remove();

// Hide empty paragraphs
$('p:empty').hide();

// Mark empty cells in a table
$('td:empty').addClass('empty-cell').text('-');

Expected output: Elements with no child elements and no text content (even whitespace counts as content) are selected. <div></div> matches, but <div> </div> (with a space) does not.

Combining Content Filters

// Find empty paragraphs that are inside articles
$('article p:empty').remove();

// Find divs with links in the sidebar
$('.sidebar div:has(a)').addClass('has-links');

// Find form groups with errors that are visible
$('.form-group:visible:has(.error-message:visible)');

// Find list items that mention "urgent" and have a class "task"
$('li.task:contains("urgent")').css('background', '#fff3cd');

Performance Considerations

Content filters are slower than ID and class selectors because they must inspect element content:

// SLOW: Scans all li elements, checks text content
$('li:contains("security")');

// FASTER: Narrow first, then filter
$('.task-list li:contains("security")');

// FASTEST: Use .filter() with a pre-compiled regex
$('.task-list li').filter(function() {
  return $(this).text().toLowerCase().includes('security');
});

Case-Insensitive Contains

Since :contains() is case-sensitive, create a custom filter for case-insensitive matching:

// Custom case-insensitive :contains
$.expr[':'].containsCI = function(elem, index, match) {
  return $(elem).text().toLowerCase()
    .indexOf(match[3].toLowerCase()) !== -1;
};

// Usage
$('p:containsCI("security")').addClass('highlight');
// Now matches "Security", "SECURITY", "security"

Common Mistakes

  1. :contains() is case-sensitive - :contains("security") does not match "Security". Use a custom filter or .filter() for case-insensitive matching.

  2. :has() checks descendants, not just direct children - div:has(span) matches even if the span is nested several levels deep. Use div:has(> span) for direct children only.

  3. :parent vs :empty confusion - :parent selects elements that HAVE children. :empty selects elements WITHOUT children. They are logical opposites but do not cover all cases (text-only elements).

  4. Whitespace in :empty - <div> </div> (a single space) is NOT empty. <div></div> IS empty. Use $.trim() or the :blank selector from polyfills for whitespace-aware emptiness.

  5. Slow performance on large pages - :contains() and :has() scan content and can be slow on pages with thousands of elements. Narrow the selection first.

Practice Questions

  1. How does :contains() differ from the text() method when searching?
  2. What is the difference between :has() and .find()?
  3. Why is div:parent different from div:not(:empty)?
  4. How can you perform a case-insensitive text search with jQuery?
  5. What is the performance impact of content filters?

Challenge: Build a search bar that highlights matching paragraphs in an article. Use :contains() for the search, add a case-insensitive custom filter, and show a count of matching results. Clear highlights when the search is cleared.

FAQ

{{< faq "Can I use :contains() with variables?" "Yes: $('p:contains(\"\" + searchTerm + \"\")'). Be careful with quotes when interpolating variables." >}}

How do I select elements that are NOT empty?

Use :parent to select elements with children, or :not(:empty) for a more intuitive (but slightly different) approach.

Can I nest :has() filters?

Yes: div:has(ul:has(li.active)) selects divs that contain a ul that contains an active li. Each :has() narrows the selection.

Do content filters work with XML?

Yes, jQuery selectors including content filters work with XML documents, assuming the XML parser preserves text content.

What is the difference between :has() and .has()?

:has() is a selector filter used in strings: $('div:has(span)'). .has() is a traversal method: $('div').has('span'). Both achieve similar results.

Mini Project

Build a search interface for a FAQ page. Use :contains() to filter questions that match the search term, :has() to find answer sections that contain relevant keywords, and :empty to hide sections with no results. Show a "No results" message when appropriate.

What's Next

Content filters select elements by what they contain. Learn how jQuery selectors offer even more ways to target specific elements in the DOM.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro