HomeGuidesHTML & CSSCSS Selector Contains — How to Select Elements by Text Content
🎨 HTML & CSS

How to Select Elements by Text Content in CSS

Developers reaching for a CSS "contains text" selector hit a wall — :contains() was removed from the spec. Here are the real alternatives.

Examifyr·2026·5 min read

Does CSS have a :contains() selector?

:contains() was drafted in early CSS Selectors specs but was removed before finalisation and was never standardised. It is not supported in any modern browser. You may encounter it in jQuery (which ships its own selector engine) or in browser devtools experiments — but it must never be used in production CSS.

/* This does NOT work in browsers — :contains() is not a CSS standard */
/* p:contains("error") { color: red; }  ❌ invalid CSS */

/* jQuery has its own :contains() — but this is jQuery, not CSS */
/* $('p:contains("error")').css('color', 'red');  jQuery only */
Note: If you saw :contains() work in a browser, it was likely a DevTools experiment or an older Chromium build. Do not rely on it.

Attribute value contains a substring: [attr*="value"]

If you want to select elements whose attribute value contains a string, CSS does support this natively with the *= operator.

/* Select links whose href contains "example" */
a[href*="example"] { color: blue; }

/* Select elements whose class contains "btn-" */
[class*="btn-"] { border-radius: 4px; }

/* Select inputs whose name contains "email" */
input[name*="email"] { border-color: #2563eb; }

/* Starts with (^=) and ends with ($=) also available */
a[href^="https"] { color: green; }   /* starts with */
a[href$=".pdf"]  { color: red; }     /* ends with   */

Select a parent that contains a specific child: :has()

:has() is the CSS "relational pseudo-class" — it selects an element if it structurally contains a matching child. Well-supported in all modern browsers since 2023.

/* Article that contains an image */
article:has(img) { border: 1px solid #e2e8f0; }

/* Form that contains an invalid input */
form:has(:invalid) { border-color: red; }

/* List item that has a direct anchor child */
li:has(> a) { font-weight: 600; }

/* Card that contains both an h2 and a button */
.card:has(h2):has(button) { padding: 24px; }
Note: :has() matches structural containment (child/descendant elements), not text content. Supported in Chrome 105+, Safari 15.4+, Firefox 121+.

Selecting by text content: you need JavaScript

CSS has no native way to select elements by their text content. For text matching, use JavaScript — querySelectorAll returns a NodeList, so convert it with Array.from before filtering.

/* Select all paragraphs whose text contains "error" */
const els = Array.from(document.querySelectorAll('p'))
    .filter(p => p.textContent.includes('error'));

els.forEach(el => el.style.color = 'red');

/* Case-insensitive match */
const els2 = Array.from(document.querySelectorAll('li'))
    .filter(li => li.textContent.toLowerCase().includes('warning'));

The cleanest CSS-only workaround: data attributes

If you control the HTML, the idiomatic CSS approach is to store the relevant value in a data attribute and target that with [data-*="value"].

<!-- HTML: mark elements with a data attribute -->
<!-- <p data-status="error">Something went wrong.</p>   -->
<!-- <p data-status="success">Saved successfully.</p>  -->

/* CSS: target the attribute value */
[data-status="error"]   { color: #dc2626; background: #fef2f2; }
[data-status="success"] { color: #16a34a; background: #f0fdf4; }

/* Partial match also works */
[data-label*="warn"] { border-left: 3px solid orange; }
Note: This is the recommended pattern: keep text content for display, use data attributes for CSS hooks. It separates presentation from content cleanly.

Exam tip

The exam question is usually: "Why doesn't :contains() work in CSS?" — it was removed from the spec before standardisation. Know the alternatives: [attr*=] for attribute substrings, :has() for structural containment, and JavaScript or data attributes for text-content matching.

🎯

Think you're ready? Prove it.

Take the free HTML & CSS readiness test. Get a score, topic breakdown, and your exact weak areas.

Take the free HTML & CSS test →

Free · No sign-up · Instant results

← Previous
CSS Selectors Cheat Sheet & Examples — Pseudo-classes, :contains & Combinators
Next →
CSS Box Model Explained — margin, padding, border & box-sizing
← All HTML & CSS guides