XPath AND: Combining Multiple Conditions in One Expression

Combine multiple XPath conditions with and, stacked predicates, not() and grouped or expressions, with attribute, text and Selenium examples.

To require that an element matches multiple conditions, join the conditions with and inside a predicate—the expression between square brackets:

//input[@type='email' and @required]

Every condition joined by and must be true for the same element to match. This expression only selects input elements that have both type='email' and a required attribute.

If either condition being true is enough, use or instead—see the companion guide on the XPath OR condition.

XPath and inside a predicate

Chain as many conditions as needed:

//input[@type='email' and @required and not(@disabled)]

Each test applies to the same input element: the type must be email, the required attribute must exist, and the disabled attribute must not.

Use lowercase and. XPath has no && operator, and AND in uppercase raises a syntax error in most engines.

Multiple predicates: [a][b]

Stacking predicates back to back also requires every condition to match:

//input[@type='email'][@required]

For simple attribute tests this behaves the same as and. The difference appears with positional predicates, because each predicate is evaluated against the result of the previous one:

//tr[@class='active'][1]
//tr[@class='active' and position() = 1]

The first expression selects the first row among the active rows. The second only matches when the first row of the table also happens to be active. Prefer and when the logic should apply to one set of elements at once, and stacked predicates when you want to filter, then pick a position.

Combine and with not()

The not() function excludes a condition from an and chain:

//button[@type='submit' and not(@disabled)]

Exclude several values by chaining not() expressions:

//a[
  contains(@class, 'nav-link')
  and not(contains(normalize-space(.), 'Delete'))
  and not(contains(normalize-space(.), 'Edit'))
]

normalize-space(.) trims leading and trailing whitespace and collapses repeated whitespace before checking the element’s complete text content.

Combine and with or: precedence and parentheses

and binds more tightly than or. Without parentheses this expression does not do what it appears to:

//input[@type='text' or @type='email' and not(@disabled)]

XPath reads it as @type='text' or (@type='email' and not(@disabled))—disabled text inputs still match. Group the alternatives explicitly:

//input[(@type='text' or @type='email') and not(@disabled)]

Now the expression finds text or email inputs, but only when they are not disabled. Whenever an expression mixes and with or, put parentheses around the or alternatives and apply the shared restrictions outside them.

Combine attribute and text conditions

Conditions joined by and are not limited to attributes. Mix attribute tests with text checks:

//button[@type='submit' and contains(normalize-space(.), 'Save')]

Or require an attribute on the element and specific text in a descendant:

//tr[@data-status='active' and .//td[contains(normalize-space(.), 'Invoice')]]

The second condition is itself a relative XPath: the row only matches when it contains a td whose text includes Invoice.

Selenium example

from selenium.webdriver.common.by import By

email_input = driver.find_element(
    By.XPATH,
    "//input[(@type='text' or @type='email') and not(@disabled)]",
)

When an XPath string contains quotes, choose the opposite quote style for the Python string or escape the inner quotes.

Common XPath and errors

  • Use lowercase and—there is no && in XPath.
  • Put the whole logical expression inside one [...] predicate, not between predicates.
  • Balance every bracket and parenthesis.
  • Use =, not ==, for comparisons.
  • Group or alternatives in parentheses before adding an and restriction.
  • Remember [a][b] filters sequentially; positional tests behave differently than with and.

Quick reference

RequirementPattern
Both conditions match//*[@a and @b]
Three or more conditions//*[@a and @b and @c]
Match one, exclude another//*[@a and not(@b)]
Either of two, plus a shared restriction//*[(@a or @b) and not(@c)]
Attribute plus text//*[@a and contains(normalize-space(.), 'value')]
First among matches//*[@a][1]

Start with a single condition, confirm it matches, then add one and clause at a time. For expressions where any one of several conditions is enough, switch to the XPath OR condition.