Locators: Finding Elements the Way a User Would
Why accessible, role-based locators are Playwright's recommended default, and how to rank a set of candidate locators by real-world stability.
What you'll learn
- Use role-based, accessible locators as the default choice for finding elements
- Explain why a locator is a lazy, re-evaluated query rather than a one-time element reference
- Rank competing locator strategies by stability and honest user-facing intent
Prerequisites
Explanation
A Playwright locator (page.getByRole("button", { name: "Submit" })) is not a reference to a specific DOM element captured at the moment you write the line — it's a lazy, re-evaluated query: every time an action or assertion uses that locator, Playwright re-runs the query against the current page. This is the mechanism behind auto-waiting (covered next lesson): the locator doesn't fail immediately if the element isn't there yet, it keeps re-querying until the element appears (or a timeout elapses), which is fundamentally different from most older automation tools' "find the element once, fail immediately if it isn't there yet" model.
Playwright's recommended default is a role-based, accessible locator: page.getByRole("button", { name: "Submit" }), page.getByLabel("Email address"), page.getByText("Welcome back"). These target the page the way a real user (or an assistive-technology user) actually perceives it — by role and visible/accessible label — rather than by internal implementation details like a CSS class name or a DOM structure. This has a genuine double benefit: the tests are more resilient to internal refactors (a CSS class renamed for styling reasons doesn't break a role-based locator), and writing tests this way tends to surface real accessibility gaps in the application under test, since an element with no discoverable role or accessible name is exactly as hard for getByRole to find as it is for a screen reader user to identify.
Locator stability is a genuine, orderable spectrum, worth reasoning about explicitly rather than reaching for whatever "just works" first: role/label/text-based locators (most stable — tied to what users actually perceive) > a dedicated data-testid attribute (stable, but requires deliberately adding test-only markup) > a specific, meaningful CSS selector (.submit-button, moderately stable — breaks if the class is renamed for styling reasons) > a deep, structural CSS or XPath selector (div > div:nth-child(3) > button, least stable — breaks on almost any layout change, and describes where an element sits rather than what it is). Reaching for the least-stable option first is a common, understandable mistake under time pressure that reliably produces the flakiest, most maintenance-heavy tests in a suite.
Example
Ranking candidate locator strategies by stability -- the actual reasoning behind Playwright's recommended locator priority.
function locatorStabilityScore(strategy) {
const scores = {
role: 4, // page.getByRole(...) -- tied to user-perceivable semantics
testId: 3, // page.getByTestId(...) -- stable, but test-only markup
cssClass: 2, // page.locator(".submit-button") -- breaks on style refactors
structural: 1, // page.locator("div > div:nth-child(3) > button") -- breaks on almost any layout change
};
return scores[strategy] ?? 0;
}
const candidates = ["structural", "role", "cssClass", "testId"];
const ranked = [...candidates].sort((a, b) => locatorStabilityScore(b) - locatorStabilityScore(a));
console.log(ranked); // ["role", "testId", "cssClass", "structural"] -- most to least stableTry it yourself
Add a fifth strategy 'label' (page.getByLabel) with a score matching role's stability, and re-rank the list.
Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.
Guided exercise
Guided exercise
Write rankLocators(candidates) that sorts an array of locator-strategy strings ('role','testId','cssClass','structural') from MOST to LEAST stable, using the stability scores from this lesson's explanation.
Checks: correctly ranks all four strategies by stability · does not mutate the original input array
Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.
Stuck? Get a hint.
Independent exercise
Independent exercise
Write isAccessibleLocatorStrategy(strategy) returning true only for 'role', 'label', or 'text' (Playwright's user-facing, accessible locator strategies), false for anything else (including 'testId', 'cssClass', 'structural', or any unrecognized string).
Checks: recognizes role as accessible · recognizes label as accessible · correctly rejects a non-accessible strategy · correctly rejects an unrecognized strategy
Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.
Stuck? Get a hint.
Common mistakes
- Reaching for a deep structural CSS or XPath selector first, because it 'just works' in the moment -- these are the most fragile locators and break on almost any unrelated layout change.
- Treating a locator as a one-time snapshot of an element rather than a re-evaluated query -- this misunderstanding is exactly what makes Playwright's auto-waiting (next lesson) seem confusing at first.
- Adding data-testid attributes everywhere by default instead of first checking whether a real accessible role/label already exists -- role-based locators are both more stable AND double as an accessibility check; testId is a reasonable fallback, not a default.
Knowledge check
Takeaway
A locator is a lazy, re-evaluated query, not a one-time reference — and role-based, accessible locators are Playwright's recommended default because they're both the most stable strategy across refactors and a genuine, incidental accessibility check.
Summary
Locators (getByRole, getByLabel, getByText, getByTestId, CSS/XPath) are re-evaluated every time they're used, not captured once. Role-based/accessible locators are the most stable and are Playwright's recommended default. Structural CSS/XPath selectors are the least stable, breaking on unrelated layout changes.
References
Your notes
Notes save automatically.
Finished this lesson?
Mark it complete to track your progress and schedule a future review.
AI tutor
The optional AI tutor isn't enabled in this deployment. All lessons, exercises, quizzes, and search work fully without it.