Element closest

This commit is contained in:
sjorsdonkers
2025-05-12 17:20:43 +02:00
parent b086337dbe
commit dd7e6d3831

View File

@@ -135,6 +135,30 @@ pub const Element = struct {
} }
} }
// The closest() method of the Element interface traverses the element and its parents (heading toward the document root) until it finds a node that matches the specified CSS selector.
// Returns the closest ancestor Element or itself, which matches the selectors. If there are no such element, null.
pub fn _closest(self: *parser.Element, selector: []const u8, state: *SessionState) !?*parser.Element {
const cssParse = @import("../css/css.zig").parse;
const CssNodeWrap = @import("../css/libdom.zig").Node;
const select = try cssParse(state.call_arena, selector, .{});
var current: CssNodeWrap = .{ .node = parser.elementToNode(self) };
while (true) {
if (try select.match(current)) {
if (!current.isElement()) {
std.debug.print("closest: is not an element: {s}\n", .{try current.tag()});
return null;
}
return parser.nodeToElement(current.node);
}
if (try current.parent()) |parent| {
current = parent;
continue;
}
return null;
}
}
pub fn _hasAttributes(self: *parser.Element) !bool { pub fn _hasAttributes(self: *parser.Element) !bool {
return try parser.nodeHasAttributes(parser.elementToNode(self)); return try parser.nodeHasAttributes(parser.elementToNode(self));
} }
@@ -401,6 +425,20 @@ test "Browser.DOM.Element" {
.{ "cl.length", "2" }, .{ "cl.length", "2" },
}, .{}); }, .{});
try runner.testCases(&.{
.{ "const el2 = document.createElement('div');", "undefined" },
.{ "el2.id = 'closest'; el2.className = 'ok';", "ok" },
.{ "el2.closest('#closest')", "[object HTMLDivElement]" },
.{ "el2.closest('.ok')", "[object HTMLDivElement]" },
.{ "el2.closest('#9000')", "null" },
.{ "el2.closest('.notok')", "null" },
.{ "const sp = document.createElement('span');", "undefined" },
.{ "el2.appendChild(sp);", "[object HTMLSpanElement]" },
.{ "sp.closest('#closest')", "[object HTMLDivElement]" },
.{ "sp.closest('#9000')", "null" },
}, .{});
try runner.testCases(&.{ try runner.testCases(&.{
.{ "let a = document.getElementById('content')", "undefined" }, .{ "let a = document.getElementById('content')", "undefined" },
.{ "a.hasAttributes()", "true" }, .{ "a.hasAttributes()", "true" },