Recent chats
ASKGEEZ Q&A GUIDETechnology & devices

How do CSS attribute selectors match exact values, prefixes, and word lists?

Quick answer

Use [attr=value] for an exact value, ^= for a prefix, $= for a suffix, and *= for text anywhere inside a value. Use ~= to match one whole word in a whitespace-separated list. The similar-looking |= matches the exact value or that value followed immediately by a hyphen. Add [attr] when you only need to test whether an attribute exists. [S1]

LET’S WORK THROUGH IT

The question, the details, the next step.

Question

I have a few elements with data-kind attributes. How do I tell whether a value is an exact match, starts with something, or merely contains some text?

Geez

Choose the operator based on the shape of the match you want. For a data-kind value of exactly notice, write [data-kind="notice"]. For a value that starts with icon-, use [data-kind^="icon-"]. For one that ends with -large, use [data-kind$="-large"]. To match alert anywhere in the value, use [data-kind*="alert"]. These operators mean exact value, prefix, suffix, and substring, respectively. [S1]

For example, if your HTML has <span data-kind="icon-alert-large">, both the prefix selector and suffix selector match it; the substring selector for alert does too. The exact selector for alert does not match: the whole attribute value is longer. [S1]

<span data-kind="icon-alert-large">Important</span>
[data-kind^="icon-"] { /* begins with icon- */ }
[data-kind$="-large"] { /* ends with -large */ }
[data-kind*="alert"] { /* contains alert */ }
[data-kind="alert"] { /* exactly alert */ }

Follow-up question

What if the attribute holds several words, like a list of classes or labels? I want warning to match as a whole item, but not inside warning-extra.

Geez

Use [attr~=value] for one exact word in a whitespace-separated list. For your example, [data-label~="warning"] matches data-label="info warning compact", but does not match data-label="warning-extra". That avoids the partial-text match you would get with [data-label*="warning"]. [S1]

<p data-label="info warning compact">Check this item</p>
<p data-label="warning-extra">A different label</p>
[data-label~="warning"] {
  /* Matches the first paragraph, not the second. */
}

The key distinction is that ~= treats the value as whitespace-separated words and looks for one whole word. It is not a general “contains this sequence of characters” test. [S1]

Follow-up question

I also see [lang|="en"]. Is that the same as a word-list match? And can I select an element just because an attribute is present?

Geez

No. [attr|=value] matches either the exact value or a value that begins with it immediately followed by a hyphen. It is often useful for language subcodes: [lang|="en"] can match lang="en" and lang="en-GB", but not lang="english". By contrast, [attr~=value] checks for a whole word in a whitespace-separated list. [S1]

To select an element simply because it has an attribute, omit the equals sign and value: [data-label] matches elements where data-label is present. It does not require any particular value. [S1]

[lang|="en"] { /* en or a hyphenated subcode */ }
[data-label] { /* any element with data-label */ }

Follow-up question

How should I handle capitalization, and what’s a quick way to choose the selector without mixing up the operators?

Geez

First decide what part of the value must match: the whole value, its beginning, its end, any contained text, a whole whitespace-separated word, or the value followed by a hyphen. Then pick =, ^=, $=, *=, ~=, or |=, in that order. If you only care that the attribute exists, use [attr]. [S1]

Attribute-value case sensitivity depends on the document language and attribute. The i modifier requests ASCII case-insensitive matching; s requests ASCII case-sensitive matching. For example, [data-kind="notice" i] requests ASCII case-insensitive matching for this comparison. These modifiers do not change which operator shape you chose: exact, prefix, suffix, substring, word-list, or hyphen form. [S1]

[data-kind="notice" i] { /* exact value, ASCII case-insensitive */ }
[data-kind^="icon-"] { /* prefix */ }
[data-label~="warning"] { /* whole word in a list */ }

A useful check when a selector seems too broad is to compare it with a sample attribute value and ask whether the operator is matching the entire value, part of it, or a separate list item. The examples show how these operators are intended to distinguish those cases; they do not establish how any particular page or stylesheet is structured.

A practical checklist

  • Write down a representative attribute value from the markup.

  • Choose = for the whole value, ^= for a prefix, $= for a suffix, or *= for contained text.

  • Choose ~= for one whole whitespace-separated word; do not substitute *= if partial matches are unwanted.

  • Use |= only when the match is the exact value or that value followed immediately by a hyphen.

  • Use [attr] for presence alone, and consider i or s when ASCII case behavior matters.

A few more useful answers

Why does `[data-label~="warning"]` not match `data-label="warning-extra"`?

Because ~= looks for an exact word in a whitespace-separated list, and warning-extra is a different word. Use *= only if matching that character sequence inside a longer value is intended. [S1]

Does `[lang|="en"]` match any value that starts with `en`?

No. It matches the exact value en or a value where en is followed immediately by a hyphen, such as en-GB; it does not match english. [S1]

Does `[data-kind]` require the attribute to have a nonempty value?

It tests whether the attribute is present, not whether it has a particular value. Use a value-matching selector when the content of the attribute matters. [S1]

Sources

References used for this guide.

  1. [S1] Attribute selectors — CSS reference — MDN
    developer.mozilla.org · Checked

YOUR QUESTION. YOUR DETAILS.

Let’s work through your situation.

Get a tailored answer or help with your next task. Starts at $1, with a quote before you pay.

Ask your own question
← Back to Geez at Work