Using Dynamic Selectors

This page is not available in the language you requested. You have been redirected to the English version of the page.
Link to this page copied to clipboard

Configure Axe Watcher to properly track accessibility issues on pages with dynamically generated IDs and classes

Not for use with personal data

When testing pages that generate dynamic element IDs or class names on each page load, Axe Watcher may have difficulty tracking whether accessibility issues are duplicates across test runs. This article explains how to configure Watcher to handle these scenarios correctly.

The Problem with Dynamic Selectors

By default, Axe Watcher uses CSS selectors that include element IDs and classes to identify where accessibility issues occur. For example, an issue might be reported at:

iframe#main-iframe

This approach works well when your page's IDs and classes remain consistent between page loads. However, many modern web applications generate dynamic identifiers that change on every page render, such as:

  • #component-a1b2c3d4
  • .form-field-xyz789

When these identifiers change between test runs, Axe Watcher cannot determine whether an issue is a duplicate of a previously detected issue or a new occurrence. This can result in:

  • The same issue being reported as both "new" and "resolved" on each test run
  • Inaccurate tracking of your accessibility progress over time
  • Difficulty identifying which issues have genuinely been fixed

The Solution: Enable Ancestry Tracking

To handle dynamic selectors, set the ancestry option to true in your runOptions configuration. When enabled, Axe Watcher uses the element's position within the DOM tree rather than relying on IDs and classes to locate elements between test runs.

With ancestry enabled, a selector that previously looked like this:

iframe#main-iframe

Will instead include the complete path from the root element:

html > body > div:nth-child(20) > div:nth-child(1) > div > div > ul > li:nth-child(1) > div > span > iframe

This positional selector remains consistent across page loads, even when IDs and classes change, allowing Axe Watcher to accurately track duplicate issues.

Configuration Examples

JavaScript and TypeScript

Add the ancestry option to your axe configuration's runOptions:

const config = {
  axe: {
    apiKey: process.env.ACCESSIBILITY_API_KEY,
    projectId: process.env.PROJECT_ID,
    runOptions: {
      ancestry: true
    }
  }
}

Java

Use the setAncestry() method on your AxeRunOptions object:

AxeRunOptions runOptions = new AxeRunOptions()
    .setAncestry(true);

AxeWatcherOptions options = new AxeWatcherOptions()
    .setApiKey(System.getenv("ACCESSIBILITY_API_KEY"))
    .setProjectId(System.getenv("PROJECT_ID"))
    .setRunOptions(runOptions);

AxeWatcher watcher = new AxeWatcher(options);

When to Use Ancestry Tracking

Enable ancestry: true when your application:

  • Uses frameworks that generate dynamic component IDs (React, Vue, Angular)
  • Employs CSS-in-JS libraries that generate unique class names
  • Has form fields or interactive elements with auto-generated identifiers
  • Shows inconsistent "new issue" and "resolved issue" counts between test runs for what appear to be the same issues

Trade-offs to Consider

While ancestry tracking solves the dynamic selector problem, there are some considerations:

  • Selector readability: Positional selectors are longer and can be harder to read when reviewing issues in Axe Developer Hub.
  • DOM structure sensitivity: If your page's DOM structure changes significantly between renders (not just the IDs/classes), the positional selectors may also change.
  • Debugging: When investigating an issue, you may find it easier to locate an element by a meaningful ID than by its position in the DOM tree.

For most applications with dynamic identifiers, the benefits of accurate issue tracking outweigh these trade-offs.

See Also