Retrieving an element’s attribute is a common part of Playwright testing. You may need to check an element’s `id`, `class`, `href`, `src`, ARIA label, or a custom `data-*` attribute before interacting with it or making an assertion.
Playwright gives you a few straightforward ways to read these values, most often through the Locator API. Once you know which method to use, you can verify UI states, inspect dynamic content, and make your tests easier to debug and maintain.
By the end of this article, you will know how to retrieve attributes in JavaScript, TypeScript, Python, and Java, check them in assertions, and handle cases where an attribute may be missing.
What does it mean to get an element’s attribute in Playwright?
In Playwright, getting an element’s attribute refers to extracting the value of a specific attribute for a given element in the DOM. This can be crucial for assertions, verifying UI states, and debugging tests. Playwright provides robust methods to handle this, ensuring that you can interact with and validate the state of web elements efficiently.
Understanding Locator.getAttribute and ElementHandle.getAttribute
Playwright provides two primary methods to get an element’s attribute: Locator.getAttribute and ElementHandle.getAttribute.
- Locator.getAttribute: Works with Playwright’s Locator API and is the recommended approach for locating elements in modern test automation. It returns the value of the specified attribute from the matching element.
- ElementHandle.getAttribute: This method is used when you have an ElementHandle object, which directly represents a DOM element. You call this method on an existing element handle to fetch the value of its attribute.
Read More: Playwright vs Cypress: A Comparison
Playwright getAttribute() Syntax and Examples
The basic syntax is:
locator.getAttribute(attributeName)
You first create a locator for the element, then pass the name of the attribute you want to retrieve.
JavaScript and TypeScript
Use getAttribute() with the attribute name:
const src = await page.locator('img').getAttribute('src');
console.log(src);This returns the value of the image’s src attribute. If the attribute does not exist, the result is null.
You can also store the locator first when you plan to reuse it:
const submitButton = page.locator('button[type="submit"]');
const ariaLabel = await submitButton.getAttribute('aria-label');
console.log(ariaLabel);
PythonIn Python, the equivalent method is get_attribute():
placeholder = page.locator('input[type="text"]').get_attribute('placeholder')
print(placeholder)When using Playwright’s asynchronous Python API, add await:
placeholder = await page.locator('input[type="text"]').get_attribute('placeholder')
print(placeholder)The method returns None when the attribute is missing.
Java
In Java, use getAttribute() on the locator:
String placeholder = page
.locator("input[name='email']")
.getAttribute("placeholder");
System.out.println(placeholder);The method returns null if the selected element does not contain the requested attribute.
Across all three languages, make sure your locator points to the correct element before reading the attribute. A broad selector may match a different element than you expect, especially when several similar elements appear on the page.
Extracting attributes from multiple elements at once
When a locator matches several elements, you can loop through them and retrieve the same attribute from each one.
const links = page.locator('a');
const count = await links.count();
for (let i = 0; i < count; i++) {
const href = await links.nth(i).getAttribute('href');
console.log(href);
}This example gets the href value from every link on the page.
You can also collect the values in an array:
const links = page.locator('a');
const hrefs = [];
for (let i = 0; i < await links.count(); i++) {
hrefs.push(await links.nth(i).getAttribute('href'));
}
console.log(hrefs);If an element does not have the requested attribute, Playwright adds null for that element. You may need to filter those values before using the result.
const validHrefs = hrefs.filter(href => href !== null);
This approach works well when you need to validate a group of links, image sources, test IDs, or ARIA attributes.
Using attributes in assertions (toHaveAttribute) and validation
One of the most common use cases for getting an element’s attribute is to assert that an element has the correct attribute value. Playwright provides built-in assertions like toHaveAttribute to make this process easier.
await page.locator(‘button’).first().toHaveAttribute(‘aria-label’, ‘Submit’);
This assertion checks if the first button on the page has the expected aria-label value of “Submit.” It’s a powerful way to ensure that the web elements behave as expected during your automated tests.
Common Mistakes When Retrieving Attributes and How to Avoid them
Attribute checks often fail because the locator is too broad, the attribute is missing, or the test is reading the wrong type of value. The following cases are worth checking before you treat an attribute result as reliable.
1. Assuming the attribute is always present
getAttribute() returns null when the selected element does not contain the requested attribute. This can happen when the attribute is optional, added after a user action, or only present in certain UI states.
Check the result before passing it to another function or comparing it as a string.
const link = page.locator('a.download-link');
const href = await link.getAttribute('href');
if (href === null) {
throw new Error('The download link does not have an href attribute');
}
console.log(href);2. Using a locator that matches several elements
A broad locator such as page.locator(‘a’) may match dozens of elements. Before retrieving an attribute, narrow the locator to the element you actually want.
In this example, the locator targets the first link inside the main navigation rather than the first link on the entire page.
const navLink = page.locator('nav[aria-label="Main"] a').first();
const href = await navLink.getAttribute('href');
console.log(href);When you expect a single match, you can also verify the locator count before reading the attribute.
const submitButton = page.getByRole('button', { name: 'Submit' });
await expect(submitButton).toHaveCount(1);
const buttonType = await submitButton.getAttribute('type');3. Using getAttribute() when you only need an assertion
When the purpose of the test is to verify an attribute value, use toHaveAttribute() instead of retrieving the value and comparing it manually.
Playwright will wait for the attribute to reach the expected value, which is useful when the UI updates asynchronously.
const submitButton = page.getByRole('button', { name: 'Submit' });
await expect(submitButton).toHaveAttribute(
'aria-label',
'Submit form'
);Use getAttribute() when you need to store, transform, log, or reuse the value elsewhere in the test.
4. Confusing an HTML attribute with the current DOM value
An element’s attribute does not always reflect its current state. For example, the value attribute may contain the input’s initial value, while the user may have already entered different text.
Use inputValue() when you need the value currently shown in an input.
const emailInput = page.getByLabel('Email address');
await emailInput.fill('user@example.com');
const currentValue = await emailInput.inputValue();
expect(currentValue).toBe('user@example.com');The same distinction applies to other element states. Depending on what you need, methods such as isChecked(), isDisabled(), or textContent() may be more suitable than getAttribute().
5. Treating CSS classes as stable test data
Class names often change during redesigns, refactoring, or CSS build updates. Reading a class attribute can be useful when the class itself is the subject of the test, but it should not usually be your main way of locating an element.
Prefer roles, labels, visible names, or stable test IDs.
const saveButton = page.getByRole('button', { name: 'Save changes' });
await expect(saveButton).toHaveAttribute('data-state', 'ready');This keeps the test focused on behaviour and stable UI contracts rather than implementation details.
Maintenance and Refactoring of Attribute-Based Locators and Checks
As web applications evolve, so do their elements, attributes, and structures. Over time, the attributes you rely on for locating elements may change, or new attributes may be added. It’s crucial to maintain and refactor your attribute-based locators to ensure your tests remain stable and accurate.
To streamline this process and avoid frequent test failures, consider the following approaches:
- Use the Page Object Model (POM): Centralizing locators in a Page Object Model (POM) helps isolate changes to a single location. This design pattern encourages modular test code and reduces the risk of having to update locators in multiple places across your test suite. When an element’s attribute changes, you only need to update it in the Page Object, rather than across every test that interacts with that element.
- Adopt Dynamic Locators: Web applications are often dynamic, and hardcoded locators may become outdated as attributes or structures change. Where possible, opt for more flexible locators that can adapt to minor changes in the UI. For instance, use attribute selectors like data-* attributes or ARIA attributes, which are less likely to change during refactoring or redesigns.
- Version Control for UI Changes: Collaborate with the development team to stay informed about UI changes, especially those that affect attributes. If you’re aware of upcoming changes, you can proactively adjust your locators before they break your tests.
- Regularly Review and Update Tests: Just as the UI evolves, so should your test suite. Regularly review and refactor tests to ensure they reflect the current state of the application. Periodic audits of locators help you identify obsolete or fragile selectors before they become a problem.
Conclusion
Use Locator.getAttribute() when you need to read and reuse an attribute value, and toHaveAttribute() when the test only needs to verify it. Keep your locators specific, account for null when an attribute may be missing, and use methods such as inputValue() when you need the element’s current state rather than its original HTML attribute.