From 21465e785f7747f33c3fb1b3964b81634d224736 Mon Sep 17 00:00:00 2001 From: Yanzhen Yu Date: Wed, 1 Apr 2026 12:00:00 +0800 Subject: [PATCH] resolve #13 add warning messages when target is not found --- guide.md | 13 ++++++----- guide.zh_CN.md | 1 + src/replay/index.ts | 53 ++++++++++++++++++++++++++++++++------------- src/types.ts | 1 + 4 files changed, 47 insertions(+), 21 deletions(-) diff --git a/guide.md b/guide.md index 01c5a58e..b81b62b5 100644 --- a/guide.md +++ b/guide.md @@ -134,12 +134,13 @@ replayer.play(); The replayer accepts options as its constructor's second parameter, and it has the following options: -| key | default | description | -| ------------ | ------------- | ------------------------------------- | -| speed | 1 | replay speed ratio | -| root | document.body | the root element of replayer | -| loadTimeout | 0 | timeout of loading remote style sheet | -| skipInactive | false | whether to skip inactive time | +| key | default | description | +| ------------ | ------------- | ----------------------------------------------- | +| speed | 1 | replay speed ratio | +| root | document.body | the root element of replayer | +| loadTimeout | 0 | timeout of loading remote style sheet | +| skipInactive | false | whether to skip inactive time | +| showWarning | true | whether to print warning messages during replay | #### Use rrweb-player diff --git a/guide.zh_CN.md b/guide.zh_CN.md index 72d5fca3..c827909c 100644 --- a/guide.zh_CN.md +++ b/guide.zh_CN.md @@ -136,6 +136,7 @@ replayer.play(); | root | document.body | 回放时使用的 HTML 元素 | | loadTimeout | 0 | 加载异步样式表的超时时长 | | skipInactive | false | 是否快速跳过无用户操作的阶段 | +| showWarning | true | 是否在回放过程中打印警告信息 | #### 使用 rrweb-player diff --git a/src/replay/index.ts b/src/replay/index.ts index c27a58c4..f496e4ce 100644 --- a/src/replay/index.ts +++ b/src/replay/index.ts @@ -16,6 +16,7 @@ import { missingNode, actionWithDelay, incrementalSnapshotEvent, + incrementalData, } from '../types'; import { mirror } from '../utils'; import injectStyleRules from './styles/inject-style'; @@ -30,6 +31,8 @@ smoothscroll.polyfill(); // tslint:disable-next-line const mitt = (mittProxy as any).default || mittProxy; +const REPLAY_CONSOLE_PREFIX = '[replayer]'; + export class Replayer { public wrapper: HTMLDivElement; @@ -64,6 +67,7 @@ export class Replayer { root: document.body, loadTimeout: 0, skipInactive: false, + showWarning: true, }; this.config = Object.assign({}, defaultConfig, config); @@ -324,7 +328,7 @@ export class Replayer { d.removes.forEach(mutation => { const target = mirror.getNode(mutation.id); if (!target) { - return; + return this.warnTargetNotFound(d, mutation.id); } const parent = (mirror.getNode( mutation.parentId!, @@ -410,9 +414,10 @@ export class Replayer { this.mouse.style.left = `${p.x}px`; this.mouse.style.top = `${p.y}px`; const target = mirror.getNode(p.id); - if (target) { - this.hoverElements((target as Node) as Element); + if (!target) { + return this.warnTargetNotFound(d, p.id); } + this.hoverElements((target as Node) as Element); }, delay: p.timeOffset + e.timestamp - this.baselineTime, }; @@ -428,13 +433,16 @@ export class Replayer { break; } const event = new Event(MouseInteractions[d.type].toLowerCase()); - const target = (mirror.getNode(d.id) as Node) as HTMLElement; + const target = mirror.getNode(d.id); + if (!target) { + return this.warnTargetNotFound(d, d.id); + } switch (d.type) { case MouseInteractions.Blur: - target.blur(); + ((target as Node) as HTMLElement).blur(); break; case MouseInteractions.Focus: - target.focus({ + ((target as Node) as HTMLElement).focus({ preventScroll: true, }); break; @@ -465,8 +473,11 @@ export class Replayer { if (d.id === -1) { break; } - const target = mirror.getNode(d.id) as Node; - if (target === this.iframe.contentDocument) { + const target = mirror.getNode(d.id); + if (!target) { + return this.warnTargetNotFound(d, d.id); + } + if ((target as Node) === this.iframe.contentDocument) { this.iframe.contentWindow!.scrollTo({ top: d.y, left: d.x, @@ -474,8 +485,8 @@ export class Replayer { }); } else { try { - (target as Element).scrollTop = d.y; - (target as Element).scrollLeft = d.x; + ((target as Node) as Element).scrollTop = d.y; + ((target as Node) as Element).scrollLeft = d.x; } catch (error) { /** * Seldomly we may found scroll target was removed before @@ -501,12 +512,13 @@ export class Replayer { if (d.id === -1) { break; } - const target: HTMLInputElement = (mirror.getNode( - d.id, - ) as Node) as HTMLInputElement; + const target = mirror.getNode(d.id); + if (!target) { + return this.warnTargetNotFound(d, d.id); + } try { - target.checked = d.isChecked; - target.value = d.text; + ((target as Node) as HTMLInputElement).checked = d.isChecked; + ((target as Node) as HTMLInputElement).value = d.text; } catch (error) { // for safe } @@ -577,4 +589,15 @@ export class Replayer { this.emitter.emit('skip-end', payload); this.noramlSpeed = -1; } + + private warnTargetNotFound(d: incrementalData, id: number) { + if (!this.config.showWarning) { + return; + } + console.warn( + REPLAY_CONSOLE_PREFIX, + `target with id '${id}' not found in`, + d, + ); + } } diff --git a/src/types.ts b/src/types.ts index 86ffb8f0..572b151f 100644 --- a/src/types.ts +++ b/src/types.ts @@ -229,6 +229,7 @@ export type playerConfig = { root: Element; loadTimeout: number; skipInactive: Boolean; + showWarning: Boolean; }; export type playerMetaData = {