QA How-To
Selenium By xpath dynamic in Python (2026)
Master selenium By xpath dynamic python patterns for changing text, attributes, lists, SVG, and waits with tested Selenium 4 and pytest examples for CI.
22 min read | 2,981 words
TL;DR
For selenium By xpath dynamic python, build a locator tuple with `By.XPATH`, anchor it to a stable attribute, label, row, or card, and wait through `WebDriverWait`. Escape variable text before inserting it, keep searches component-scoped, and use CSS or a test ID when XPath adds no relationship value.
Key Takeaways
- Represent locators as `(By.XPATH, expression)` tuples so waits and page objects can reuse them.
- Model the stable business relationship first, then insert only controlled runtime data.
- Use XPath string literal escaping when values can contain either quote type.
- Scope collection searches to a container and filter rows or cards by a unique domain value.
- Use custom wait predicates for application states that built-in expected conditions cannot express.
- Handle iframes, shadow roots, SVG, and virtual lists as distinct DOM context problems.
- Keep pytest fixtures responsible for driver cleanup, even when setup or assertions fail.
A reliable selenium By xpath dynamic python solution uses a stable page fact and treats changing values as narrow inputs, not as permission to match anything. In Selenium 4 Python, store the locator as (By.XPATH, expression), give it to WebDriverWait, and act on the element returned when the required state exists.
Python makes string construction compact, but an elegant f-string can still produce an invalid or dangerously broad XPath. This guide focuses on Python-specific implementation: pytest fixtures, locator factories, safe XPath literals, custom wait functions, lists, SVG, frames, shadow DOM, and diagnostic code you can use in a real framework.
TL;DR
| Need | Python pattern | Why it works |
|---|---|---|
| Stable prefix, dynamic suffix | (By.XPATH, "//button[starts-with(@id,'submit-')]") |
Ignores only the known-changing suffix |
| Runtime row value | Create an XPath literal, then build the tuple | Prevents quote-related syntax failures |
| Changing DOM node | WebDriverWait(driver, 10).until(EC.visibility_of_element_located(locator)) |
Re-locates during polling |
| Repeated card | Anchor to the card heading, then find its control | Maintains domain scope |
| Complex ready state | Custom callable returning an element or False |
Makes application readiness explicit |
| Open shadow root | Use element.shadow_root, then search inside |
XPath cannot cross the boundary |
The expression should select the right element because of stable meaning. The wait should address timing. Do not try to make one compensate for the other.
1. Selenium By xpath dynamic Python: Locator Tuple Basics
Selenium Python locators are two-item tuples. By.XPATH identifies the strategy and the second item is the XPath expression. The tuple works with find_element, find_elements, Selenium expected conditions, and your own page component methods.
from selenium.webdriver.common.by import By
save_button = (By.XPATH, "//button[starts-with(@id, 'save-')]")
# Later: driver.find_element(*save_button)
The star expands the tuple into the two arguments expected by find_element. Expected conditions accept the tuple directly. This small convention keeps expression construction outside test actions and makes locator names carry intent.
A dynamic XPath is appropriate when a stable relationship identifies the target despite a changing attribute, collection position, or displayed value. For example, an order card can be selected by its number, then its cancel button can be found inside that card. Dynamic does not mean that every attribute is fuzzy. Usually most of the expression should be exact.
Python code often builds selectors inline because f-strings are convenient. Resist putting a long XPath inside the click call. Name the locator, validate the runtime value, and decide whether it is safe to display in an error message. This separation makes failures reviewable and permits focused tests for locator factories.
If a unique data-testid exists, a CSS locator such as (By.CSS_SELECTOR, "[data-testid='save-profile']") is usually simpler. Use XPath because its text or relationship features solve the actual problem, not because it appears more powerful.
2. Run a Complete Selenium 4 and pytest Example
Current Selenium 4 Python can manage local driver discovery through Selenium Manager when you construct webdriver.Chrome() and have a supported Chrome installation. You do not need a manually downloaded executable for the ordinary local setup. Pin maintained Selenium and pytest versions in your project dependency lock rather than relying on an unbounded install.
This test is self-contained. It opens HTML through a data URL, waits for a button with a generated suffix, clicks it, and verifies the result.
from urllib.parse import quote
import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
@pytest.fixture
def driver():
options = webdriver.ChromeOptions()
options.add_argument("--headless=new")
browser = webdriver.Chrome(options=options)
yield browser
browser.quit()
def test_clicks_button_with_generated_suffix(driver):
html = """
<main aria-label="Profile">
<button id="save-71942" onclick="this.textContent='Saved'">
Save profile
</button>
</main>
"""
driver.get("data:text/html;charset=utf-8," + quote(html))
save = (
By.XPATH,
"//main[@aria-label='Profile']"
"//button[starts-with(@id,'save-')]",
)
button = WebDriverWait(driver, 5).until(EC.element_to_be_clickable(save))
button.click()
WebDriverWait(driver, 5).until(EC.text_to_be_present_in_element(save, "Saved"))
assert driver.find_element(*save).text == "Saved"
The fixture uses yield so teardown runs after the test body. In a production suite, put the fixture in conftest.py, add artifacts before quitting on failure, and configure local or remote driver creation centrally. Keep driver scope per test unless you have a carefully isolated reason to share sessions.
3. Turn Page Semantics Into a Locator Plan
Before writing syntax, describe the target in plain language. For example: inside the Orders region, find the card whose heading equals the order number, then select its Cancel button. The sentence exposes the stable region, dynamic domain value, and intended action. Translate it into XPath only after confirming the DOM supports the relationship.
def cancel_button_for(order_number: str):
order = xpath_literal(order_number)
return (
By.XPATH,
"//section[@aria-label='Orders']"
f"//article[.//h3[normalize-space(.)={order}]]"
"//button[@name='cancel']",
)
The factory returns a locator, not an element. That distinction delays DOM lookup until the caller's wait or action. It also prevents a page object from holding a stale reference after a component redraw.
Rank candidate anchors by stability: a documented test attribute or domain identifier is strong, an exact accessible label may be strong, and a framework-generated class or page-wide position is weak. Confirm assumptions through multiple renders and representative records. A numeric value is not automatically unstable. An order number may be the best identifier on the page.
Scope improves both correctness and performance. //button[normalize-space()='Cancel'] may match navigation, a dialog, and several cards. The component-aware form communicates which Cancel control belongs to which order. If the resulting XPath becomes hard to explain in one sentence, request a better test hook.
For a wider locator review process, use the Selenium locator best practices checklist.
4. Use Exact, Contains, and Prefix Attribute Predicates
Choose the most restrictive predicate supported by the application's contract. Exact equality is the default. starts-with() is useful for a stable prefix plus generated suffix. contains() is useful for a stable fragment whose position varies, but it needs enough scope to avoid accidental matches.
username = (By.XPATH, "//input[@name='username']")
upload = (By.XPATH, "//input[starts-with(@id,'document-upload-')]")
remove_invoice = (
By.XPATH,
"//button[contains(@aria-label,'Remove invoice')]",
)
Class attributes contain space-separated tokens. A substring predicate can confuse alert with alerting. Use the XPath 1.0 token idiom when class is the only appropriate hook:
error_panel = (
By.XPATH,
"//div[contains(concat(' ',normalize-space(@class),' '),' alert ')"
" and contains(concat(' ',normalize-space(@class),' '),' error ')]",
)
CSS supports class token matching more readably as .alert.error, so compare strategies before keeping that XPath. XPath adds value when you also need text, an ancestor, or a sibling relationship.
Browser XPath is effectively XPath 1.0. There is no standard ends-with() function in this environment. A substring() suffix expression is possible, but it is difficult to read. If a stable suffix is all you have, ask whether another attribute or a CSS suffix selector such as [id$='-submit'] communicates intent more clearly.
Do not combine several uncertain partial predicates just to reach uniqueness. That encodes multiple unstable details. One strong component anchor plus one exact action attribute is usually easier to maintain.
5. Insert Python Variables Without Breaking XPath
An f-string does not escape XPath. If customer_name is O'Neil, this expression is invalid: f"//td[.='{customer_name}']". If the value contains both quote types, merely switching delimiters also fails. Generate a proper XPath string literal.
def xpath_literal(value: str) -> str:
if "'" not in value:
return f"'{value}'"
if '"' not in value:
return f'"{value}"'
parts = value.split("'")
arguments = []
for index, part in enumerate(parts):
if index:
arguments.append('"\'"')
arguments.append(f"'{part}'")
return f"concat({', '.join(arguments)})"
def customer_row(name: str):
return (
By.XPATH,
f"//table[@aria-label='Customers']"
f"//tr[td[normalize-space(.)={xpath_literal(name)}]]",
)
Test the helper as ordinary Python code:
import pytest
@pytest.mark.parametrize(
"value",
["Avery", "O'Neil", 'The "Ace" Team', 'O\'Neil "Ace"'],
)
def test_xpath_literal_can_be_evaluated(driver, value):
html = f"<p id='target'></p>"
driver.get("data:text/html;charset=utf-8," + quote(html))
expression = f"string({xpath_literal(value)})"
actual = driver.execute_script(
"return document.evaluate(arguments[0], document, null, "
"XPathResult.STRING_TYPE, null).stringValue;",
expression,
)
assert actual == value
Safe quoting prevents syntax errors, but runtime data can still be unsuitable. Use known case identifiers where possible, validate unexpected length or control characters, and avoid including secrets or personal data in exception output. Selector construction should not become an unrestricted query service.
6. Normalize Text and Account for Nested Markup
Direct text() matching sees direct text nodes, not the combined text of arbitrary descendants. A button like <button><span>Continue</span></button> may not match text()='Continue'. normalize-space(.) evaluates the element's full string value, trims edges, and collapses whitespace.
continue_button = (
By.XPATH,
"//button[normalize-space(.)='Continue']",
)
notice_for_order = lambda order: (
By.XPATH,
"//div[@role='status']"
f"[contains(normalize-space(.),{xpath_literal(order)})]",
)
Use exact normalized text when the wording is a stable contract. Use contains() when a stable runtime value appears inside a larger message. Always scope generic words such as Continue, Edit, or Delete to a dialog, form, or card.
Text is not a universal locator. Localization changes it, copy edits can be legitimate, and repeated controls share labels. An aria-label can be useful when the application supplies one, but XPath queries raw DOM attributes. It does not automatically understand every computed accessible name or implicit role. Validate accessibility separately with appropriate tooling.
Case-insensitive text matching is awkward in XPath 1.0. translate() can map a fixed ASCII alphabet, but it does not provide complete Unicode case folding. Avoid a huge case-normalization expression when a normalized data attribute or exact domain ID can solve the problem. Tests should not silently accept unintended case changes unless the requirement says matching is case-insensitive.
7. Locate Dynamic Lists, Cards, and Tables
Repeated content should be selected in two phases: identify one logical item, then find a control inside it. XPath predicates make that relationship readable. Do not calculate the item's current visual position when a stable item value exists.
def product_card(name: str):
return (
By.XPATH,
"//section[@aria-label='Products']"
f"//article[.//h2[normalize-space(.)={xpath_literal(name)}]]",
)
def add_product_button(name: str):
product = xpath_literal(name)
return (
By.XPATH,
"//section[@aria-label='Products']"
f"//article[.//h2[normalize-space(.)={product}]]"
"//button[@name='add-to-cart']",
)
For a table, use a unique cell to identify the row. If two rows can intentionally share that text, add another stable cell or a data key. Verify the count during development:
row_locator = customer_row("O'Neil")
rows = WebDriverWait(driver, 10).until(
EC.presence_of_all_elements_located(row_locator)
)
assert len(rows) == 1, f"Expected one customer row, found {len(rows)}"
The count assertion gives domain meaning to an ambiguity. Do not routinely call find_elements(...)[0], which silently chooses the first match.
Virtualized lists render only a window of items. XPath searches the DOM, not the entire backing dataset. Use the product's search or filter behavior, scroll through the supported container if that is the requirement, and wait for the requested item to render. Infinite scrolling also needs a termination rule, such as a maximum number of pages or an explicit end marker. Without one, a missing item can hang the test.
8. Navigate Relationships With XPath Axes
Axes are most useful when the stable identifier and target live in the same component but not in a simple parent-child chain. following-sibling connects adjacent controls, ancestor moves to a shared component, and a nested predicate can select a container based on its descendants.
email_input = (
By.XPATH,
"//label[normalize-space(.)='Work email']"
"/following-sibling::input[@type='email']",
)
plan_select = (
By.XPATH,
"//div[.//span[normalize-space(.)='Subscription plan']]"
"//select[@name='plan']",
)
Prefer the closest stable relationship. following::input[1] searches all later descendants in document order and can cross into another form. following-sibling::input stays at one parent level. ancestor::form[1] can provide a safe boundary when the form is the intended component.
Python page components can also narrow the search context without one large XPath. Find a stable card or row, then call row.find_element(By.CSS_SELECTOR, "button[name='open']"). This often produces simpler expressions and lets the component object own the root. Re-find the root if the application replaces the entire item after updates.
Axes are not a substitute for semantic HTML. When a label uses a stable for attribute pointing to a stable input ID, select that input directly. Relationship-based XPath is valuable when the relationship itself is the only durable contract or when the test intentionally verifies the labeled layout.
9. Wait for Application State With Built-in and Custom Conditions
A locator identifies a potential element. A wait describes when it is ready. Selenium's expected conditions cover presence, visibility, clickability, text, invisibility, frames, alerts, selection, URL state, and more. Select the condition closest to the next operation.
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
row = customer_row("O'Neil")
try:
customer = WebDriverWait(driver, 10).until(
EC.visibility_of_element_located(row)
)
except TimeoutException as exc:
raise AssertionError("Customer row did not become visible") from exc
A custom callable can express a domain state and return the useful element when ready:
def element_has_attribute(locator, name, expected):
def condition(driver):
element = driver.find_element(*locator)
return element if element.get_attribute(name) == expected else False
return condition
checkout = (By.XPATH, "//button[@name='checkout']")
button = WebDriverWait(driver, 15).until(
element_has_attribute(checkout, "data-state", "ready")
)
button.click()
During polling, Selenium ignores NoSuchElementException by default. If the node is replaced, a custom condition may encounter a stale reference. Re-locating inside the callable helps, but do not broadly suppress every Selenium exception because real invalid selectors and browser loss should fail clearly.
For more synchronization patterns, see the Python Selenium explicit waits guide. Fixed time.sleep() is not a readiness strategy.
10. Handle SVG, Iframes, Shadow DOM, and Virtual Content
Some apparent XPath failures are search-context failures. XPath evaluates only in the current document or element context. For an iframe, wait for it and switch before locating its content:
payment_frame = (By.CSS_SELECTOR, "iframe[title='Payment']")
WebDriverWait(driver, 10).until(
EC.frame_to_be_available_and_switch_to_it(payment_frame)
)
pay = (By.XPATH, "//button[normalize-space(.)='Pay now']")
WebDriverWait(driver, 10).until(EC.element_to_be_clickable(pay)).click()
driver.switch_to.default_content()
Document XPath does not cross a shadow root. Selenium 4 exposes an open root through the host element. Search inside that root as a separate context:
host = driver.find_element(By.CSS_SELECTOR, "account-menu")
shadow = host.shadow_root
logout = shadow.find_element(By.CSS_SELECTOR, "button[name='logout']")
logout.click()
SVG element names can behave differently because of namespaces. //*[local-name()='svg']//*[local-name()='path'] can locate SVG nodes when a plain element name does not. Prefer a stable accessible label or data attribute on the clickable SVG container rather than coupling to a path's shape.
Canvas content is pixels, not ordinary DOM nodes. XPath cannot select a drawn chart point. Use application-supported accessibility overlays, exposed data, or purposeful visual testing depending on the requirement. Virtualized content similarly requires the app to render the item before any DOM locator can find it.
Diagnose the boundary first. A broader //* expression will not cross it and can make the failure slower and less understandable.
11. Organize Locator Factories in Python Page Components
Keep static locators as named tuples and dynamic locators as small functions. Put each locator near the component whose markup contract it describes. Tests should call domain operations rather than pass arbitrary XPath strings into a generic click helper.
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
class OrdersPanel:
ROOT = (By.XPATH, "//section[@aria-label='Orders']")
def __init__(self, driver, timeout=10):
self.driver = driver
self.wait = WebDriverWait(driver, timeout)
@staticmethod
def _open_button(order_number):
value = xpath_literal(order_number)
return (
By.XPATH,
"//section[@aria-label='Orders']"
f"//article[.//*[@data-order-number={value}]]"
"//button[@name='open']",
)
def open_order(self, order_number):
locator = self._open_button(order_number)
self.wait.until(EC.element_to_be_clickable(locator)).click()
A static method makes the locator factory easy to unit-test. The domain method owns readiness and action. If clicking triggers navigation, it can also wait for the destination's defining state before returning.
Avoid a base page with dozens of unrelated wrappers. click(locator) adds little value unless it consistently provides meaningful logging, state handling, and errors. Component-specific methods make ownership and business intent clearer.
Type hints can improve framework APIs. Define a locator alias such as Locator = tuple[str, str] for modern Python, and annotate factories. Do not depend on private Selenium types or internals. Keep browser setup, page behavior, data creation, and assertions as separate responsibilities so a locator failure is not confused with fixture failure. The Python Selenium page object guide covers that structure in more depth.
12. Selenium By xpath dynamic Python Review Checklist
Review a dynamic XPath by testing both selection and lifecycle. First, state why each predicate is stable. Next, confirm the expression is unique in representative DOM states. Then verify the wait corresponds to the action and that the locator is evaluated in the correct frame, window, or shadow context.
A practical review sequence is:
- Prefer a stable ID, name, test attribute, or concise CSS selector.
- Use XPath when text or a relationship provides the durable meaning.
- Restrict partial functions to the known-changing segment.
- Convert every runtime value through a tested XPath literal helper.
- Scope repeated items by domain identity, never current visual position.
- Wait for an observable ready state and retain useful failure artifacts.
Exercise edge-case data. Names with apostrophes, quotation marks, Unicode, repeated whitespace, and duplicates reveal assumptions that ordinary fixtures miss. Run with supported viewport sizes and locales if markup changes across them.
On failure, capture a screenshot, URL, exact locator, relevant DOM fragment, and safe test data ID. Evaluate the XPath against that captured state and count all matches. Compare local and CI frame, locale, feature flag, authentication, and data conditions.
If the expression needs several axes and partial matches to remain unique, stop optimizing the XPath. Propose a stable application attribute or a more semantic component boundary. Maintainability is part of test correctness.
Interview Questions and Answers
Q: How is an XPath locator represented in Selenium Python?
It is normally a tuple such as (By.XPATH, "//button[@name='save']"). driver.find_element(*locator) expands it, while expected conditions accept the tuple directly. I return tuples from dynamic locator factories so lookup happens at the correct time.
Q: How do you place a Python variable in an XPath safely?
I convert the value to a valid XPath string literal before formatting the expression. Single quotes, double quotes, and values containing both require different representations. I prefer a controlled domain ID and avoid logging sensitive runtime values.
Q: Why return a locator instead of a WebElement from a factory?
A locator can be evaluated by a wait and re-evaluated if the DOM replaces the element. Returning an element immediately couples construction to the current page moment and can create stale references. The page operation should control synchronization.
Q: When is XPath better than CSS in Selenium Python?
XPath is valuable for normalized visible text, ancestor navigation, label relationships, and selecting a table row based on a cell. CSS is usually clearer for stable attributes, classes, and descendant structure. I choose the simplest strategy that expresses a stable contract.
Q: How would you wait for a dynamic element to be ready?
I use WebDriverWait with a built-in expected condition when it represents the state. For a domain-specific state, I write a small callable that returns the useful element when ready and False otherwise. I do not use a fixed sleep.
Q: Can a dynamic XPath find items in a virtualized list?
Only items currently rendered in the DOM are searchable. I use supported filter, pagination, or bounded scrolling to cause the target item to render, then wait for it. A missing item needs a clear termination condition.
Q: How do you debug an XPath that works locally but fails in CI?
I inspect the failing screenshot and DOM, count matches, and verify the frame or shadow context. Then I compare browser, viewport, locale, flags, authentication, data, and timing. I change the locator only after confirming a selection defect.
Q: What makes a dynamic XPath maintainable?
It has a stable semantic anchor, a narrow component scope, a documented runtime input, and an explicit ready-state wait. It remains understandable without DevTools. If it requires positional tricks, I request a better application hook.
Common Mistakes
- Putting long f-string XPath expressions directly inside click calls.
- Wrapping every runtime value in single quotes without handling apostrophes.
- Calling
find_elements(...)[0]when multiple matches indicate an ambiguous locator. - Using
contains(@class,...)as if class were a plain substring field. - Caching elements from components that are redrawn after API responses.
- Confusing an unrendered virtual item with a bad XPath.
- Attempting to cross an iframe or shadow root with a page-level expression.
- Using
following::when a closer sibling or ancestor boundary exists. - Adding
time.sleep()instead of defining the application-ready state. - Logging personal or secret runtime values in locator diagnostics.
- Building a universal base page that hides domain behavior.
- Keeping a complex XPath when a stable data attribute can be added.
Conclusion
Good selenium By xpath dynamic python code treats XPath as a precise description of stable page meaning. Build locator tuples, use tested literal conversion for variables, scope repeated content by a domain key, and let explicit waits evaluate the locator when the application reaches an observable state.
Choose one frequently failing Python test and separate its three concerns: selector, search context, and readiness. Verify each against captured evidence, move the final locator into its component, and add edge-case data tests for the locator factory before reusing the pattern.
Interview Questions and Answers
What is a dynamic XPath in Selenium Python?
It is an XPath built around stable attributes, text, relationships, or domain data while allowing a documented fragment to vary. Selenium represents it with a `(By.XPATH, expression)` tuple. I keep it unique, component-scoped, and synchronized with an explicit wait.
How do you safely include runtime text in XPath?
I convert the input into a valid XPath 1.0 string literal. If it contains both quote types, I construct a `concat()` expression. I use controlled identifiers and prevent sensitive values from reaching logs.
Why is a locator tuple useful in Python frameworks?
The tuple works with driver methods and Selenium expected conditions. It can be returned from a locator factory without querying the DOM. That lets the page operation own timing and re-locate after DOM replacement.
How would you locate a button in a dynamic card list?
I identify the card with a unique heading or domain attribute, then locate the named button inside it. I avoid a global index and safely quote any runtime value. For virtualized lists, I first cause the card to render through supported UI behavior.
What is the role of normalize-space(.) in XPath?
It reads the element string value, including descendants, trims edge whitespace, and collapses whitespace runs. It handles nested text more reliably than a direct `text()` comparison. It does not remove localization or copy-change risk.
How do you write a custom Selenium wait in Python?
I create a callable that receives the driver and returns the desired element or value when ready, otherwise `False`. The callable re-finds elements as needed and suppresses only expected transient states. Invalid selectors and browser failures remain visible.
Can XPath pierce shadow DOM?
No. I locate the shadow host, retrieve its open `shadow_root`, and search within that new context. Closed shadow roots require an application-supported testing interface or a different testing approach.
How do you decide between CSS and XPath?
I use CSS for concise stable attribute and hierarchy selection. I choose XPath when text or a relationship such as ancestor, sibling, or table row is the durable signal. A stable test attribute is preferable to either complex selector.
Frequently Asked Questions
How do I create a dynamic XPath in Selenium Python?
Create a tuple such as `(By.XPATH, expression)` where the expression anchors to stable meaning and only the documented fragment varies. Return that tuple from a small function, then pass it to `WebDriverWait` or expand it into `find_element`.
How do I pass a string variable into XPath in Python?
Convert the value to a valid XPath string literal before inserting it into an f-string. A tested helper must handle single quotes, double quotes, and values containing both. Prefer controlled domain IDs over unrestricted text.
What does the star mean in driver.find_element(*locator)?
The star unpacks the two-item locator tuple into separate strategy and expression arguments. For example, `(By.XPATH, "//button")` becomes the two arguments required by `find_element`.
Can Selenium XPath find hidden or virtualized items?
XPath can find a hidden DOM node, although a visibility wait will reject it. It cannot find a virtualized item that has not been inserted into the DOM, so the test must filter, paginate, or scroll until the application renders it.
How do I wait for a dynamic XPath in Python?
Pass the locator tuple to a Selenium expected condition, such as `visibility_of_element_located` or `element_to_be_clickable`, inside `WebDriverWait`. Use a custom callable when readiness depends on an application-specific attribute or state.
Why does an XPath not work inside an iframe?
XPath is evaluated in the current browsing context. Wait for the iframe, switch into it, locate and interact with its content, then switch back to default content when the workflow requires it.
Should Python page objects store WebElement instances?
Usually store locators and find elements at action time, especially for reactive pages that replace nodes. A short-lived component can use a root element, but it must be re-created if its root is replaced.
Related Guides
- How to Use Selenium By xpath dynamic in Java (2026)
- Selenium DevTools in Selenium 4 in Python (2026)
- How to Use Selenium DevTools in Selenium 4 in Java (2026)
- Selenium By cssSelector in Python (2026)
- How to Debug a failing test in VS Code in Selenium (2026)
- How to Run tests in headed mode in Selenium (2026)