Class Selector
Using selectors
Use selectors to find elements by tag, class, attribute, text, or their position in the document. Pass a query to
Element.select(String) to get matching elements, or Element.selectFirst(String) to get the first match.
Elements links = doc.select("a[href]");
Elements images = doc.select("article img");
Element heading = doc.selectFirst("article > h1");
Combine selectors to narrow your search: a.download[href$=.zip] finds links with the download class
whose URLs end in .zip. Try queries in the
Try jsoup online playground.
To retrieve nodes such as text and comments, use Element.selectNodes(String).
jsoup 1.24.1 introduces an opt-in Web query mode with changes to matching behavior; see Query compatibility changes and the selector migration guide.
Selector syntax
A selector combines simple selectors, such as .header[title], with combinators, such as >, to
describe relationships between elements. The universal selector * is implicit:
.header and *.header are equivalent.
See element and attribute selectors, combinators, logical selectors, structural selectors, text, regex, and index selectors, and node selectors.
Element and attribute selectors
| Pattern | Matches | Example |
|---|---|---|
* | any element | * |
tag | elements with the given tag name | div |
*|E | elements of type E in any namespace (including non-namespaced) | *|name finds <dc:name> and <name> elements |
ns|E | elements of type E in the namespace ns | dc|name finds <dc:name> elements |
ns|* | all elements in the namespace ns | dc|* finds <dc:p> and <dc:img> elements |
#id | elements with attribute ID of "id" | div#wrap, #logo |
.class | elements with a class name of "class" | div.left, .result |
[attr] | elements with an attribute named "attr" (with any value) | a[href], [title] |
[^attrPrefix] | elements with an attribute name starting with "attrPrefix". Use to find elements with HTML5 datasets | [^data-], div[^data-] |
[attr=val] | elements with an attribute named "attr", and value equal to "val" | img[width="500"], a[rel=nofollow] |
[attr!=val] | elements whose attribute value differs from "val", including elements without the attribute | a[rel!=nofollow] |
[attr^=valPrefix] | elements with an attribute named "attr", and value starting with "valPrefix" | a[href^="http:"] |
[attr$=valSuffix] | elements with an attribute named "attr", and value ending with "valSuffix" | img[src$=".png"] |
[attr*=valContaining] | elements with an attribute named "attr", and value containing "valContaining" | a[href*="/search/"] |
[attr~=regex] | elements with an attribute value matching the regular expression; see the upcoming change to token matching | img[src~=(?i)\.(png|jpe?g)] |
[attr|=val] | matches an exact value or that value followed by a hyphen | [lang|=en] matches "en" and "en-US" |
[attr=val i], [attr=val s] | i ignores ASCII case in the attribute value; s requires exact case. Neither changes attribute-name matching. Also applies to substring and hyphen operators; see case matching | [data-sku="AbC123" i], [type="TEXT" s] |
:matchesAttr(attr, regex) | elements with an attribute value containing a regex match | img:matchesAttr(src, (?i)\.(png|jpe?g)) |
[*] | elements with any attribute | p[*] finds p elements that have at least one attribute; p:not([*]) finds those with no attributes |
Combinators
| Pattern | Matches | Example |
|---|---|---|
E F | an F element descended from an E element | div a, .logo h1 |
E > F | an F direct child of E | ol > li |
E + F | an F element immediately preceded by sibling E | li + li, div.head + div |
E ~ F | an F element preceded by sibling E | h1 ~ p |
E, F, G | all matching elements E, F, or G | a[href], div, h3 |
Logical selectors
| Pattern | Matches | Example |
|---|---|---|
:has(selector) | elements that contain at least one element matching the selector | div:has(p) finds divs that contain p elements.div:has(> a) selects div elements that have at least one direct child a element.section:has(h1, h2) finds section elements that contain a h1 or a h2 element |
:is(selector list) | elements that match any of the selectors in the selector list | :is(h1, h2, h3, h4, h5, h6) finds any heading element.:is(section, article) > :is(h1, h2) finds a h1 or h2 that is a direct child of a section or an article |
:not(selector) | elements that do not match the selector. See also Elements.not(String) | div:not(.logo) finds all divs that do not have the "logo" class.
|
Structural selectors
| Pattern | Matches | Example |
|---|---|---|
:scope | The element you query from, or the document element (usually html) when querying a Document | :scope > p finds direct child paragraphs |
:root | The tree's root element: the document element, or the topmost element of a detached tree | :root |
:nth-child(an+b) | elements at child positions an+b, for any integer n starting at zero. Positions start at 1 and count element siblings. Use odd for 2n+1, or even for 2n | tr:nth-child(2n+1) finds odd rows; li:nth-child(5) finds the fifth list item; :nth-child(10n-1) finds positions 9, 19, 29, etc. |
:nth-last-child(an+b) | like :nth-child(), counting from the last sibling | tr:nth-last-child(-n+2) finds the last two rows |
:nth-of-type(an+b) | like :nth-child(), counting only siblings of the same type | img:nth-of-type(2n+1) |
:nth-last-of-type(an+b) | like :nth-of-type(), counting from the last sibling | img:nth-last-of-type(2n+1) |
:first-child | elements that are the first child of some other element. | div > p:first-child |
:last-child | elements that are the last child of some other element. | ol > li:last-child |
:first-of-type | elements that are the first sibling of its type in the list of children of its parent element | dl dt:first-of-type |
:last-of-type | elements that are the last sibling of its type in the list of children of its parent element | tr > td:last-of-type |
:only-child | elements with a parent and no element siblings | p:only-child |
:only-of-type | elements with a parent and no siblings of the same type | p:only-of-type |
:empty | elements that contain no child elements or nodes, with the exception of blank text nodes, comments, XML declarations, and doctype declarations. In other words, it matches elements that are effectively empty of meaningful content. | li:not(:empty) |
Text, regex, and index selectors
These jsoup extensions match text or zero-based sibling indexes.
| Pattern | Matches | Example |
|---|---|---|
:lt(n) | elements whose sibling index is less than n | td:lt(3) finds the first 3 cells of each row |
:gt(n) | elements whose sibling index is greater than n | td:gt(1) finds cells after skipping the first two |
:eq(n) | elements whose sibling index is equal to n | td:eq(0) finds the first cell of each row |
:contains(text) | elements that contain the specified text. The search is case insensitive. The text may appear in the found element, or any of its descendants. The text is whitespace normalized. To find content that includes parentheses, escape those with a | p:contains(jsoup) finds p elements containing the text "jsoup".
|
:containsOwn(text) | elements that directly contain the specified text. The search is case insensitive. The text must appear in the found element, not any of its descendants. | p:containsOwn(jsoup) finds p elements with own text "jsoup". |
:containsData(data) | elements that contain the specified data. The contents of script and style elements, and comment nodes (etc) are considered data nodes, not text nodes. The search is case insensitive. The data may appear in the found element, or any of its descendants. | script:containsData(jsoup) finds script elements containing the data "jsoup". |
:containsWholeText(text) | elements containing the exact text, including case, spaces, and newlines, in themselves or their descendants. A br contributes a newline. Escape parentheses with a backslash | p:containsWholeText(Hello) finds paragraphs containing "Hello", but not "hello" |
:containsWholeOwnText(text) | like :containsWholeText(), but matches only the element's own text | p:containsWholeOwnText(Hello) finds paragraphs containing "Hello" in their own text, excluding child elements' text |
:matches(regex) | elements containing whitespace normalized text that matches the specified regular expression. The text may appear in the found element, or any of its descendants. | td:matches(\d+) finds table cells containing digits. div:matches((?i)login) finds divs containing the text, case insensitively. |
:matchesOwn(regex) | like :matches(), but matches only the element's own whitespace-normalized text | td:matchesOwn(\d+) |
:matchesWholeText(regex) | elements containing non-normalized whole text that matches the specified regular expression. The text may appear in the found element, or any of its descendants. | td:matchesWholeText(\s{2,}) finds table cells containing a run of at least two whitespace characters. |
:matchesWholeOwnText(regex) | elements whose own non-normalized whole text matches the specified regular expression. The text must appear in the found element, not any of its descendants. | td:matchesWholeOwnText(\n\d+) finds table cells directly containing digits following a newline. |
Node selectors
These jsoup extensions select text, comments, and other nodes directly. Combine a node type with a content filter,
such as ::text:contains(foo), and use combinators to restrict where it appears:
Nodes<TextNode> text = doc.selectNodes("p ::text:contains(foo)", TextNode.class);
This returns individual text nodes containing "foo" inside paragraphs. To find paragraphs containing that text,
use the regular element selector p:contains(foo).
Use Element.selectNodes(String) to retrieve nodes, or the
typed overload to collect a specific node type.
With Element.select(String), use node selectors inside :has() when their presence or content determines
which elements you want. For example, this finds divs with a direct child comment containing "TODO":
Elements annotated = doc.select("div:has(> ::comment:contains(TODO))");
| Pattern | Matches | Example |
|---|---|---|
::node | Any Node, including elements and leaf nodes | section > ::node finds all direct child nodes of sections, including elements, text, and comments |
::leafnode | Non-element nodes, such as text, comments, data, and processing instructions. An empty element is still an element, not a leaf node | article ::leafnode:not(:blank) finds non-element nodes with nonblank values inside articles |
::text | Text nodes, including CDATA sections | p ::text:contains(foo) finds individual text nodes containing "foo" inside paragraphs |
::comment | Comments; content filters search the comment data, without the enclosing markup | ::comment:contains(TODO) finds comments containing "TODO" |
::data | Data nodes, such as the contents of HTML script and style elements, which are stored as data rather than text nodes | script > ::data:contains(analytics) finds script data containing "analytics" |
::cdata | CDATA sections; content filters search the section's text, without the CDATA delimiters | ::cdata:contains(config) finds CDATA sections containing "config" |
::pi | Processing instructions; content filters search the instruction's data | ::pi:contains(country-options) finds instructions with "country-options" in their data |
::pi(target) | Processing instructions with the specified case-sensitive target | ::pi(xml-stylesheet) finds instructions such as <?xml-stylesheet href="style.xsl"?> |
::pi[...] | Processing instructions whose data matches an attribute selector. The instruction data is parsed into attributes for comparison | ::pi(marker)[name=country-options] finds instructions such as <?marker name="country-options"?> |
Append :contains(text), :matches(regex), or :blank to any of these node selectors to test its
value. For text, comments, data, and CDATA, this is the node's content; for
processing instructions, its data; for elements, their own text, excluding child elements' text.
Node values retain whitespace; :contains() normalizes whitespace in the search text and ignores case.
:matches() searches the value using a regular expression, and :blank tests for an empty or whitespace-only value.
For example, ::comment:matches(\d+) finds comments containing digits, and ::text:not(:blank) finds
text nodes with nonblank content. These filters can be combined with :not() and with each other.
Matching rules
In the default legacy mode, Element.select(String) searches the element it is called on and its descendants.
For example, given <div id="outer"><div id="inner"></div></div>, outer.select("div") returns both divs,
while outer.select(":scope > div") returns only the inner div.
See Query compatibility changes for Web mode's matching rules.
Element.is(String) checks whether the element itself matches the query.
Element.closest(String) finds the nearest matching element: itself or an ancestor.
Ancestor matching stops at the element you query from. For example, panel.select("body p") does not match
paragraphs when the body is outside the panel.
Tag and attribute names ignore ASCII case. Class names and literal attribute values ignore Unicode case; IDs match
exactly. Structural selectors such as :nth-of-type() group siblings by their normalized tag names.
Use i on an attribute selector to ignore ASCII case in its value, or s for exact case; see
case matching for examples. These flags do not affect attribute names or the default regex
interpretation of ~=; use regex inline flags such as (?i) there.
Text selectors keep the matching rules described in their entries above. Regex selectors use Java's
Pattern syntax and flags.
Escaping and regular expressions
The examples in the tables are CSS queries. When writing a query in a Java string literal, double each backslash.
Escape CSS special characters with a backslash. To match <p id="i.d">, the CSS query is #i\.d;
in Java, write document.select("#i\\.d"). You can also use escapeCssIdentifier(String) to
escape a tag, ID, or class name.
Regex arguments are passed to the regex parser without CSS unescaping. For example, the CSS query
img:matchesAttr(src, (?i)\.(png|jpe?g)) is written in Java as
document.select("img:matchesAttr(src, (?i)\\.(png|jpe?g))").
Whitespace immediately after the argument comma is skipped; use \x20 for a leading literal space.
Regex arguments use balanced parentheses. Escape literal parentheses that would unbalance the argument, including
inside character classes: :matchesAttr(x, ^[^\)]+$) is written in Java as
document.select(":matchesAttr(x, ^[^\\)]+$)"). Surrounding quotes are part of the regex, not an argument wrapper.
When searching for literal text through a regex selector, use Pattern.quote(String) to quote it:
String query = "div:matches(" + Pattern.quote(text) + ")";.
Query compatibility changes
jsoup 1.24.1 introduces Web query behavior that follows CSS and Web DOM standards more closely.
Set the jsoup.queryMode system property before running queries to choose:
legacy: use the query behavior of earlier jsoup releases.web: use the updated CSS and Web DOM matching rules described below.check: return legacy results while comparing supported queries against Web mode and reporting differences. See Checking query compatibility.
The default is legacy in 1.24.1 and changes to web in 1.25.1, when legacy and check
remain available. In 1.26.1, Web behavior is the only option and the property is removed. See the
selector migration guide for compatibility testing and query updates.
What changes in Web mode
| Query behavior | Legacy mode | Web mode |
|---|---|---|
| Query results | Include the starting element if it matches. | Include only its descendants. |
| Selector relationships | Ancestor matching stops at the element you query from. | Can match ancestors and siblings outside its subtree; results stay inside the element you query from. |
| Tag and attribute names | Ignore ASCII case. | HTML names ignore ASCII case. Names in XML and foreign content (such as SVG and MathML) match exactly, as do attribute names on processing instructions. |
| Classes and IDs | Classes ignore Unicode case; IDs match exactly. | Match exactly, except class and ID selectors in quirks documents ignore ASCII case. |
| Literal attribute values without a flag | Ignore Unicode case. | Match exactly, except HTML attributes with case-insensitive values, such as type, rel, and lang, ignore ASCII case. |
~= | Matches an attribute value using a regex. | Matches a complete whitespace-separated token. Supports i and s flags. |
:nth-of-type() and related selectors | Compares normalized tag names. | Compares namespace and exact local name. |
These rules apply to select, selectFirst, their stream and node variants, and the convenience finders on
Element: getElementsBy*, text finders, getElementById, and getAllElements.
Element.hasClass(String) uses the selected mode's class case rules. getElementById continues to match IDs exactly,
including in quirks documents. Text and regex selectors retain their value-matching rules.
Checking query compatibility
Check mode helps you find queries that may behave differently when switching to Web mode. Enable it before running your queries:
System.setProperty("jsoup.queryMode", "check");
Each supported query runs in both modes and returns the legacy result. If the results differ, or the query fails in
Web mode, jsoup writes a diagnostic to stderr identifying the query and up to two caller locations.
Comparisons check which elements are returned and their order, not just the number of matches.
jsoup query compat: select("div"): legacy=2, web=1; results differ; at example.ProductParser.items(ProductParser.java:42)
(Checks cover common string-query methods such as select() and selectFirst(); streams, node queries,
supplied evaluators, and convenience finders run without checks.)
Use check mode when running tests or assessing compatibility; running queries twice adds overhead.
After reviewing and updating affected queries, switch to web.
Query results and scope
:scope refers to the element the query is called on, or the document element when querying a Document.
In Web mode, adding :scope does not include that element in query results. Element.is(String) and
Element.closest(String) still test the supplied element; their :scope refers to that element.
Elements.not(String) tests each supplied element directly.
To include the starting element when it matches, filter Element.stream() with
Evaluator.asPredicate(Element).
In Web mode, inside :has(), a leading combinator starts from each element being tested, but :scope still
refers to the element the query is called on. For example, outer.select("div:has(> p)") finds divs with a direct child
paragraph; outer.select("div:has(:scope > p)") finds none, because a paragraph inside a descendant div cannot also
be a direct child of outer.
Attribute regular expressions
Use :matchesAttr(attr, regex) for attribute regexes in either mode, as shown in the
syntax table. See Escaping and regular expressions
for Java string examples and escaping rules.
Case matching and attribute values
In Web mode, the document's parser selects HTML rules (the default) or XML rules with
Parser.xmlParser(). The comparison table distinguishes attribute
names from values. The i and s flags affect values only: [data-sku="AbC123" i] ignores ASCII
case, while [type="TEXT" s] requires exact case. Both flags work in every query mode from 1.24.1.
For case-insensitive class matching in Web mode, use [class~="card" i] instead of .card.
See the comparison table for default class and ID case rules.
Unquoted attribute values are supported in both modes, including numbers, URLs, and text with spaces. In Web mode,
a final separate i or s is a case flag. Quote literal values ending in either token,
such as [data-x="two words i"].
-
Nested Class Summary
Nested Classes -
Method Summary
Modifier and TypeMethodDescriptionstatic StringGiven a CSS identifier (such as a tag, ID, or class), escape any CSS special characters that would otherwise not be valid in a selector.static EvaluatorevaluatorOf(String css) Parse a CSS query into an Evaluator.static ElementsFind elements matching the query, across multiple roots.static ElementsFind Elements matching the CSS query.static ElementsFind Elements matching the Evaluator.static @Nullable ElementselectFirst(String cssQuery, Iterable<Element> roots) Find the first element matching the query, across multiple roots.static @Nullable ElementselectFirst(String cssQuery, Element root) Find the first Element that matches the query.selectStream(String query, Element root) Finds a Stream of elements matching the CSS query.selectStream(Evaluator evaluator, Element root) Finds a Stream of elements matching the evaluator.static StringConsume a CSS identifier (ID or class) off the queue.
-
Method Details
-
select
Find Elements matching the CSS query.- Parameters:
query- CSS selectorroot- root element to descend into- Returns:
- matching elements, empty if none
- Throws:
Selector.SelectorParseException- (unchecked) on an invalid CSS query.
-
select
Find Elements matching the Evaluator.- Parameters:
evaluator- CSS Evaluatorroot- root (context) element to start from- Returns:
- matching elements, empty if none
-
selectStream
Finds a Stream of elements matching the CSS query.- Parameters:
query- CSS selectorroot- root element to descend into- Returns:
- a Stream of matching elements, empty if none
- Throws:
Selector.SelectorParseException- (unchecked) on an invalid CSS query.- Since:
- 1.19.1
-
selectStream
Finds a Stream of elements matching the evaluator.- Parameters:
evaluator- CSS selectorroot- root element to descend into- Returns:
- matching elements, empty if none
- Since:
- 1.19.1
-
select
Find elements matching the query, across multiple roots. Elements will be deduplicated (in the case of overlapping hierarchies).- Parameters:
query- CSS selectorroots- root elements to descend into- Returns:
- matching elements, empty if none
-
selectFirst
Find the first Element that matches the query.- Parameters:
cssQuery- CSS selectorroot- root element to descend into- Returns:
- the matching element, or null if none.
-
selectFirst
Find the first element matching the query, across multiple roots.- Parameters:
cssQuery- CSS selectorroots- root elements to descend into- Returns:
- the first matching element, or
nullif none - Since:
- 1.19.1
-
escapeCssIdentifier
Given a CSS identifier (such as a tag, ID, or class), escape any CSS special characters that would otherwise not be valid in a selector.- Since:
- 1.20.1
- See Also:
-
unescapeCssIdentifier
Consume a CSS identifier (ID or class) off the queue.Note: For backwards compatibility this method supports improperly formatted CSS identifiers, e.g.
1instead of\31.- Returns:
- The unescaped identifier.
- Throws:
IllegalArgumentException- if an invalid escape sequence was found.- Since:
- 1.20.1
- See Also:
-
evaluatorOf
Parse a CSS query into an Evaluator. If you are evaluating the same query repeatedly, it may be more efficient to parse it once and reuse the Evaluator. The evaluator captures the current value ofjsoup.queryMode; later property changes do not alter it.- Parameters:
css- CSS query- Returns:
- Evaluator
- Throws:
Selector.SelectorParseException- if the CSS query is invalid- Since:
- 1.21.1
- See Also:
-