Positional Selectors: :eq and :nth
Relevant source files
The following files were used as context for generating this wiki page:
cheerio-select acts as a thin wrapper around the css-select library to provide support for jQuery-style positional pseudo-selectors. These selectors allow developers to query elements based on their index or position within a set of matched elements, rather than just their attributes or tree relationships.
The primary positional selectors are :eq(index) and :nth(index), which are functionally equivalent in this implementation. They allow for zero-indexed selection of elements, including support for negative indices to select from the end of the set.
Sources: README.md:1-12, src/index.ts:114-124
Architecture and Logic
The processing of positional selectors is handled through a filtering mechanism that occurs after initial element sets are identified. The engine differentiates between "plain" CSS selectors and "filtered" selectors (positionals).
Execution Flow
When a selector contains a positional like :eq, the engine follows a specific sequence:
- Parsing: The selector string is parsed into tokens.
- Grouping: Selectors are grouped into plain and filtered categories.
- Limiting: The engine determines the maximum number of elements needed (e.g.,
:eq(0)only needs the first match). - Filtering: The
filterByPositionfunction applies the specific logic for the pseudo-class.
The diagram shows the decision path within the engine when encountering positional pseudos versus standard CSS selectors.
Sources: src/index.ts:54-65, src/index.ts:182-195, src/index.ts:205-212
Implementation of :eq and :nth
The :eq and :nth selectors utilize the filterByPosition function. This function takes the requested index (data) and the current list of candidate elements.
Index Handling
- Positive Index: Selects the element at
elements[index]. - Negative Index: Selects the element at
elements[elements.length + index]. - Validation: If the index is not a finite number or is out of bounds, an empty array is returned.
| Selector | Behavior | Logic |
|---|---|---|
:eq(index) | Selects element at specified index | elements[index] or elements[len + index] |
:nth(index) | Equivalent to :eq | Same as :eq |
Sources: src/index.ts:114-124, README.md:8-9
Code Snippet: filterByPosition logic
// From src/index.ts:114-124
case "nth":
case "eq": {
return Number.isFinite(position) &&
Math.abs(position) < elements.length
? [
position < 0
? elements[elements.length + position]
: elements[position],
]
: [];
}
Sources: src/index.ts:114-124
Performance Optimization: Limits
A key feature of the positional implementation is the getLimit optimization. If a selector ends with :eq(n), the engine can stop searching once it has gathered enough elements to satisfy the index. For example, :eq(2) only requires the engine to find 3 elements (indices 0, 1, and 2).
This sequence illustrates how the engine uses the positional data to limit the expensive DOM traversal operations.
Sources: src/index.ts:213-225, src/index.spec.ts:119-123
Interaction with Other Selectors
Positional selectors can be used in complex, chained queries. When a positional is in the middle of a selector (e.g., div:eq(0) span), the engine must resolve the positional first and then treat the resulting element as a new context for the remaining selectors.
- Context Shifting: If a traversal (like a space or
>) follows a positional, the engine prepares a new context. - Root Functionality: A
rootFuncis often applied to ensure that subsequent traversals stay within the scope of the elements matched by the positional selector.
Sources: src/index.ts:241-267, test/advanced-selectors.spec.ts:37-41
Summary
The :eq and :nth selectors are essential components of cheerio-select that bridge the gap between standard CSS and jQuery's selector API. By implementing these as post-process filters with integrated limit optimizations, the library maintains high performance while providing flexible indexing capabilities, including support for negative offsets and complex chained selections.
Sources: README.md:1-12, src/index.ts:213-225