🎯 Playwright Advanced Locator Operations
The Treasure Hunter’s Toolkit
Imagine you’re a treasure hunter in a huge museum. You need to find specific artifacts. Sometimes the artifact is easy to find. But sometimes, you need special tools to narrow down exactly which treasure you want.
Playwright locators are like your treasure-hunting toolkit. Today, we learn the advanced tools that help you find elements with laser precision!
đź”— Locator Chaining: Following the Trail
What Is It?
Think of a trail of breadcrumbs. Each breadcrumb leads to the next, until you reach the treasure.
Locator chaining means: Start somewhere, then go deeper step by step.
Simple Example
Imagine a house with many rooms. Inside one room, there’s a toy box. Inside the toy box, there’s a red ball.
// Find the room first
const room = page.locator('.bedroom');
// Then find the toy box inside that room
const toyBox = room.locator('.toy-box');
// Then find the red ball inside
const ball = toyBox.locator('.red-ball');
Real World Use
// Find a table, then a specific row,
// then a button in that row
const table = page.locator('table.users');
const row = table.locator('tr:has-text("John")');
const editBtn = row.locator('button.edit');
await editBtn.click();
Why It’s Powerful
- You break down complex searches into simple steps
- Each step narrows your search
- Makes your code easy to read and debug
graph TD A[Page] --> B[Table] B --> C[Row with "John"] C --> D[Edit Button] style D fill:#4CAF50,color:white
🔍 The .filter() Method: Be Picky!
What Is It?
You have a basket of fruits. You want only red apples. The .filter() method helps you pick exactly what you want.
How It Works
The .filter() method takes a locator and keeps only the ones that match your conditions.
Example: Filter by Text
// Find all buttons
const buttons = page.locator('button');
// Keep only the one that says "Submit"
const submitBtn = buttons.filter({
hasText: 'Submit'
});
Example: Filter by Child Element
// Find all product cards
const cards = page.locator('.product-card');
// Keep only cards that have a sale badge
const saleCards = cards.filter({
has: page.locator('.sale-badge')
});
Example: Filter by NOT Having
// Find all items
const items = page.locator('.item');
// Keep items WITHOUT a "sold out" label
const available = items.filter({
hasNot: page.locator('.sold-out')
});
Quick Reference
| Filter Option | What It Does |
|---|---|
hasText |
Matches text content |
has |
Must contain element |
hasNot |
Must NOT contain element |
📍 Locator Positioning Methods: Neighbors & Family
What Is It?
Sometimes you want to find something based on its neighbors. Like finding your friend’s house because it’s next to the park.
.first() - The First One
// Get the first button on the page
const firstBtn = page.locator('button').first();
.last() - The Last One
// Get the last item in a list
const lastItem = page.locator('li').last();
.nth(index) - Pick by Position
// Get the third image (index starts at 0)
const thirdImg = page.locator('img').nth(2);
Family Tree Diagram
graph TD A[List of Items] --> B["first#40;#41; → Item 1"] A --> C["nth#40;1#41; → Item 2"] A --> D["nth#40;2#41; → Item 3"] A --> E["last#40;#41; → Item 4"] style B fill:#2196F3,color:white style E fill:#FF9800,color:white
Pro Tip
Avoid using position methods when you can use better filters. Positions can change if the page changes!
âž• The .and() Method: Both Must Be True
What Is It?
Imagine you’re looking for a friend who is tall AND has red hair. Both conditions must be true.
How It Works
.and() combines two locators. The element must match both.
Example
// Find a button
const button = page.locator('button');
// That is also visible
const visibleBtn = button.and(
page.locator(':visible')
);
// That also has specific class
const submitBtn = page.locator('button').and(
page.locator('.primary')
);
Real World Use
// Find an input field
const input = page.locator('input');
// That is ALSO a required field
const required = input.and(
page.locator('[required]')
);
await required.fill('Hello');
Visual Explanation
graph LR A[Locator A] --> C{AND} B[Locator B] --> C C --> D[Must Match Both!] style C fill:#9C27B0,color:white style D fill:#4CAF50,color:white
🔀 The .or() Method: Either One Works
What Is It?
Now you’re looking for someone who is a teacher OR a doctor. Either condition is fine!
How It Works
.or() combines two locators. The element can match either one.
Example
// Find submit button OR save button
const saveOrSubmit = page.locator('button.submit')
.or(page.locator('button.save'));
await saveOrSubmit.click();
Real World Use
// Some sites use different selectors
// for mobile vs desktop
const menu = page.locator('.mobile-menu')
.or(page.locator('.desktop-menu'));
// Clicks whichever exists!
await menu.click();
Visual Explanation
graph LR A[Locator A] --> C{OR} B[Locator B] --> C C --> D[Match Either One] style C fill:#FF5722,color:white style D fill:#4CAF50,color:white
When To Use .or()
- Different pages have different layouts
- Handling mobile vs desktop elements
- Fallback options for clicking
🔢 The .count() Method: How Many?
What Is It?
Before picking apples, you want to know: How many apples are there?
How It Works
.count() returns the number of matching elements.
Example
// How many products are shown?
const products = page.locator('.product');
const total = await products.count();
console.log(`Found ${total} products!`);
Use Cases
// Check if search has results
const results = page.locator('.result');
const count = await results.count();
if (count === 0) {
console.log('No results found!');
} else {
console.log(`Found ${count} results`);
}
Loop Through All Matches
const items = page.locator('.item');
const count = await items.count();
for (let i = 0; i < count; i++) {
const text = await items.nth(i).textContent();
console.log(`Item ${i}: ${text}`);
}
Remember
.count()is async - always useawait- Returns
0if nothing matches - Great for validating search results
⚠️ Strict Mode: One and Only One
What Is It?
Strict mode is like saying: “I expect exactly one answer. If there are more or none, tell me!”
How It Works
By default, Playwright locators are strict. When you try to interact with a locator that matches multiple elements, Playwright throws an error.
The Strict Rule
// âś… GOOD: Only one element matches
await page.locator('#unique-button').click();
// ❌ ERROR: Multiple elements match!
// This will fail in strict mode
await page.locator('button').click();
// Error: strict mode violation
Why Strict Mode Exists
Imagine clicking a button, but there are 3 buttons that match. Which one gets clicked? That’s confusing!
Strict mode protects you from accidental clicks on wrong elements.
Working With Multiple Elements
When you intentionally want multiple elements:
// Get all buttons (returns locator,
// no strict violation)
const buttons = page.locator('button');
// Use .first(), .last(), or .nth()
await buttons.first().click();
// Or use .count() and loop
const count = await buttons.count();
Checking Before Acting
const btn = page.locator('.submit-btn');
// Check count first
if (await btn.count() === 1) {
await btn.click();
} else if (await btn.count() > 1) {
// Handle multiple matches
await btn.first().click();
} else {
console.log('Button not found!');
}
Visual Summary
graph TD A[Locator Action] --> B{How many matches?} B -->|0| C[❌ Error: Not Found] B -->|1| D[✅ Success!] B -->|2+| E[❌ Error: Strict Mode] style D fill:#4CAF50,color:white style C fill:#f44336,color:white style E fill:#f44336,color:white
🎯 Putting It All Together
Combo Example
Let’s use all the tools in one search!
// Start with a table
const table = page.locator('table.orders');
// Chain: find rows in the table
const rows = table.locator('tr');
// Filter: only rows with "Pending" status
const pending = rows.filter({
has: page.locator('.status:has-text("Pending")')
});
// Count them
const count = await pending.count();
console.log(`${count} pending orders`);
// Get the first one's action button
const firstPending = pending.first();
const actionBtn = firstPending.locator('.action');
// Or use .and() for extra safety
const safeBtn = actionBtn.and(
page.locator('[data-action="approve"]')
);
await safeBtn.click();
Cheat Sheet
| Method | Purpose | Example |
|---|---|---|
| Chaining | Go deeper | parent.locator(child) |
.filter() |
Be picky | .filter({hasText: 'X'}) |
.first() |
First match | .first() |
.last() |
Last match | .last() |
.nth(n) |
Specific position | .nth(2) |
.and() |
Both conditions | a.and(b) |
.or() |
Either condition | a.or(b) |
.count() |
How many | await loc.count() |
| Strict Mode | Expect exactly 1 | (default behavior) |
🏆 You Did It!
You now have the complete treasure hunter toolkit! You can:
- Chain locators to follow a trail
- Filter to be super picky
- Use positioning to grab by order
- Combine with and/or for complex searches
- Count before you act
- Understand strict mode safety
Go forth and locate elements like a pro! 🚀