diff --git a/src/browser/dom/element.zig b/src/browser/dom/element.zig index fb15304f..b256c841 100644 --- a/src/browser/dom/element.zig +++ b/src/browser/dom/element.zig @@ -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 { return try parser.nodeHasAttributes(parser.elementToNode(self)); } @@ -401,6 +425,20 @@ test "Browser.DOM.Element" { .{ "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(&.{ .{ "let a = document.getElementById('content')", "undefined" }, .{ "a.hasAttributes()", "true" },