unique-selector
Version:
Given a DOM node, return a unique CSS selector matching only that element
267 lines (226 loc) • 7.22 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = unique;
var _getID = require('./getID');
var _getClasses = require('./getClasses');
var _getCombinations = require('./getCombinations');
var _getAttributes = require('./getAttributes');
var _getNthChild = require('./getNthChild');
var _getTag = require('./getTag');
var _isUnique = require('./isUnique');
var _getParents = require('./getParents');
/**
* Returns all the selectors of the elmenet
* @param { Object } element
* @return { Object }
*/
/**
* Expose `unique`
*/
function getAllSelectors(el, selectors, attributesToIgnore) {
var funcs = {
'Tag': _getTag.getTag,
'NthChild': _getNthChild.getNthChild,
'Attributes': function Attributes(elem) {
return (0, _getAttributes.getAttributes)(elem, attributesToIgnore);
},
'Class': _getClasses.getClassSelectors,
'ID': _getID.getID
};
return selectors.reduce(function (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) {
var parentNode = element.parentNode;
var 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) {
var combinations = (0, _getCombinations.getCombinations)(items, 3),
firstUnique = getFirstUnique(element, combinations);
if (Boolean(firstUnique)) {
return firstUnique;
}
if (Boolean(tag)) {
combinations = combinations.map(function (combination) {
return 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) {
var foundSelector = void 0;
var elementSelectors = getAllSelectors(element, selectorTypes, attributesToIgnore);
if (excludeRegex && excludeRegex instanceof RegExp) {
elementSelectors.ID = excludeRegex.test(elementSelectors.ID) ? null : elementSelectors.ID;
elementSelectors.Class = elementSelectors.Class.filter(function (className) {
return !excludeRegex.test(className);
});
}
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = selectorTypes[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var selectorType = _step.value;
var ID = elementSelectors.ID,
Tag = elementSelectors.Tag,
Classes = elementSelectors.Class,
Attributes = elementSelectors.Attributes,
NthChild = elementSelectors.NthChild;
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;
}
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return '*';
}
/**
* Generate unique CSS selector for given DOM element
*
* @param {Element} el
* @return {String}
* @api private
*/
function unique(el) {
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var _options$selectorType = options.selectorTypes,
selectorTypes = _options$selectorType === undefined ? ['ID', 'Class', 'Tag', 'NthChild'] : _options$selectorType,
_options$attributesTo = options.attributesToIgnore,
attributesToIgnore = _options$attributesTo === undefined ? ['id', 'class', 'length'] : _options$attributesTo,
_options$excludeRegex = options.excludeRegex,
excludeRegex = _options$excludeRegex === undefined ? null : _options$excludeRegex;
var allSelectors = [];
var parents = (0, _getParents.getParents)(el);
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = parents[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var elem = _step2.value;
var selector = getUniqueSelector(elem, selectorTypes, attributesToIgnore, excludeRegex);
if (Boolean(selector)) {
allSelectors.push(selector);
}
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
var selectors = [];
var _iteratorNormalCompletion3 = true;
var _didIteratorError3 = false;
var _iteratorError3 = undefined;
try {
for (var _iterator3 = allSelectors[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
var it = _step3.value;
selectors.unshift(it);
var _selector = selectors.join(' > ');
if ((0, _isUnique.isUnique)(el, _selector)) {
return _selector;
}
}
} catch (err) {
_didIteratorError3 = true;
_iteratorError3 = err;
} finally {
try {
if (!_iteratorNormalCompletion3 && _iterator3.return) {
_iterator3.return();
}
} finally {
if (_didIteratorError3) {
throw _iteratorError3;
}
}
}
return null;
}