diff --git a/src/browser/tests/element/replace_with.html b/src/browser/tests/element/replace_with.html
index 14940f4d..0597b9b1 100644
--- a/src/browser/tests/element/replace_with.html
+++ b/src/browser/tests/element/replace_with.html
@@ -332,3 +332,34 @@
testing.expectEqual(new13, document.getElementById('new13'));
testing.expectEqual(l4, new13.parentElement);
+
+
+
diff --git a/src/browser/tests/node/replace_child.html b/src/browser/tests/node/replace_child.html
index 51b0a173..b45a3d51 100644
--- a/src/browser/tests/node/replace_child.html
+++ b/src/browser/tests/node/replace_child.html
@@ -40,3 +40,34 @@
testing.expectEqual(c3, d1.replaceChild(c3, c3));
assertChildren([c3, c4], d1)
+
+
diff --git a/src/browser/webapi/Element.zig b/src/browser/webapi/Element.zig
index 655a4e9d..a26f1f80 100644
--- a/src/browser/webapi/Element.zig
+++ b/src/browser/webapi/Element.zig
@@ -872,7 +872,9 @@ pub fn replaceWith(self: *Element, nodes: []const Node.NodeOrText, page: *Page)
);
}
- if (rm_ref_node) {
+ // Re-check parent after insertNodeRelative since callbacks (e.g. connectedCallback)
+ // could have already removed ref_node from parent.
+ if (rm_ref_node and ref_node._parent == parent) {
page.removeNode(parent, ref_node, .{ .will_be_reconnected = false });
}
}
diff --git a/src/browser/webapi/Node.zig b/src/browser/webapi/Node.zig
index 5871abee..d6d73fee 100644
--- a/src/browser/webapi/Node.zig
+++ b/src/browser/webapi/Node.zig
@@ -624,7 +624,9 @@ pub fn replaceChild(self: *Node, new_child: *Node, old_child: *Node, page: *Page
// Special case: if we replace a node by itself, we don't remove it.
// insertBefore is an noop in this case.
- if (new_child != old_child) {
+ // Re-check parent after insertBefore since callbacks (e.g. connectedCallback)
+ // could have already removed old_child from self.
+ if (new_child != old_child and old_child._parent == self) {
page.removeNode(self, old_child, .{ .will_be_reconnected = false });
}