Check whether an element exists in Playwright
Use retrying assertions for DOM presence and visibility, and understand when count() or isVisible() only gives a snapshot.
Use await expect(locator).toBeVisible() when a test requires a visible element. Use toBeAttached() when DOM presence is sufficient. For a conditional branch, isVisible() or count() can inspect the current state, but they do not wait for a future render.
Presence, visibility, and the number of matches are different requirements. None alone guarantees that an element can receive a click.
Prefer retrying assertions
These fragments assume a configured Playwright Test project with expect imported and a test page already loaded:
// Visible: attached, with a nonempty bounding box and visible style.
await expect(page.getByRole('button', { name: 'Submit' })).toBeVisible();
// Attached to the DOM, even if hidden with display: none.
await expect(page.locator('#modal')).toBeAttached();
// Hidden or absent, versus specifically absent from the DOM.
await expect(page.locator('.error')).toBeHidden();
await expect(page.locator('.error')).not.toBeAttached();
// Exactly five matching elements.
await expect(page.locator('ul > li')).toHaveCount(5);
Playwright's asynchronous assertions retry until they pass or reach the assertion timeout, which defaults to five seconds. A one-time if check does not provide that behavior.
Use snapshots for conditional logic
For example, close an optional dialog if it is currently present and visible:
const dialog = page.locator('.dialog').first();
if (await dialog.count() > 0 && await dialog.isVisible()) {
await dialog.getByRole('button', { name: 'Close' }).click();
}
isVisible() returns immediately. This branch can miss a dialog that appears asynchronously afterward. Wait for an application-specific readiness signal first, or use a bounded wait if that matches the flow. For a required element, use an assertion instead of silently skipping the interaction.
Choose the right condition
| Method | What it checks | Appropriate use |
|---|---|---|
toBeAttached() |
The element is attached to the DOM | Presence regardless of visibility |
toBeVisible() |
A nonempty bounding box and visibility other than hidden |
Visibility, not viewport position or clickability; opacity: 0 can still count as visible |
toHaveCount(n) |
The number of matches | Lists and duplicate-element checks |
Frequently asked questions
Should I use isVisible() or toBeVisible()?
Use the assertion to express a required test outcome. Use the immediate query for a deliberate branch based on current state. The assertion is usually the better default for verifying a page.
Why does my if miss an element that appears later?
count() can run before rendering finishes. Use locator.waitFor() for an explicit wait, or let a retrying assertion wait for the required condition. For optional elements, define when the application is ready before deciding that absence is meaningful.
How do I wait for an element to disappear?
Use toBeHidden() if hiding is sufficient, or not.toBeAttached() if it must leave the DOM. Both retry and are useful when waiting for a loading indicator to go away.
References
Related reading
Contact us
Join the community
to join the community
Try Guance
Start online and pay only for what you use.
Get startedChoose a Guance plan