Skip to content

Commit 0e05425

Browse files
committed
feat: improve queryElements()
1 parent e8fd9fb commit 0e05425

File tree

1 file changed

+136
-118
lines changed

1 file changed

+136
-118
lines changed

src/index.js

Lines changed: 136 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -540,132 +540,117 @@
540540
return path;
541541
}
542542

543-
function queryElements({ element, prefix, type, selector }) {
544-
let elements = new Map();
545-
if (!element) element = document;
543+
function queryElements({ element = document, prefix, type, selector }) {
544+
let elements = new Set();
545+
let queryTypes = [
546+
"selector",
547+
"closest",
548+
"parent",
549+
"next",
550+
"previous",
551+
"document",
552+
"frame",
553+
"top"
554+
];
555+
546556
let hasAttribute = false;
547557

548-
if (selector) {
549-
if (!type) type = ["selector"];
550-
else if (!Array.isArray(type)) type = [type];
551-
} else type = ["selector", "closest", "parent", "next", "previous", "document"];
558+
if (!prefix) {
559+
for (let attr of element.attributes) {
560+
let parts = attr.name.split("-");
561+
if (parts.length < 2) continue;
562+
563+
let possibleType = parts.pop();
564+
if (queryTypes.includes(possibleType)) {
565+
type = [possibleType];
566+
prefix = parts.join("-");
567+
break;
568+
}
569+
}
570+
} else if (!type) {
571+
for (let i = 0; i < queryTypes.length; i++) {
572+
if (element.hasAttribute(`${prefix}-${queryTypes[i]}`)) {
573+
type = [queryTypes[i]];
574+
}
575+
}
576+
}
577+
578+
if (!prefix) return false;
579+
580+
if (!type) type = selector ? ["selector"] : queryTypes;
581+
582+
if (!Array.isArray(type)) type = [type];
552583

553584
for (let i = 0; i < type.length; i++) {
554-
let Selector = selector;
555-
if (!Selector && element.nodeType !== 9) {
585+
if (!selector && element.nodeType !== 9) {
556586
let name = prefix + "-" + type[i];
557587
if (!element.hasAttribute(name)) continue;
558588
hasAttribute = true;
559-
Selector = element.getAttribute(name);
589+
selector = element.getAttribute(name);
590+
type = [type[i]];
560591
}
561592

562-
if (Selector) {
563-
let selectors = Selector.split(/,(?![^()]*\))/g);
593+
let mainElement = element;
594+
if (
595+
[
596+
"parent",
597+
"next",
598+
"previous",
599+
"document",
600+
"frame",
601+
"top"
602+
].includes(type[i])
603+
) {
604+
mainElement = queryType(mainElement, type[i]);
605+
}
606+
607+
if (!selector) {
608+
elements.add(mainElement);
609+
} else {
610+
let selectors = selector.split(/,(?![^()]*\))/g);
564611

565612
for (let j = 0; j < selectors.length; j++) {
613+
if (!selectors[j]) continue;
614+
615+
let queriedElement = mainElement;
616+
566617
if (selectors[j].includes("@")) {
567618
selectors[j] = checkMediaQueries(selectors[j]);
568619
if (selectors[j] === false) continue;
569620
}
570621

571-
let queriedElement = element;
622+
if (type[i] === "closest") {
623+
let [closestSelector, remainingSelector = ""] =
624+
selectors[j].split(/\s+/, 2);
625+
queriedElement =
626+
queriedElement.closest(closestSelector);
627+
628+
if (!queriedElement) continue;
629+
630+
selectors[j] = remainingSelector;
631+
}
632+
572633
let specialSelectors = selectors[j].split(";");
573634
for (let k = 0; k < specialSelectors.length; k++) {
574-
// TODO: Support an array of queried elements and branch off to return matches for each
575-
// if (!Array.isArray(queriedElement)) {
576-
// queriedElement = [queriedElement]
577-
// }
578-
635+
specialSelectors[k] = specialSelectors[k].trim();
579636
if (!specialSelectors[k]) continue;
580-
if (k === 0) {
581-
if (type[i] === "parent")
582-
queriedElement = queriedElement.parentElement;
583-
else if (type[i] === "next")
584-
queriedElement =
585-
queriedElement.nextElementSibling;
586-
else if (type[i] === "previous")
587-
queriedElement =
588-
queriedElement.previousElementSibling;
589-
else if (type[i] === "document")
590-
queriedElement = queriedElement.ownerDocument;
591-
} else if (queriedElement.contentDocument) {
592-
queriedElement = queriedElement.contentDocument;
637+
if (queryTypes.includes(specialSelectors[k])) {
638+
queriedElement = queryType(
639+
queriedElement,
640+
specialSelectors[k]
641+
);
642+
continue; // Skip directly to the next specialSelector
593643
}
594644

595-
switch (
596-
(specialSelectors[k] = specialSelectors[k].trim())
597-
) {
598-
case "top":
599-
queriedElement = window.top.document;
600-
break;
601-
case "frame":
602-
if (queriedElement.nodeType === 9)
603-
queriedElement =
604-
queriedElement.window.frameElement;
605-
else if (queriedElement.contentDocument)
606-
queriedElement =
607-
queriedElement.contentDocument;
608-
break;
609-
case "document":
610-
queriedElement = document;
611-
break;
612-
case "parent":
613-
queriedElement = queriedElement.parentElement;
614-
break;
615-
case "next":
616-
queriedElement =
617-
queriedElement.nextElementSibling;
618-
break;
619-
case "previous":
620-
queriedElement =
621-
queriedElement.previousElementSibling;
622-
break;
623-
default:
624-
if (k === 0 && type[i] === "closest")
625-
if (specialSelectors[k].includes(" ")) {
626-
let [firstSelector, ...restSelectors] =
627-
specialSelectors[k].split(/ (.+)/);
628-
queriedElement =
629-
queriedElement.closest(
630-
firstSelector
631-
);
632-
if (restSelectors.length > 0) {
633-
if (restSelectors[0].endsWith("[]"))
634-
queriedElement =
635-
queriedElement.querySelectorAll(
636-
restSelectors[0].slice(
637-
0,
638-
-2
639-
)
640-
);
641-
else
642-
queriedElement =
643-
queriedElement.querySelector(
644-
restSelectors[0]
645-
);
646-
}
647-
} else {
648-
// If no space, just use the selector with closest
649-
queriedElement = queriedElement.closest(
650-
specialSelectors[k]
651-
);
652-
}
653-
else if (
654-
specialSelectors[k] === "$clickedElement"
655-
) {
656-
queriedElement =
657-
queriedElement.clickedElement;
658-
} else if (specialSelectors[k].endsWith("[]"))
659-
queriedElement =
660-
queriedElement.querySelectorAll(
661-
specialSelectors[k].slice(0, -2)
662-
);
663-
else
664-
queriedElement =
665-
queriedElement.querySelector(
666-
specialSelectors[k]
667-
);
645+
if (queriedElement.contentDocument) {
646+
queriedElement = queriedElement.contentDocument;
668647
}
648+
649+
queriedElement = querySelector(
650+
queriedElement,
651+
specialSelectors[k]
652+
);
653+
669654
if (!queriedElement) break;
670655
}
671656

@@ -675,32 +660,65 @@
675660
queriedElement instanceof NodeList
676661
) {
677662
for (let el of queriedElement) {
678-
elements.set(el, "");
663+
if (el instanceof Element) {
664+
elements.add(el);
665+
}
679666
}
680-
} else if (queriedElement) {
681-
elements.set(queriedElement, "");
667+
} else if (queriedElement instanceof Element) {
668+
elements.add(queriedElement);
682669
}
683670
}
684-
} else if (Selector === "") {
685-
if (type[i] === "parent" && element.parentElement)
686-
elements.set(element.parentElement, "");
687-
else if (type[i] === "next" && element.nextElementSibling)
688-
elements.set(element.nextElementSibling, "");
689-
else if (
690-
type[i] === "previous" &&
691-
element.previousElementSibling
692-
)
693-
elements.set(element.previousElementSibling, "");
694671
}
695672
}
696673

697674
if (!hasAttribute && !selector) {
698675
elements = false;
699-
} else elements = Array.from(elements.keys());
676+
} else {
677+
elements = Array.from(elements);
678+
}
700679

701680
return elements;
702681
}
703682

683+
function queryType(element, type) {
684+
if (!element) return null;
685+
686+
switch (type) {
687+
case "top":
688+
return window.top.document;
689+
case "frame":
690+
// ✅ If element is a document, return the iframe element containing it
691+
if (element.nodeType === 9) return window.frameElement;
692+
// ✅ If element is an iframe, return it as is
693+
return element;
694+
case "document":
695+
// ✅ If element is a document, return itself, else return `ownerDocument`
696+
return element.nodeType === 9 ? element : element.ownerDocument;
697+
case "parent":
698+
// ✅ If it's a document, return the parent document (if inside an iframe)
699+
if (element.nodeType === 9) {
700+
return element.defaultView !== window.top
701+
? element.defaultView.parent.document
702+
: null;
703+
}
704+
// ✅ Otherwise, return parent element
705+
return element.parentElement;
706+
case "next":
707+
return element.nextElementSibling;
708+
case "previous":
709+
return element.previousElementSibling;
710+
default:
711+
return null;
712+
}
713+
}
714+
715+
function querySelector(element, selector) {
716+
if (!element) return null;
717+
return selector.endsWith("[]")
718+
? element.querySelectorAll(selector.slice(0, -2))
719+
: element.querySelector(selector);
720+
}
721+
704722
const mediaRanges = {
705723
xs: [0, 575],
706724
sm: [576, 768],

0 commit comments

Comments
 (0)