Attribute Selectors
Master the @ syntax for selecting elements by their attributes in XPath.
Attribute Selectors
Attributes are the backbone of robust XPath expressions. The @ symbol accesses element attributes, enabling precise and maintainable selectors.
Basic Attribute Syntax
The @ symbol references an attribute within a predicate:
//element[@attribute='value']
Common Examples
//input[@type='email'] // Input with type="email"
//button[@id='submit-btn'] // Button with specific ID
//div[@class='container'] // Div with exact class
//a[@href='/login'] // Link with specific href
//*[@data-testid='header'] // Any element with data-testid
Attribute Exists Check
Check if an attribute exists (regardless of value):
//input[@disabled] // Inputs that are disabled
//button[@aria-label] // Buttons with any aria-label
//img[@alt] // Images with alt text
//*[@data-testid] // Any element with data-testid
Attribute Value Matching
Exact Match
//input[@name='email'] // Exact match
Partial Match with contains()
//div[contains(@class, 'card')] // Class contains "card"
//a[contains(@href, 'login')] // Href contains "login"
//button[contains(@id, 'submit')] // ID contains "submit"
Starts With
//input[starts-with(@id, 'user-')] // ID starts with "user-"
//div[starts-with(@class, 'btn-')] // Class starts with "btn-"
Multiple Attributes
// AND - both must match
//input[@type='text' and @name='username']
// OR - either can match
//button[@type='submit' or @type='button']
// Combined conditions
//input[@type='text' and contains(@class, 'form-control')]
Negation
Select elements that don’t have a certain attribute or value:
//input[not(@disabled)] // Enabled inputs
//button[not(@type='submit')] // Non-submit buttons
//div[not(contains(@class, 'hidden'))] // Visible divs
Working with Classes
HTML classes are tricky because multiple classes are space-separated in a single attribute:
<button class="btn btn-primary btn-large">Click me</button>
Common Mistake
// ❌ Won't work - exact match fails
//button[@class='btn-primary']
// ❌ Risky - might match "btn-primary-outline"
//button[contains(@class, 'btn-primary')]
Better Approaches
// ✅ Match with word boundaries
//button[contains(concat(' ', @class, ' '), ' btn-primary ')]
// ✅ Or simply use contains if unique enough
//button[contains(@class, 'btn-primary')]
Best Attributes for Selectors
Ranked by stability and recommendation:
Tier 1: Test Attributes (Best)
//*[@data-testid='login-button']
//*[@data-test='email-input']
//*[@data-cy='submit-form']
These exist specifically for testing and won’t change due to styling or refactoring.
Tier 2: Semantic Attributes
//*[@id='main-navigation'] // Stable IDs
//*[@name='email'] // Form element names
//*[@aria-label='Close dialog'] // Accessibility labels
//*[@role='button'] // ARIA roles
Tier 3: Functional Attributes
//input[@type='email']
//a[@href='/dashboard']
//button[@type='submit']
Tier 4: Styling Attributes (Use Carefully)
//div[contains(@class, 'product-card')] // Semantic classes OK
//span[contains(@class, 'error-message')] // Purpose-indicating
//div[@class=‘css-1a2b3c4’] // ❌ Will change between buildsPractical Examples
Form Elements
// Email input by multiple attributes
//input[@type='email' and @required]
// Password field with placeholder
//input[@type='password' and @placeholder='Enter password']
// Submit button
//button[@type='submit'] | //input[@type='submit']
Navigation Links
// Specific nav link
//nav//a[@href='/products']
// Active menu item
//nav//a[contains(@class, 'active')]
// External links
//a[starts-with(@href, 'https://')]
Data Attributes
// Product by ID
//div[@data-product-id='12345']
// Items in a specific state
//*[@data-status='pending']
// Elements with specific behavior
//button[@data-action='delete']
Common Patterns
Select All Inputs in a Form
//form[@id='signup']//input[@type!='hidden']
Find Required Fields
//input[@required] | //select[@required] | //textarea[@required]
Locate by Partial Dynamic ID
// If IDs follow pattern: "user-123-email"
//input[starts-with(@id, 'user-') and contains(@id, '-email')]
Try It Yourself
Practice attribute selectors on our sample forms:
Open in Playground →
Summary
| Pattern | Use Case | Example |
|---|---|---|
[@attr='val'] | Exact match | [@id='login'] |
[@attr] | Attribute exists | [@disabled] |
[contains(@attr, 'val')] | Partial match | [contains(@class, 'btn')] |
[starts-with(@attr, 'val')] | Prefix match | [starts-with(@id, 'nav-')] |
[not(@attr)] | Attribute absent | [not(@disabled)] |
[@a and @b] | Multiple conditions | [@type='text' and @required] |
Pro tip: When you have a choice, prefer data-testid or aria-* attributes. They’re the most stable foundation for your selectors.