UNPKG

unique-selector

Version:

Given a DOM node, return a unique CSS selector matching only that element

201 lines (176 loc) 4.87 kB
/** * Expose `unique` */ import { getID } from './getID'; import { getClassSelectors } from './getClasses'; import { getCombinations } from './getCombinations'; import { getAttributes } from './getAttributes'; import { getNthChild } from './getNthChild'; import { getTag } from './getTag'; import { isUnique } from './isUnique'; import { getParents } from './getParents'; /** * Returns all the selectors of the elmenet * @param { Object } element * @return { Object } */ function getAllSelectors( el, selectors, attributesToIgnore ) { const funcs = { 'Tag' : getTag, 'NthChild' : getNthChild, 'Attributes' : elem => getAttributes( elem, attributesToIgnore ), 'Class' : getClassSelectors, 'ID' : getID, }; return selectors.reduce( ( res, next ) => { res[ next ] = funcs[ next ]( el ); return res; }, {} ); } /** * Tests uniqueNess of the element inside its parent * @param { Object } element * @param { String } Selectors * @return { Boolean } */ function testUniqueness( element, selector ) { const { parentNode } = element; const elements = parentNode.querySelectorAll( selector ); return elements.length === 1 && elements[ 0 ] === element; } /** * Tests all selectors for uniqueness and returns the first unique selector. * @param { Object } element * @param { Array } selectors * @return { String } */ function getFirstUnique( element, selectors ) { return selectors.find( testUniqueness.bind( null, element ) ); } /** * Checks all the possible selectors of an element to find one unique and return it * @param { Object } element * @param { Array } items * @param { String } tag * @return { String } */ function getUniqueCombination( element, items, tag ) { let combinations = getCombinations( items, 3 ), firstUnique = getFirstUnique( element, combinations ); if( Boolean( firstUnique ) ) { return firstUnique; } if( Boolean( tag ) ) { combinations = combinations.map( combination => tag + combination ); firstUnique = getFirstUnique( element, combinations ); if( Boolean( firstUnique ) ) { return firstUnique; } } return null; } /** * Returns a uniqueSelector based on the passed options * @param { DOM } element * @param { Array } options * @return { String } */ function getUniqueSelector( element, selectorTypes, attributesToIgnore, excludeRegex ) { let foundSelector; const elementSelectors = getAllSelectors( element, selectorTypes, attributesToIgnore ); if( excludeRegex && excludeRegex instanceof RegExp ) { elementSelectors.ID = excludeRegex.test( elementSelectors.ID ) ? null : elementSelectors.ID; elementSelectors.Class = elementSelectors.Class.filter( className => !excludeRegex.test( className ) ); } for( let selectorType of selectorTypes ) { const { ID, Tag, Class : Classes, Attributes, NthChild } = elementSelectors; switch ( selectorType ) { case 'ID' : if ( Boolean( ID ) && testUniqueness( element, ID ) ) { return ID; } break; case 'Tag': if ( Boolean( Tag ) && testUniqueness( element, Tag ) ) { return Tag; } break; case 'Class': if ( Boolean( Classes ) && Classes.length ) { foundSelector = getUniqueCombination( element, Classes, Tag ); if (foundSelector) { return foundSelector; } } break; case 'Attributes': if ( Boolean( Attributes ) && Attributes.length ) { foundSelector = getUniqueCombination( element, Attributes, Tag ); if ( foundSelector ) { return foundSelector; } } break; case 'NthChild': if ( Boolean( NthChild ) ) { return NthChild } } } return '*'; } /** * Generate unique CSS selector for given DOM element * * @param {Element} el * @return {String} * @api private */ export default function unique( el, options={} ) { const { selectorTypes = ['ID', 'Class', 'Tag', 'NthChild'], attributesToIgnore = ['id', 'class', 'length'], excludeRegex = null, } = options; const allSelectors = []; const parents = getParents( el ); for( let elem of parents ) { const selector = getUniqueSelector( elem, selectorTypes, attributesToIgnore, excludeRegex ); if( Boolean( selector ) ) { allSelectors.push( selector ); } } const selectors = []; for( let it of allSelectors ) { selectors.unshift( it ); const selector = selectors.join( ' > ' ); if( isUnique( el, selector ) ) { return selector; } } return null; }