Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: optimize unique query #4

Merged
merged 11 commits into from
Dec 20, 2023
18 changes: 18 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Test

on: [push]

jobs:
test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: '20.x'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm run test
62 changes: 40 additions & 22 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,12 @@ function getAllSelectors( el, selectors, attributesToIgnore )
function testUniqueness( element, selector )
{
const { parentNode } = element;
const elements = parentNode.querySelectorAll( selector );
return elements.length === 1 && elements[ 0 ] === element;
try {
const elements = parentNode.querySelectorAll( selector );
return elements.length === 1 && elements[0] === element;
} catch (e) {
return false
}
}

/**
Expand Down Expand Up @@ -167,31 +171,45 @@ function getUniqueSelector( element, selectorTypes, attributesToIgnore )
* @api private
*/

export default function unique( el, options={} )
{
const { selectorTypes=['id', 'class', 'tag', 'nth-child'], attributesToIgnore= ['id', 'class', 'length'] } = options;
export default function unique( el, options={} ) {
const {
selectorTypes=['id', 'class', 'tag', 'nth-child'],
attributesToIgnore= ['id', 'class', 'length'],
selectorCache,
isUniqueCache
} = options;
const allSelectors = [];
const parents = getParents( el );

for( let elem of parents )
{
const selector = getUniqueSelector( elem, selectorTypes, attributesToIgnore );
if( Boolean( selector ) )
{
allSelectors.push( selector );
let currentElement = el
while (currentElement) {
let selector = selectorCache ? selectorCache.get(currentElement) : undefined

if (!selector) {
selector = getUniqueSelector(
currentElement,
selectorTypes,
attributesToIgnore
)
if (selectorCache) {
selectorCache.set(currentElement, selector)
}
}

allSelectors.unshift(selector)
const maybeUniqueSelector = allSelectors.join(' > ')
let isUniqueSelector = isUniqueCache ? isUniqueCache.get(maybeUniqueSelector) : undefined
if (isUniqueSelector === undefined) {
isUniqueSelector = isUnique(el, maybeUniqueSelector)
if (isUniqueCache) {
isUniqueCache.set(maybeUniqueSelector, isUniqueSelector)
}
}
}

const selectors = [];
for( let it of allSelectors )
{
selectors.unshift( it );
const selector = selectors.join( ' > ' );
if( isUnique( el, selector ) )
{
return selector;
if (isUniqueSelector) {
return maybeUniqueSelector
}
}
currentElement = currentElement.parentNode
}

return null;
}
8 changes: 6 additions & 2 deletions src/isUnique.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
export function isUnique( el, selector )
{
if( !Boolean( selector ) ) return false;
const elems = el.ownerDocument.querySelectorAll( selector );
return elems.length === 1 && elems[ 0 ] === el;
try {
var elems = el.ownerDocument.querySelectorAll(selector);
return elems.length === 1 && elems[0] === el;
} catch (e) {
return false
}
}