From 255b45d07b214b733fbc72921f3dfce12d6ef274 Mon Sep 17 00:00:00 2001 From: nikneym Date: Wed, 24 Sep 2025 20:21:08 +0300 Subject: [PATCH] initial `insertAdjacentHTML` attempt --- src/browser/dom/element.zig | 65 +++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/src/browser/dom/element.zig b/src/browser/dom/element.zig index 02d01c4b..13e4049a 100644 --- a/src/browser/dom/element.zig +++ b/src/browser/dom/element.zig @@ -231,6 +231,71 @@ pub const Element = struct { } } + /// Parses the given `input` string and inserts it's children to an element at given `position`. + /// https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML + /// + /// TODO: Support for XML parsing and `TrustedHTML` instances. + pub fn _insertAdjacentHTML(self: *parser.Element, position: []const u8, input: []const u8) !void { + const self_node = parser.elementToNode(self); + const doc = parser.nodeOwnerDocument(self_node) orelse { + return parser.DOMError.WrongDocument; + }; + + // Parse the fragment. + const fragment = try parser.documentParseFragmentFromStr(doc, input); + const fragment_node = parser.documentFragmentToNode(fragment); + + // We always get it wrapped like so: + // { ... } + const html = parser.nodeFirstChild(fragment_node) orelse return; + const head = parser.nodeFirstChild(html) orelse return; + const body = parser.nodeNextSibling(head) orelse return; + + const children = try parser.nodeGetChildNodes(body); + const len = parser.nodeListLength(children); + + if (std.mem.eql(u8, position, "beforeend")) { + for (0..len) |_| { + const child = parser.nodeListItem(children, 0) orelse continue; + _ = try parser.nodeInsertBefore(self_node, child, null); + } + } else if (std.mem.eql(u8, position, "afterbegin")) { + const target = parser.nodeFirstChild(self_node) orelse self_node; + for (0..len) |_| { + const child = parser.nodeListItem(children, 0) orelse continue; + _ = try parser.nodeInsertBefore(target, child, null); + } + } else if (std.mem.eql(u8, position, "beforebegin")) { + const parent = parser.nodeParentNode(self_node) orelse { + return error.NoModificationAllowed; + }; + + // Make sure parent is not Document. + // Should also check for document_fragment and document_type? + if (parser.nodeType(parent) == .document) { + return error.NoModificationAllowed; + } + + for (0..len) |_| { + const child = parser.nodeListItem(children, 0) orelse continue; + _ = try parser.nodeInsertBefore(parent, child, self_node); + } + } else if (std.mem.eql(u8, position, "afterend")) { + const parent = parser.nodeParentNode(self_node) orelse { + return error.NoModificationAllowed; + }; + + if (parser.nodeType(parent) == .document) { + return error.NoModificationAllowed; + } + + for (0..len) |_| { + const child = parser.nodeListItem(children, 0) orelse continue; + _ = try parser.nodeInsertBefore(parent, child, null); + } + } + } + // 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, page: *Page) !?*parser.Element {