Dynamic elements are web elements whose attributes or structure change during test execution. In Selenium automation, these changes can occur due to page reloads, asynchronous updates, or user interactions that modify the DOM. When locators tied to these elements no longer match, test scripts fail even though the application works as intended.
This makes dynamic elements a primary source of flaky tests. If they are not handled properly, automation suites produce false negatives, slow down debugging, and reduce trust in the results. To address this, testers rely on strategies such as flexible locators, waits for synchronization, and handling of dynamic identifiers.
This article explains the challenges dynamic elements create and the techniques that make Selenium tests stable in their presence.
Dynamic elements are web elements whose attributes, identifiers, or position in the DOM change during or between executions. Unlike static elements, which keep consistent properties across page loads, dynamic elements can look different each time a test runs.
These changes usually occur because modern applications use JavaScript frameworks, AJAX calls, and client-side rendering. Instead of reloading the entire page, they update parts of the DOM in response to user actions or background requests. As a result, the same button, link, or input field may appear with a new ID, a different class name, or a shifted position.
Key characteristics of dynamic elements include:
Handling dynamic elements effectively is not just about preventing test failures. It directly impacts the reliability and long-term value of an automation suite. When testers account for dynamic behavior, the scripts remain stable across multiple runs, environments, and application updates.
The main benefits include:
Dynamic elements change because modern web applications modify the DOM while the user is interacting with the page. These changes do not follow a fixed pattern, which makes them difficult for Selenium to locate using standard strategies. The runtime behavior depends on how the application is built and how it handles rendering and user actions.
The most common runtime changes include:
For testers, the key challenge is that these changes often occur unpredictably. Without strategies like flexible locators and synchronization, even small runtime modifications can make an entire test case fail.
Dynamic elements appear in many parts of modern web applications. They are not limited to complex interfaces; even simple forms and navigation menus can introduce them. Recognizing these common cases helps testers prepare locator strategies that work reliably.
Some frequent examples include:
Dynamic elements cannot be made static from the application side, so the responsibility for handling them lies entirely with the test engineer.
Below are the core approaches, explained in depth:
XPath is one of the most effective tools for locating dynamic elements because it allows partial matches and traversal across the DOM. Instead of hardcoding a value that may change, XPath functions can target the consistent part of an attribute.
For example:
//input[starts-with(@id,'username_')]
This locator matches an input field whose ID always starts with “username_” but ends with a dynamic suffix. Similarly, the contains() function works when a substring remains consistent:
//button[contains(@class,'login-btn')]
Beyond functions, XPath can also use element relationships. If a dynamic element is always inside the same parent container, you can write a relative path anchored to the stable parent. This keeps locators reliable even when attributes shift.
CSS selectors are generally faster than XPath and provide another robust way to handle dynamic elements. They work well when classes or structural relationships are stable while IDs keep changing.
For example:
button[class*='login-btn']
This matches any button whose class contains “login-btn,” regardless of other dynamic suffixes. CSS selectors can also chain multiple attributes or descend through the DOM hierarchy:
div.container form#login button.submit
This approach is especially effective in React or Angular applications where classes are predictable, but IDs are auto-generated. CSS selectors keep scripts clean, readable, and adaptable to changes.
Many dynamic elements do not appear immediately in the DOM. They load only after an AJAX request, an animation, or delayed rendering. If Selenium attempts to locate them too soon, the test fails with “element not found” or “element not interactable” errors.
Explicit waits solve this issue. Instead of executing immediately, they pause until a certain condition is met. For example:
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
WebElement element = wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("dynamicElement")));
Here, Selenium waits up to ten seconds for the element to become visible. Conditions like elementToBeClickable, presenceOfElementLocated, or invisibilityOf allow precise synchronization. This approach eliminates timing issues and reduces flakiness in tests.
Some applications assign new IDs on each page load, but the IDs often follow a pattern. A login input might have IDs like user_123, user_456, and so on. Instead of hardcoding the full ID, you can focus on the consistent prefix.
Using XPath:
//input[starts-with(@id,'user_')]
Or by avoiding IDs altogether and using attributes that remain static:
//input[@name='username']
The key is to analyze the DOM carefully, spot repeating patterns, and base your locators on the part that does not change. This significantly reduces maintenance effort.
There are scenarios where standard Selenium locators cannot interact with a dynamic element. Examples include elements hidden behind overlays, nodes inside shadow DOMs, or elements created entirely by JavaScript after a user action.
In such cases, the JavaScript Executor acts as a fallback. It allows you to run JavaScript directly in the browser context to locate or manipulate elements.
For example:
JavascriptExecutor js = (JavascriptExecutor) driver;
WebElement element = (WebElement) js.executeScript("return document.querySelector('#dynamicButton')");
js.executeScript("arguments[0].click();", element);
While this approach should be used sparingly, it provides the control needed when no other locator works. It is particularly useful for troubleshooting edge cases without blocking automation progress.
Dynamic elements often trigger errors that can break otherwise correct test scripts. Below are the most frequent issues and how to resolve them.
Dynamic elements are unavoidable in modern applications, but their impact on automation can be minimized with disciplined practices. Below are key guidelines that experienced testers follow.
Dynamic elements are a major source of instability in Selenium automation. Their unpredictable attributes and runtime changes often lead to locator failures, stale references, and flakiness in test suites.
Adding real device testing with platforms like BrowserStack ensures that locators perform consistently in production-like conditions. When handled correctly, dynamic elements stop being blockers and become just another factor accounted for in robust, reliable Selenium test automation.
Run Selenium Tests on Cloud
Get visual proof, steps to reproduce and technical logs with one click
Continue reading
Try Bird on your next bug - you’ll love it
“Game changer”
Julie, Head of QA
Try Bird later, from your desktop