Is there a CSS selector for elements containing certain text?
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
Introduction
No, standard CSS does not have a selector that matches elements based on their text content. CSS selectors operate on the DOM structure (element names, classes, IDs, attributes, and relationships between elements), but they have no mechanism to inspect or match against text nodes. If you need to style elements based on what text they contain, you must either add semantic markup (classes or data attributes) or use JavaScript to inspect the text and apply classes dynamically.
Why CSS Cannot Select by Text
CSS was designed as a declarative styling language, not as a DOM query language. The CSS selector specification defines matching rules based on structural properties of elements:
| CSS Can Match | CSS Cannot Match |
Element type (div, p, span) | Text content of an element |
Class names (.warning) | Substring of inner text |
IDs (#header) | Text of child text nodes |
Attributes ([data-status]) | Computed text from pseudo-elements |
Pseudo-classes (:hover, :focus, :checked) | Text generated by JavaScript |
Structural position (:first-child, :nth-of-type) | Dynamic content changes |
Parent-child relationships (article > p) | Text across nested elements |
This is a deliberate design boundary. Text-based matching would require the CSS engine to parse and compare string content during layout, which would be expensive and would blur the line between styling and application logic.
The Right Solution: Semantic Markup
If you need to style elements differently based on their meaning, encode that meaning in the markup. The most reliable approaches are classes and data attributes.
Using Classes
Using Data Attributes
When the styling condition maps to a value rather than a boolean category, data attributes are cleaner:
Data attributes also support partial matching:
These attribute selectors are CSS-native and work in all browsers. They match attribute values, not text content, which is the key distinction.
Using JavaScript When Text Content Matters
If you genuinely need to style based on text content and you cannot change the markup (third-party widgets, CMS output, legacy systems), JavaScript is the correct tool.
Vanilla JavaScript
Using MutationObserver for Dynamic Content
If the DOM changes after initial load (e.g., streaming log output), use a MutationObserver to apply classes as new elements appear:
This keeps the text inspection in JavaScript where it belongs, while CSS handles only the visual styling.
jQuery's :contains() Is Not CSS
Some developers have seen :contains("text") syntax and assume it is part of CSS. It is not. The :contains() pseudo-class was proposed in early CSS3 drafts (Selectors Level 3) but was removed from the specification before browsers implemented it. It lives on in two places:
Both jQuery and Cypress implement their own selector extensions beyond what CSS supports. Code using :contains() will not work in a CSS stylesheet or in document.querySelector().
What About :has()?
The :has() selector (now supported in Chrome, Safari, and Firefox) is one of the most powerful additions to CSS. But it selects based on descendant elements, not text content.
:has() is structural. It can check for the presence of child elements, but it cannot inspect the text content of those elements. It does not solve the text-matching problem.
Framework-Specific Approaches
Modern component frameworks handle this at the rendering level rather than in CSS:
React
Vue
In both cases, the class assignment happens during rendering based on component state, not by inspecting rendered text. This is the correct architectural boundary: components decide what classes to apply, and CSS decides what those classes look like.
Common Pitfalls
Confusing jQuery selectors or testing framework locators with standard CSS is the most frequent mistake. :contains() works in jQuery and Cypress but is not part of the CSS specification and is not supported by browsers in stylesheets or querySelector.
Trying to use attribute selectors ([title*="warning"]) as a workaround for text content matching only works if the information is duplicated in an attribute. This approach is fragile and adds redundant data to the DOM.
Writing complex JavaScript to scan and classify text when the real fix is adding a class in the component that renders the markup adds unnecessary runtime cost. If you control the rendering, add semantic classes at render time. JavaScript text scanning should only be used when the markup comes from an external source you cannot modify.
Forgetting about localization makes text-based styling rules break when the application supports multiple languages. A rule that looks for the English word "Warning" fails for users seeing "Advertencia" or "Avertissement". Semantic classes and data attributes are language-independent.
Over-relying on :has() as a general replacement for text matching leads to brittle selectors. :has() is powerful for structural queries, but it still cannot match text content. If your styling condition is truly text-based, JavaScript is the right tool.
Summary
- Standard CSS has no selector for matching elements by their text content.
- Use classes or data attributes to encode meaning in the markup so CSS can target it.
- Use JavaScript to inspect text content and add classes dynamically when you cannot control the markup.
- jQuery's
:contains()is a jQuery extension, not a standard CSS selector. - '
:has()selects based on descendant elements, not text nodes.' - In component frameworks, assign semantic classes at render time rather than scanning rendered text.
Related reading
- Is there a good object mapper for Amazons dynamodbthrough aws sdk which can be used in nodejs?
- Is there a good way to Promise.all an array of objects which has a property as promise?
- Is there a JavaScript preprocessor that makes callbacks look nice?
- Is there a "null coalescing" operator in JavaScript?
- Is there a shortcut to create padded array in JavaScript?
- Is there a Thrift or Cassandra client for Node.js/JavaScript
- Is there a way to deal with values emitted for multiple observables independently, and then do stuff when all observables are complete?
- Is there a way to get the version from the 'package.json' file in Node.js code?
.png&w=3840&q=75)
Tackling System Design Interview Problems
A short course that equips you with the skills to approach system design interviews methodically.
Start the free courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.