Decision Use data-drupal-selector to target form elements in JavaScript

accepted

Classes and IDs are unstable JavaScript targets in Drupal, while data-drupal-selector is a stable hook provided by the Form API.

Decision

When JavaScript needs to target an element rendered through Drupal's Form API, select it by its data-drupal-selector attribute instead of a class or an ID.

Drupal adds data-drupal-selector to every form and to every form element that has an ID. The attribute exists to provide a selector usable by JavaScript, because the ID cannot be relied on. In practice this covers entity forms, configuration forms, views exposed filters, the search block, and the forms inside Media Library and Layout Builder dialogs.

Classes and IDs fail as JavaScript hooks for different reasons:

  • IDs must be unique, so Drupal appends suffixes such as edit-title--2 when a form or element renders more than once on a page.
  • On AJAX requests, Drupal appends random suffixes to ids such as edit-title--aB3-cD_eFgh to ensure uniqueness.
  • Classes are owned by the theme layer, and switching the theme, or functionality meant to work across a user facing theme and an admin facing theme, can break your class-based selectors.
// Avoid: breaks when the ID gets a suffix on rebuild.
document.querySelector('#edit-title');

// Avoid: breaks if the active theme overrides the relevant template / pre-processor and changes the class
document.querySelector('.form-item-title');

// Prefer: stable across rebuilds, themes, and duplicate renders.
document.querySelector('[data-drupal-selector="edit-title"]');

Exceptions

  • Accessibility wiring such as label[for] and aria-describedby must keep referencing IDs. That is referencing an ID, not selecting by it in JavaScript.
  • When third-party libraries or contrib modules only expose class-based hooks, classes can be used as selectors for these elements.
  • Older Drupal convention used js-* prefixed classes. When data-drupal-selector attributes are not available, but js-* classes are available, prefer js-* classes before presentational classes.

Consequences

  • JavaScript keeps working when a form renders more than once on the same page. This removes a common source of regressions in views exposed filters and dialog-based UIs.
  • Selectors document their own intent. A [data-drupal-selector] selector marks a functional dependency, while a class only describes presentation, making it clear which parts of the markup JavaScript relies on.

Additional Resources