diff --git a/.changeset/polite-olives-wave.md b/.changeset/polite-olives-wave.md new file mode 100644 index 0000000000..1d3e5987f3 --- /dev/null +++ b/.changeset/polite-olives-wave.md @@ -0,0 +1,5 @@ +--- +'rrweb': patch +--- + +Add 'recordDOM' config option to turn off recording of DOM (making recordings unreplayable). Specialist use case e.g. only heatmap click/scroll recording diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts index 0945f585d7..9c4661ecc1 100644 --- a/packages/rrweb/src/record/index.ts +++ b/packages/rrweb/src/record/index.ts @@ -101,6 +101,7 @@ function record( sampling = {}, dataURLOptions = {}, mousemoveWait, + recordDOM = true, recordCanvas = false, recordCrossOriginIframes = false, recordAfter = options.recordAfter === 'DOMContentLoaded' @@ -399,6 +400,9 @@ function record( }); const takeFullSnapshot = (isCheckout = false) => { + if (!recordDOM) { + return; + } wrappedEmit( wrapEvent({ type: EventType.Meta, @@ -604,6 +608,7 @@ function record( maskInputOptions, inlineStylesheet, sampling, + recordDOM, recordCanvas, inlineImages, userTriggeredOnInput, diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts index e975062144..26b36ec144 100644 --- a/packages/rrweb/src/record/observer.ts +++ b/packages/rrweb/src/record/observer.ts @@ -1291,7 +1291,11 @@ export function initObservers( } // We do not use hooks, so we skip this - const mutationObserver = initMutationObserver(o, o.doc); + // mergeHooks(o, hooks); + let mutationObserver: MutationObserver | undefined; + if (o.recordDOM) { + mutationObserver = initMutationObserver(o, o.doc); + } const mousemoveHandler = initMoveObserver(o); const mouseInteractionHandler = initMouseInteractionObserver(o); const scrollHandler = initScrollObserver(o); @@ -1301,16 +1305,20 @@ export function initObservers( const inputHandler = initInputObserver(o); const mediaInteractionHandler = initMediaInteractionObserver(o); - const styleSheetObserver = initStyleSheetObserver(o, { win: currentWindow }); - const adoptedStyleSheetObserver = initAdoptedStyleSheetObserver(o, o.doc); - const styleDeclarationObserver = initStyleDeclarationObserver(o, { - win: currentWindow, - }); - const fontObserver = o.collectFonts - ? initFontObserver(o) - : () => { - // - }; + let styleSheetObserver = () => {}; + let adoptedStyleSheetObserver = () => {}; + let styleDeclarationObserver = () => {}; + let fontObserver = () => {}; + if (o.recordDOM) { + styleSheetObserver = initStyleSheetObserver(o, { win: currentWindow }); + adoptedStyleSheetObserver = initAdoptedStyleSheetObserver(o, o.doc); + styleDeclarationObserver = initStyleDeclarationObserver(o, { + win: currentWindow, + }); + if (o.collectFonts) { + fontObserver = initFontObserver(o); + } + } const selectionObserver = initSelectionObserver(o); const customElementObserver = initCustomElementObserver(o); @@ -1324,7 +1332,7 @@ export function initObservers( return callbackWrapper(() => { mutationBuffers.forEach((b) => b.reset()); - mutationObserver.disconnect(); + mutationObserver?.disconnect(); mousemoveHandler(); mouseInteractionHandler(); scrollHandler(); diff --git a/packages/rrweb/src/types.ts b/packages/rrweb/src/types.ts index 0eb4333199..940f58bd5f 100644 --- a/packages/rrweb/src/types.ts +++ b/packages/rrweb/src/types.ts @@ -71,6 +71,7 @@ export type recordOptions = { packFn?: PackFn; sampling?: SamplingStrategy; dataURLOptions?: DataURLOptions; + recordDOM?: boolean; recordCanvas?: boolean; recordCrossOriginIframes?: boolean; recordAfter?: 'DOMContentLoaded' | 'load'; @@ -120,6 +121,7 @@ export type observerParam = { customElementCb: customElementCallback; fontCb: fontCallback; sampling: SamplingStrategy; + recordDOM: boolean; recordCanvas: boolean; inlineImages: boolean; userTriggeredOnInput: boolean;