Basics beginner attributes selectors

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
Avoid auto-generated classes:
//div[@class=‘css-1a2b3c4’]  // ❌ Will change between builds

Practical 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']
// 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

PatternUse CaseExample
[@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.