- Jul 06, 2026
- admin
- 0

Advanced locators are one of the most powerful features in Playwright. They let you create stable, readable, and intelligent selectors without relying on brittle XPath or CSS selectors.
- Filtering with filter()
Use filter() to narrow down matching elements.
HTML
<ul>
<li>Apple <button>Add</button></li>
<li>Orange <button>Add</button></li>
<li>Mango <button>Add</button></li>
</ul>
Playwright
await page.locator(‘li’).filter({ hasText: ‘Orange’ }).getByRole(‘button’).click();
- hasText
Matches elements containing specific text.
await page.locator(‘article’, {hasText: ‘Playwright’}).click();
Partial matching works.
await page.locator(‘div’, {hasText: ‘Welcome’});
- has
Find a parent containing a child element.
HTML
<div class=”card”>
<h2>iPhone</h2>
<button>Buy</button>
</div>
<div class=”card”>
<h2>Samsung</h2>
<button>Buy</button>
</div>
Playwright
await page.locator(‘.card’, {has: page.locator(‘h2’, {hasText: ‘Samsung’})}).getByRole(‘button’).click();
Very useful for cards, tables and forms.
- nth()
Select a particular element.
await page.locator(‘.product’).nth(2).click();
Third product.
- first()
await page.locator(‘.menu-item’).first().click();
- last()
await page.locator(‘.menu-item’).last().click();
- or()
Locate either one element or another.
const login = page.getByRole(‘button’, { name: ‘Login’ });
const signin = page.getByRole(‘button’, { name: ‘Sign In’ });
await login.or(signin).click();
Useful when UI varies across environments.
- and()
Combine two locator conditions.
await page.getByRole(‘button’).and(page.getByTitle(‘Submit’)).click();
Both conditions must match.
- Chaining Locators
Instead of long XPath:
//table//tr[2]//td[3]//button
Use:
await page.locator(‘table’).locator(‘tr’).nth(1).locator(‘td’).nth(2).getByRole(‘button’).click();
More readable.
- Relative Locators using locator()
const card = page.locator(‘.product-card’).nth(0);
await card.getByRole(‘button’).click();
Search only inside that card.
- Locate by Role
Preferred approach.
await page.getByRole(‘textbox’, {name: ‘Username’}).fill(‘admin’);
- Locate by Label
await page.getByLabel(‘Email’).fill(‘abc@gmail.com’);
- Locate by Placeholder
await page.getByPlaceholder(‘Search’).fill(‘Playwright’);
- Locate by Alt Text
await page.getByAltText(‘Company Logo’).click();
- Locate by Title
await page.getByTitle(‘Delete’).click();
- Locate by Test ID
await page.getByTestId(‘login-btn’).click();
- Combining Filters
await page.locator(‘.employee’).filter({ hasText: ‘John’ }).filter({has: page.getByRole(‘button’, {name: ‘Edit’})}).click();
- Exact Text Matching
await page.getByText(‘Submit’, {exact: true});
Without exact, “Submit Now” would also match.
- Visible Elements Only
await page.locator(‘button:visible’).click();
Useful when hidden duplicate elements exist.
- Locator Reuse
const username =page.getByLabel(‘Username’);
await username.fill(‘admin’);
await expect(username).toHaveValue(‘admin’);
Avoids repeating locator definitions.
Best Practices
| ✅ Recommended | ❌ Avoid |
| getByRole() | Long XPath (//div[2]/div[3]/…) |
| getByLabel() | CSS based on position |
| getByTestId() | Dynamic IDs |
| filter({ hasText }) | Absolute XPath |
| has() | Fragile DOM traversal |
| Chained locators | Deep CSS selectors |
| or() and and() | Duplicate test logic |
Order of Preference
- getByRole()
- getByLabel()
- getByPlaceholder()
- getByTestId()
- getByText()
- filter({ has })
- locator()
- CSS selectors
- XPath (only when necessary)
Mastering filter(), has(), hasText(), or(), and and() will allow you to write robust, maintainable Playwright tests that are far less likely to break as the UI evolves. These advanced locator APIs are among the biggest advantages Playwright has over many older browser automation approaches.

