From 12fc5de3a7c3b76f983e65e153a3dc1fa9d76412 Mon Sep 17 00:00:00 2001 From: Yanzhen Yu Date: Wed, 1 Apr 2026 12:00:00 +0800 Subject: [PATCH] check whether the removed node's ancestors has been removed --- src/record/observer.ts | 3 ++- src/replay/index.ts | 2 +- src/utils.ts | 18 ++++++++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/record/observer.ts b/src/record/observer.ts index 3abba73a..537a3154 100644 --- a/src/record/observer.ts +++ b/src/record/observer.ts @@ -7,6 +7,7 @@ import { getWindowHeight, getWindowWidth, isBlocked, + isAncestorRemoved, } from '../utils'; import { mutationCallBack, @@ -118,7 +119,7 @@ function initMutationObserver(cb: mutationCallBack): MutationObserver { * before callback fired, so we can ignore it. * TODO: verify this */ - } else if (!mirror.has(parentId)) { + } else if (isAncestorRemoved(target as INode)) { /** * If parent id was not in the mirror map any more, it * means the parent node has already been removed. So diff --git a/src/replay/index.ts b/src/replay/index.ts index ae00635c..1c345fda 100644 --- a/src/replay/index.ts +++ b/src/replay/index.ts @@ -625,7 +625,7 @@ export class Replayer { private debugNodeNotFound(d: incrementalData, id: number) { /** - * There may me some valid scenes of node not being found. + * There maybe some valid scenes of node not being found. * Because DOM events are macrotask and MutationObserver callback * is microtask, so events fired on a removed DOM may emit * snapshots in the reverse order. diff --git a/src/utils.ts b/src/utils.ts index b770259e..38e6e483 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -126,3 +126,21 @@ export function isBlocked(node: Node | null): boolean { } return isBlocked(node.parentNode); } + +export function isAncestorRemoved(target: INode): boolean { + const id = mirror.getId(target); + if (!mirror.has(id)) { + return true; + } + if ( + target.parentNode && + target.parentNode.nodeType === target.DOCUMENT_NODE + ) { + return false; + } + // if the root is not document, it means the node is not in the DOM tree anymore + if (!target.parentNode) { + return true; + } + return isAncestorRemoved((target.parentNode as unknown) as INode); +}