From 991531d02419eeb8d499761127c813128985667b Mon Sep 17 00:00:00 2001 From: fczbkk Date: Sun, 15 Aug 2021 09:10:32 +0200 Subject: [PATCH] feat: multi elements support for fallback --- src/constants.ts | 1 + src/selector-fallback.ts | 18 ++++++++++++------ src/types.ts | 2 ++ src/utilities-dom.ts | 9 +++++++++ src/utilities-selectors.ts | 8 ++++++-- test/selector-fallback.spec.js | 24 +++++++++++++++++++----- 6 files changed, 49 insertions(+), 13 deletions(-) diff --git a/src/constants.ts b/src/constants.ts index 5eab38b42..880de1272 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,5 +1,6 @@ export const DESCENDANT_OPERATOR = ' > ' export const CHILD_OPERATOR = ' ' +export const SELECTOR_SEPARATOR = ', ' // RegExp that will match invalid patterns that can be used in ID attribute. export const INVALID_ID_RE = new RegExp([ diff --git a/src/selector-fallback.ts b/src/selector-fallback.ts index e58028be6..e55363d26 100644 --- a/src/selector-fallback.ts +++ b/src/selector-fallback.ts @@ -1,14 +1,20 @@ import {getParents} from './utilities-dom' import {getNthChildSelector} from './selector-nth-child' -import {DESCENDANT_OPERATOR} from './constants' -import {CssSelector} from './types' +import {DESCENDANT_OPERATOR, SELECTOR_SEPARATOR} from './constants'; +import {CssSelector, SelectorNeedle} from './types'; +import {sanitizeSelectorNeedle} from './utilities-selectors'; -/** - * Creates chain if :nth-child selectors from root to the element. - */ -export function getFallbackSelector (element: Element): CssSelector { +export function constructFallbackSelector (element: Element): CssSelector { const selectors = getParents(element) .map((element) => getNthChildSelector(element)[0]) .reverse() return [':root', ...selectors].join(DESCENDANT_OPERATOR) } + +/** + * Creates chain if :nth-child selectors from root to the element. + */ +export function getFallbackSelector (needle: SelectorNeedle): CssSelector { + const elements = sanitizeSelectorNeedle(needle) + return elements.map(constructFallbackSelector).join(SELECTOR_SEPARATOR) +} diff --git a/src/types.ts b/src/types.ts index 4ec99b160..989493a89 100644 --- a/src/types.ts +++ b/src/types.ts @@ -34,3 +34,5 @@ export type CssSelectorGeneratorOptions = { export type IdentifiableParent = null | { foundElement: Element, selector: CssSelector } + +export type SelectorNeedle = Element | Array diff --git a/src/utilities-dom.ts b/src/utilities-dom.ts index edba6bc1a..039400018 100644 --- a/src/utilities-dom.ts +++ b/src/utilities-dom.ts @@ -13,6 +13,15 @@ export function testSelector ( return (result.length === 1 && result[0] === element) } +export function testMultiSelector ( + element: Element, + selector: CssSelector, + root: ParentNode = document +): boolean { + const result = [...root.querySelectorAll(selector)] + return result.includes(element) +} + /** * Find all parent elements of the element. */ diff --git a/src/utilities-selectors.ts b/src/utilities-selectors.ts index a0ae13faa..e92efb70a 100644 --- a/src/utilities-selectors.ts +++ b/src/utilities-selectors.ts @@ -21,8 +21,8 @@ import { CssSelectorData, CssSelectorGeneratorOptions, CssSelectorType, - IdentifiableParent -} from './types' + IdentifiableParent, SelectorNeedle +} from './types'; export const ESCAPED_COLON = ':' .charCodeAt(0) @@ -327,3 +327,7 @@ export function getClosestIdentifiableParent ( } return null } + +export function sanitizeSelectorNeedle (needle: SelectorNeedle) { + return Array.isArray(needle) ? needle : [needle] +} diff --git a/test/selector-fallback.spec.js b/test/selector-fallback.spec.js index 2c7f50459..4f1f22e52 100644 --- a/test/selector-fallback.spec.js +++ b/test/selector-fallback.spec.js @@ -1,6 +1,6 @@ import {assert} from 'chai' import {getFallbackSelector} from '../src/selector-fallback.ts' -import {testSelector} from '../src/utilities-dom.ts' +import {testMultiSelector, testSelector} from '../src/utilities-dom.ts' describe('selector - fallback', function () { @@ -17,7 +17,7 @@ describe('selector - fallback', function () { it('should produce simple selector', () => { root.innerHTML = '
' const needleElement = root.firstChild - const result = getFallbackSelector(needleElement, root) + const result = getFallbackSelector(needleElement) assert.ok(testSelector(needleElement, result, root)) }) @@ -27,14 +27,14 @@ describe('selector - fallback', function () { .firstElementChild .firstElementChild .firstElementChild - const result = getFallbackSelector(needleElement, root) + const result = getFallbackSelector(needleElement) assert.ok(testSelector(needleElement, result, root)) }) it('should produce selector beside similar elements', () => { root.innerHTML = '
' const needleElement = root.firstElementChild - const result = getFallbackSelector(needleElement, root) + const result = getFallbackSelector(needleElement) assert.ok(testSelector(needleElement, result, root)) }) @@ -57,7 +57,21 @@ describe('selector - fallback', function () { .firstElementChild .firstElementChild .firstElementChild - const result = getFallbackSelector(needleElement, haystackElement) + const result = getFallbackSelector(needleElement) assert.ok(testSelector(needleElement, result, haystackElement)) }) + + it('should produce selector for multiple elements', () => { + root.innerHTML = ` +
+
+
+ ` + const needleElements = [...root.querySelectorAll('div')] + const result = getFallbackSelector(needleElements) + console.log('x', result) + needleElements.forEach((element) => { + assert.ok(testMultiSelector(element, result, root)) + }) + }) })