Skip to content

Commit 78776e8

Browse files
committed
feat: Add optional prevElementSibling adapter method
Already exists in DomUtils, and helps speed things up.
1 parent 7b35c8a commit 78776e8

File tree

3 files changed

+45
-1
lines changed

3 files changed

+45
-1
lines changed

src/general.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,13 @@ export function compileGeneralSelector<Node, ElementNode extends Node>(
135135
};
136136
}
137137
case SelectorType.Adjacent: {
138+
if (adapter.prevElementSibling) {
139+
return function adjacent(elem: ElementNode): boolean {
140+
const previous = adapter.prevElementSibling!(elem);
141+
return previous != null && next(previous);
142+
};
143+
}
144+
138145
return function adjacent(elem: ElementNode): boolean {
139146
const siblings = adapter.getSiblings(elem);
140147
let lastElement;

src/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ export interface Adapter<Node, ElementNode extends Node> {
4040
*/
4141
getSiblings: (node: Node) => Node[];
4242

43+
/**
44+
* Returns the previous element sibling of a node.
45+
*/
46+
prevElementSibling?: (node: Node) => ElementNode | null;
47+
4348
/**
4449
* Get the text content of the node, and its children if it has any.
4550
*/

test/api.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import * as CSSselect from "../src";
22
import { parseDOM, parseDocument } from "htmlparser2";
33
import { trueFunc, falseFunc } from "boolbase";
4-
import type { Element } from "domhandler";
4+
import * as DomUtils from "domutils";
5+
import type { Node, Element } from "domhandler";
56
import { SelectorType, AttributeAction } from "css-what";
7+
import { Adapter } from "../src/types";
68

79
const [dom] = parseDOM("<div id=foo><p>foo</p></div>") as Element[];
810
const [xmlDom] = parseDOM("<DiV id=foo><P>foo</P></DiV>", {
@@ -294,4 +296,34 @@ describe("API", () => {
294296
expect(CSSselect.selectAll(query, [dom])).toHaveLength(1);
295297
});
296298
});
299+
300+
describe("optional adapter methods", () => {
301+
it("should support prevElementSibling", () => {
302+
const adapter = {
303+
...DomUtils,
304+
prevElementSibling: undefined,
305+
} as Adapter<Node, Element>;
306+
307+
const dom = parseDOM(
308+
`${"<p>foo".repeat(10)}<div>bar</div>`
309+
) as Element[];
310+
311+
expect(
312+
CSSselect.selectAll("p + div", dom, { adapter })
313+
).toStrictEqual(CSSselect.selectAll("p + div", dom));
314+
});
315+
316+
it("should support isHovered", () => {
317+
const dom = parseDOM(`${"<p>foo".repeat(10)}`) as Element[];
318+
319+
const adapter = {
320+
...DomUtils,
321+
isHovered: (el) => el === dom[dom.length - 1],
322+
} as Adapter<Node, Element>;
323+
324+
const selection = CSSselect.selectAll("p:hover", dom, { adapter });
325+
expect(selection).toHaveLength(1);
326+
expect(selection[0]).toBe(dom[dom.length - 1]);
327+
});
328+
});
297329
});

0 commit comments

Comments
 (0)