Principle:SeleniumHQ Selenium Element Caching Strategy
| Knowledge Sources | |
|---|---|
| Domains | Test_Design, Performance, Browser_Automation |
| Last Updated | 2026-02-11 00:00 GMT |
Overview
Optimization mechanism for caching resolved WebElement references to avoid redundant DOM queries in Page Object fields.
Description
By default, PageFactory-created proxies re-query the DOM every time a method is called on a WebElement field. This ensures the element reference is always fresh but adds network and processing overhead. The @CacheLookup annotation opts a field into caching: after the first successful findElement() or findElements() call, the resolved WebElement reference (or List<WebElement>) is stored in the DefaultElementLocator instance and reused for all subsequent method calls. This trades freshness for performance -- cached elements may become stale if the DOM changes after the initial lookup.
The caching decision is made at initialization time when DefaultElementLocator is constructed: it reads annotations.isLookupCached() which checks for the presence of @CacheLookup on the field. The shouldCache flag is stored as a final boolean and consulted on every findElement() and findElements() call.
Usage
Apply @CacheLookup to elements that are static on the page (navigation menus, headers, footers, static labels, persistent form fields). Do NOT apply it to elements that are dynamically added or removed, appear after AJAX calls, exist within iframes that are reloaded, or belong to pages with heavy DOM manipulation. If a cached element becomes detached from the DOM, a StaleElementReferenceException will be thrown on the next interaction.
Theoretical Basis
# DefaultElementLocator caching behavior
Without @CacheLookup (shouldCache = false):
proxy.click() -> locator.findElement() -> searchContext.findElement(by) -> element.click()
proxy.getText() -> locator.findElement() -> searchContext.findElement(by) -> element.getText()
// DOM query on every interaction
With @CacheLookup (shouldCache = true):
proxy.click() -> locator.findElement() -> searchContext.findElement(by)
-> cachedElement = element -> element.click()
proxy.getText() -> locator.findElement() -> return cachedElement -> element.getText()
// DOM query only on first interaction; cached reference reused thereafter
The actual caching logic in DefaultElementLocator.findElement():
1. If cachedElement != null AND shouldCache -> return cachedElement
2. element = searchContext.findElement(by)
3. If shouldCache -> cachedElement = element
4. return element
Trade-offs:
| Aspect | Without Cache | With @CacheLookup |
|---|---|---|
| DOM Queries | Every method call | First method call only |
| Performance | Slower (network round-trip each time) | Faster (single round-trip) |
| Freshness | Always current | May become stale |
| StaleElementReferenceException risk | Low | Higher if DOM changes |
| Suitable for | Dynamic content, AJAX-loaded elements | Static page structure |