codeceptjs
Version:
Supercharged End 2 End Testing Framework for NodeJS
126 lines (87 loc) • 4.87 kB
Markdown
---
permalink: /element-selection
title: Element Selection
---
# Element Selection
When you write `I.click('a')` and there are multiple links on a page, CodeceptJS clicks the **first** one it finds. Most of the time this is exactly what you need — your locators are specific enough that there's only one match, or the first match happens to be the right one.
But what happens when it's not?
## Picking a Specific Element
Say you have a list of items and you want to click the second one. You could write a more specific CSS selector, but sometimes the simplest approach is to tell CodeceptJS which element you want by position:
```js
import step from 'codeceptjs/steps'
// click the 2nd link
I.click('a', step.opts({ elementIndex: 2 }))
// click the last link
I.click('a', step.opts({ elementIndex: 'last' }))
// fill the last matching input
I.fillField('.email-input', 'test@example.com', step.opts({ elementIndex: -1 }))
```
The `elementIndex` option accepts:
* **Positive numbers** (1-based) — `1` is first, `2` is second, `3` is third
* **Negative numbers** — `-1` is last, `-2` is second-to-last
* **`'first'`** and **`'last'`** as readable aliases
This works with any action that targets a single element: `click`, `doubleClick`, `rightClick`, `fillField`, `appendField`, `clearField`, `checkOption`, `selectOption`, `attachFile`, and others.
If only one element matches the locator, `elementIndex` is silently ignored — you always get that single element regardless of the index value. This is convenient when the number of matches depends on page state: you won't get an error if the list happens to have just one item.
When multiple elements exist but the index is out of range, CodeceptJS throws a clear error:
```
elementIndex 100 exceeds the number of elements found (3) for "a"
```
You can combine `elementIndex` with other step options:
```js
I.click('a', step.opts({ elementIndex: 2 }).timeout(5).retry(3))
```
## Strict Mode
If you'd rather not silently click the first of many matches, enable `strict: true` in your helper configuration. This makes CodeceptJS throw an error whenever a locator matches more than one element, forcing you to write precise locators:
```js
// codecept.conf.js
helpers: {
Playwright: {
url: 'http://localhost',
browser: 'chromium',
strict: true,
}
}
```
Now any ambiguous locator will fail immediately:
```js
I.click('a') // MultipleElementsFound: Multiple elements (3) found for "a" in strict mode
```
This is useful on projects where you want to catch accidental matches early — clicking the wrong button because of a vague locator is a common source of flaky tests.
When a test fails in strict mode, the error includes a `fetchDetails()` method that lists the matched elements with their XPath and simplified HTML, so you can see exactly what was found and write a better locator:
```js
// Multiple elements (3) found for "a" in strict mode. Call fetchDetails() for full information.
// After fetchDetails():
// /html/body/div/a[1] <a id="first-link">First</a>
// /html/body/div/a[2] <a id="second-link">Second</a>
// /html/body/div/a[3] <a id="third-link">Third</a>
// Use a more specific locator or grabWebElements() to work with multiple elements
```
Strict mode is supported in **Playwright**, **Puppeteer**, and **WebDriver** helpers.
### Per-Step Strict Mode with `exact`
You don't have to enable strict mode globally. Use `exact: true` to enforce it on a single step — handy when most of your tests are fine with default behavior but a particular action needs to be precise:
```js
import step from 'codeceptjs/steps'
I.click('a', step.opts({ exact: true }))
// throws MultipleElementsFound if more than one link matches
```
`strictMode: true` is an alias if you prefer a more descriptive name:
```js
I.click('a', step.opts({ strictMode: true }))
```
It works the other way too. If your helper has `strict: true` globally but you need to relax it for one step, use `exact: false`:
```js
// strict: true in config, but this step allows multiple matches
I.click('a', step.opts({ exact: false }))
```
And when you know there are multiple matches and want a specific one, `elementIndex` also overrides the strict check — no error is thrown because you've explicitly chosen which element to use:
```js
// strict: true in config, but this works without error
I.click('a', step.opts({ elementIndex: 2 }))
```
## Summary
| Situation | Approach |
|-----------|----------|
| You want to catch ambiguous locators early | Enable `strict: true` in helper config |
| You need a specific element from a known list | Use `step.opts({ elementIndex: N })` |
| You want to iterate over all matching elements | Use [`eachElement`](/els) from the `els` module |
| You need full control over element inspection | Use [`grabWebElements`](/WebElement) to get all matches |