Playwright comes with multiple built-in locators. To make tests resilient, we recommend prioritizing user-facing attributes and explicit contracts such as [`method: Page.getByRole`].
For example, consider the following DOM structure.
Note that all methods that create a locator, such as [`method: Page.getByLabel`], are also available on the [Locator] and [FrameLocator] classes, so you can chain them and iteratively narrow down your locator.
The [`method: Page.getByRole`] locator reflects how users and assistive technology perceive the page, for example whether some element is a button or a checkbox. When locating by role, you should usually pass the accessible name as well, so that the locator pinpoints the exact element.
For example, consider the following DOM structure.
```html
<h1>my form<h1>
<form>
<labelfor="newsletter">newsletter</label>
<inputtype="checkbox"checkedid="newsletter">
<button>Submit</button>
<form>
```
<imgwidth="247"alt="form with newsletter checkbox that is checked and a submit button"src="https://user-images.githubusercontent.com/13063165/201355711-20f1f45d-81b5-42b1-8932-397c935cedd8.png"/>
<imgwidth="239"alt="form with newsletter checkbox unchecked and submit button highlighted"src="https://user-images.githubusercontent.com/13063165/201134851-f707a433-1e10-4b83-b648-c19df2a04de0.png"/>
Role locators include [buttons, checkboxes, headings, links, lists, tables, and many more](https://www.w3.org/TR/html-aria/#docconformance) and follow W3C specifications for [ARIA role](https://www.w3.org/TR/wai-aria-1.2/#roles), [ARIA attributes](https://www.w3.org/TR/wai-aria-1.2/#aria-attributes) and [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).
Most form controls usually have dedicated labels that could be conveniently used to interact with the form. In this case, you can locate the control by its associated label using [`method: Page.getByLabel`].
For example, consider the following DOM structure.
<imgwidth="220"alt="password input with label of password"src="https://user-images.githubusercontent.com/13063165/201115785-f996ede8-01ae-4aa6-bd76-a60600efa125.png"/>
<imgwidth="225"alt="password input with label and password filled in with encryption"src="https://user-images.githubusercontent.com/13063165/201113928-3b383887-433c-4b2c-9516-6c17d87d1eb2.png"/>
Inputs may have a placeholder attribute to hint to the user what value should be entered. You can locate such an input using [`method: Page.getByPlaceholder`].
For example, consider the following DOM structure.
<imgwidth="155"alt="input field filled in with name@example.com"src="https://user-images.githubusercontent.com/13063165/201114537-597b157f-68f3-473e-8802-4c099d1d6e93.png"/>
<imgwidth="155"alt="input field filled in with playwright@microsoft.com"src="https://user-images.githubusercontent.com/13063165/201114985-c5bb9709-3680-4137-a3d9-e272056ca79e.png"/>
:::tip When to use placeholder locators
Use this locator when locating form elements that do not have labels but do have placeholder texts.
Matching by text always normalizes whitespace, even with exact match. For example, it turns multiple spaces into one, turns line breaks into spaces and ignores leading and trailing whitespace.
We recommend using text locators to find non interactive elements like `div`, `span`, `p`, etc. For interactive elements like `button`, `a`, `input`, etc. use [role locators](#locate-by-role).
:::
You can also [filter by text](#filter-by-text) which can be useful when trying to find a particular item in a list.
All images should have an `alt` attribute that describes the image. You can locate an image based on the text alternative using [`method: Page.getByAltText`].
<imgwidth="49"alt="playwright logo being clicked"src="https://user-images.githubusercontent.com/13063165/201134010-4b7af9fc-cfaf-42b5-b968-79b2e1921b57.png"/>
:::tip When to use alt locators
Use this locator when your element supports alt text such as `img` and `area` elements.
Testing by test ids is the most resilient way of testing as even if your text or role of the attribute changes the test will still pass. QA's and developers should define explicit test ids and query them with [`method: Page.getByTestId`]. However testing by test ids is not user facing. If the role or text value is important to you then consider using user facing locators such as [role](#locate-by-role) and [text locators](#locate-by-text).
For example, consider the following DOM structure.
<imgwidth="72"alt="button with Itinéraire text showing click action"src="https://user-images.githubusercontent.com/13063165/201133588-789926c6-9de3-4866-abd9-8d0a7e3fa95d.png"/>
You can also use test ids when you choose to use the test id methodology or when you can't locate by [role](#locate-by-role) or [text](#locate-by-text).
By default, [`method: Page.getByTestId`] will locate elements based on the `data-testid` attribute, but you can configure it in your test config or by calling [`method: Selectors.setTestIdAttribute`].
If you absolutely must use CSS or XPath locators, you can use [`method: Page.locator`] to create a locator that takes a [selector](./selectors.md) describing how to find an element in the page. Playwright supports CSS and XPath selectors, and auto-detects them if you omit `css=` or `xpath=` prefix.
XPath and CSS selectors can be tied to the DOM structure or implementation. These selectors can break when the DOM structure changes. Long CSS or XPath chains below are an example of a **bad practice** that leads to unstable tests:
CSS and XPath are not recommended as the DOM can often change leading to non resilient tests. Instead, try to come up with a locator that is close to how the user perceives the page such as [role locators](#locate-by-role) or [define an explicit testing contract](#locate-by-testid) using test ids.
<imgwidth="101"alt="Title, shadow-root and details"src="https://user-images.githubusercontent.com/13063165/201364144-fe6ea945-505d-4650-bbe4-056439c311c8.png"/>
You can locate in the same way as if the shadow root was not present at all.
<imgwidth="100"alt="Title, shadow-root and details with text details highlighted"src="https://user-images.githubusercontent.com/13063165/201364000-42820d75-053f-449e-80bf-8b38470a2a23.png"/>
<imgwidth="103"alt="Title, shadow-root and details highlighted"src="https://user-images.githubusercontent.com/13063165/201363856-09fd3eec-6e55-45fe-a251-7b39c3ea09d7.png"/>
Consider the following DOM structure where we want to click on the buy button of the second product card. We have a few options in order to filter the locators to get the right one.
<imgwidth="83"alt="2 product cards with text and a button"src="https://user-images.githubusercontent.com/13063165/201182468-348eb733-7cf5-4e5b-94de-604312fe5fc7.png"/>
Locators can be filtered by text with the [`method: Locator.filter`] method. It will search for a particular string somewhere inside the element, possibly in a descendant element, case-insensitively. You can also pass a regular expression.
.Filter(new() { HasTextRegex = new Regex("Product 2") })
.GetByRole(AriaRole.Button, new () { NameString = "Buy" })
.ClickAsync();
```
<imgwidth="83"alt="2 product cards with text and a button and the second one being highlighted"src="https://user-images.githubusercontent.com/13063165/201182749-253ff808-cd70-4d42-88c6-a9f07fb7eeae.png"/>
### Filter by another locator
Locators support an option to only select elements that have a descendant matching another locator. You can therefore filter by any other locator such as a [`method: Locator.getByRole`], [`method: Locator.getByTestId`], [`method: Locator.getByText`] etc.
<imgwidth="83"alt="2 product cards with text and a button and the second one being highlighted"src="https://user-images.githubusercontent.com/13063165/201182749-253ff808-cd70-4d42-88c6-a9f07fb7eeae.png"/>
You can chain methods that create a locator, like [`method: Page.getByText`] or [`method: Locator.getByRole`], to narrow down the search to a particular part of the page.
In this example we first create a locator called product by locating the test id. We then filter by text. We can use the product locator again to get by role of button and click it and then use an assertion to make sure there is only one product with the text ' Product 2'.
<imgwidth="83"alt="2 product cards with text and a button and the second one being highlighted"src="https://user-images.githubusercontent.com/13063165/201182749-253ff808-cd70-4d42-88c6-a9f07fb7eeae.png"/>
## Lists
### Count items in a list
You can assert locators in order to count the items in a list.
For example, consider the following DOM structure:
```html
<ul>
<li>apple</li>
<li>banana</li>
<li>orange</li>
</ul>
```
<imgwidth="81"alt="list of 3 items, apple, banana and orange"src="https://user-images.githubusercontent.com/13063165/200641602-95a801ce-8a3e-4141-b4ac-926b890f4648.png"/>
Use the count assertion to ensure that the list has 3 items.
You can assert locators in order to find all the text in a list.
For example, consider the following DOM structure:
```html
<ul>
<li>apple</li>
<li>banana</li>
<li>orange</li>
</ul>
```
<imgwidth="81"alt="list of 3 items, apple, banana and orange"src="https://user-images.githubusercontent.com/13063165/200641602-95a801ce-8a3e-4141-b4ac-926b890f4648.png"/>
Use [`method: LocatorAssertions.toHaveText`] to ensure that the list has the text "apple", "banana" and "orange".
There are many ways to get a specific item in a list.
#### Get by text
Use the [`method: Page.getByText`] method to locate an element in a list by it's text content and then click on it.
For example, consider the following DOM structure:
```html
<ul>
<li>apple</li>
<li>banana</li>
<li>orange</li>
</ul>
```
Locate an item by it's text content and click it.
```js
await page.getByText('orange')
.click();
```
```python async
await page.get_by_text("orange").click()
```
```python sync
page.get_by_text("orange").click()
```
```java
page.getByText("orange")
.click();
```
```csharp
await page.GetByText("orange")
.ClickAsync();
```
<imgwidth="78"alt="list of apple, banana and orange highlighting orange"src="https://user-images.githubusercontent.com/13063165/201171918-0f689261-a48e-4660-9726-c8fcf29e9105.png"/>
#### Filter by text
Use the [`method: Locator.filter`] to locate a specific item in a list.
For example, consider the following DOM structure:
```html
<ul>
<li>apple</li>
<li>banana</li>
<li>orange</li>
</ul>
```
Locate an item by the role of "listitem" and then filter by the text of "orange" and then click it.
<imgwidth="78"alt="list of apple, banana and orange highlighting orange"src="https://user-images.githubusercontent.com/13063165/201171918-0f689261-a48e-4660-9726-c8fcf29e9105.png"/>
#### Get by test id
Use the [`method: Page.getByTestId`] method to locate an element in a list. You may need to modify the html and add a test id if you don't already have a test id.
For example, consider the following DOM structure:
```html
<ul>
<lidata-testid='apple'>apple</li>
<lidata-testid='banana'>banana</li>
<lidata-testid='orange'>orange</li>
</ul>
```
Locate an item by it's test id of "orange" and then click it.
```js
await page.getByTestId('orange')
.click();
```
```python async
await page.get_by_test_id("orange").click()
```
```python sync
page.get_by_test_id("orange").click()
```
```java
page.getByTestId("orange")
.click();
```
```csharp
await page.GetByTestId("orange")
.ClickAsync();
```
<imgwidth="78"alt="list of apple, banana and orange highlighting orange"src="https://user-images.githubusercontent.com/13063165/201171918-0f689261-a48e-4660-9726-c8fcf29e9105.png"/>
#### Get by nth item
If you have a list of identical elements, and the only way to distinguish between them is the order, you can choose a specific element from a list with [`method: Locator.first`], [`method: Locator.last`] or [`method: Locator.nth`].
However, use this method with caution. Often times, the page might change, and the locator will point to a completely different element from the one you expected. Instead, try to come up with a unique locator that will pass the [strictness criteria](#strictness).
### Chaining filters
When you have elements with various similarities, you can use the [`method: Locator.filter`] method to select the right one. You can also chain multiple filters to narrow down the selection.
For example, consider the following DOM structure:
```html
<ul>
<li>
<div>John</div>
<div><button>Say hello</button></div>
</li>
<li>
<div>Mary</div>
<div><button>Say hello</button></div>
</li>
<li>
<div>John</div>
<div><button>Say goodbye</button></div>
</li>
<li>
<div>Mary</div>
<div><button>Say goodbye</button></div>
</li>
</ul>
```
<imgwidth="112"alt="text John and Mary with buttons say hello and say goodbye beside their names"src="https://user-images.githubusercontent.com/13063165/201173459-4c560974-6712-48a6-8b6d-a636276f0dd3.png"/>
To take a screenshot of the row with "Mary" and "Say goodbye":
You should now have a "screenshot.png" file in your project's root directory.
<imgwidth="153"alt="text Mary with buttons say goodbye"src="https://user-images.githubusercontent.com/13063165/201357594-7910f8ad-7626-48a4-85a2-2a3e63d67e34.png"/>
var texts = await rows.EvaluateAllAsync("list => list.map(element => element.textContent)");
```
## Strictness
Locators are strict. This means that all operations on locators that imply
some target DOM element will throw an exception if more than one element matches. For example, the following call throws if there are several buttons in the DOM:
You can explicitly opt-out from strictness check by telling Playwright which element to use when multiple elements match, through [`method: Locator.first`], [`method: Locator.last`], and [`method: Locator.nth`]. These methods are **not recommended** because when your page changes, Playwright may click on an element you did not intend. Instead, follow best practices above to create a locator that uniquely identifies the target element.