Given a DOM node, return a unique CSS selector matching only that element.
Selector uniqueness is determined based on the given element's root node. Elements rendered within Shadow DOM will derive a selector unique within the associated ShadowRoot context. Otherwise, a selector unique within an element's owning document will be derived.
npm install @cypress/unique-selector
Generates a unique CSS selector for the given DOM element.
element
(Element) - The DOM element for which to generate a unique selectoroptions
(Object, optional) - Configuration options for selector generation
-
selectorTypes
(String[], optional) - Array of selector types to use in order of preference. Default:['id', 'name', 'class', 'tag', 'nth-child']
Available selector types:
'id'
- Uses element's ID attribute (e.g.,#myId
)'name'
- Uses element's name attribute (e.g.,[name="myName"]
)'class'
- Uses element's class attributes (e.g.,.myClass
)'tag'
- Uses element's tag name (e.g.,div
,span
)'nth-child'
- Uses nth-child position (e.g.,:nth-child(1)
)'attributes'
- Uses all attributes except those inattributesToIgnore
'data-*'
- Uses specific data attributes (e.g.,data-foo
,data-test-id
)'attribute:*'
- Uses specific attributes (e.g.,attribute:role
,attribute:aria-label
)
-
attributesToIgnore
(String[], optional) - Array of attribute names to ignore when generating selectors. Default:['id', 'class', 'length']
-
filter
(Function, optional) - A filter function to conditionally reject various traits when building selectors.function filter(type, key, value) { // type: 'attribute', 'class', 'tag', 'nth-child' // key: attribute name (for attributes), class name (for classes), etc. // value: attribute value, class name, tag name, nth-child position // Return true to allow this trait, false to reject it return true; }
-
selectorCache
(Map<Element, String>, optional) - Cache to improve performance of repeated selector generation. The caller is responsible for cache invalidation. -
isUniqueCache
(Map<String, Boolean>, optional) - Cache to improve performance of repeated uniqueness checks. The caller is responsible for cache invalidation.
- String - A unique CSS selector for the element, or
null
if no unique selector can be generated
import unique from '@cypress/unique-selector';
// Get a unique selector for an element
const element = document.querySelector('.my-button');
const selector = unique(element);
// Returns: '#my-button' or '.my-button' or 'button' etc.
// Use only ID and class selectors
const selector = unique(element, {
selectorTypes: ['id', 'class']
});
// Use only data attributes
const selector = unique(element, {
selectorTypes: ['data-test-id', 'data-cy']
});
// Use specific attributes
const selector = unique(element, {
selectorTypes: ['attribute:role', 'attribute:aria-label']
});
// Filter out certain IDs
const selector = unique(element, {
filter: (type, key, value) => {
if (type === 'attribute' && key === 'id') {
// Only allow IDs that contain 'test'
return value.includes('test');
}
return true;
}
});
// Filter out certain classes
const selector = unique(element, {
filter: (type, key, value) => {
if (type === 'class') {
// Only allow classes that start with 'btn-'
return value.startsWith('btn-');
}
return true;
}
});
const selectorCache = new Map();
const isUniqueCache = new Map();
const selector = unique(element, {
selectorCache,
isUniqueCache
});
// Reuse the same caches for multiple calls
const selector2 = unique(anotherElement, {
selectorCache,
isUniqueCache
});
// Works with Shadow DOM elements
const shadowHost = document.querySelector('#shadow-host');
const shadowRoot = shadowHost.attachShadow({ mode: 'open' });
const shadowElement = shadowRoot.querySelector('.shadow-button');
const selector = unique(shadowElement);
// Returns a selector unique within the shadow root context
The library follows this strategy to generate unique selectors:
- Element-level uniqueness: First tries to find a selector that uniquely identifies the element within its parent
- Parent traversal: If element-level uniqueness fails, traverses up the DOM tree, building a path of selectors
- Selector type precedence: Uses the
selectorTypes
array to determine which selector types to try first - Combination fallback: For classes and attributes, tries combinations of multiple selectors if single selectors aren't unique
- Nth-child fallback: Uses nth-child positioning as a last resort
<!-- Element with ID -->
<div id="header">Header</div>
<!-- Selector: #header -->
<!-- Element with unique class -->
<button class="submit-btn">Submit</button>
<!-- Selector: .submit-btn -->
<!-- Element with multiple classes -->
<div class="card primary large">Card</div>
<!-- Selector: .card.primary.large -->
<!-- Element with data attributes -->
<div data-test-id="user-profile">Profile</div>
<!-- Selector: [data-test-id="user-profile"] -->
<!-- Element with custom attributes -->
<button role="button" aria-label="Close">×</button>
<!-- Selector: [role="button"] or [aria-label="Close"] -->
<!-- Element requiring nth-child -->
<div class="item">Item 1</div>
<div class="item">Item 2</div>
<!-- Selector: :nth-child(1) or :nth-child(2) -->
<!-- Complex nested element -->
<div class="container">
<div class="wrapper">
<span class="text">Hello</span>
</div>
</div>
<!-- Selector: .container > .wrapper > .text -->