From 1312dbda8faffbdf88296bad7dd6c9f9b66d15e1 Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Mon, 24 Jan 2022 01:03:02 +1100 Subject: [PATCH 1/2] feat: add observer for 'selected' setter of HTMLOptionElement and try to fix issue #746 --- packages/rrweb-snapshot/src/snapshot.ts | 2 +- packages/rrweb/src/record/observer.ts | 5 ++++- packages/rrweb/test/__snapshots__/integration.test.ts.snap | 3 +-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/rrweb-snapshot/src/snapshot.ts b/packages/rrweb-snapshot/src/snapshot.ts index 91f6400abc..bd544fa102 100644 --- a/packages/rrweb-snapshot/src/snapshot.ts +++ b/packages/rrweb-snapshot/src/snapshot.ts @@ -491,7 +491,7 @@ function serializeNode( } } if (tagName === 'option') { - if ((n as HTMLOptionElement).selected) { + if ((n as HTMLOptionElement).selected && !maskInputOptions['select']) { attributes.selected = true; } else { // ignore the html attribute (which corresponds to DOM (n as HTMLOptionElement).defaultSelected) diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts index b01ef49e1f..bd348cc205 100644 --- a/packages/rrweb/src/record/observer.ts +++ b/packages/rrweb/src/record/observer.ts @@ -371,8 +371,10 @@ function initInputObserver( userTriggeredOnInput: boolean, ): listenerHandler { function eventHandler(event: Event) { - const target = getEventTarget(event); + let target = getEventTarget(event); const userTriggered = event.isTrusted; + if (target && (target as Element).tagName === 'OPTION') + target = (target as Element).parentElement; if ( !target || !(target as Element).tagName || @@ -463,6 +465,7 @@ function initInputObserver( [HTMLTextAreaElement.prototype, 'value'], // Some UI library use selectedIndex to set select value [HTMLSelectElement.prototype, 'selectedIndex'], + [HTMLOptionElement.prototype, 'selected'], ]; if (propertyDescriptor && propertyDescriptor.set) { handlers.push( diff --git a/packages/rrweb/test/__snapshots__/integration.test.ts.snap b/packages/rrweb/test/__snapshots__/integration.test.ts.snap index 75440a372e..983788b7a2 100644 --- a/packages/rrweb/test/__snapshots__/integration.test.ts.snap +++ b/packages/rrweb/test/__snapshots__/integration.test.ts.snap @@ -6066,8 +6066,7 @@ exports[`record integration tests should not record input values if maskAllInput \\"type\\": 2, \\"tagName\\": \\"option\\", \\"attributes\\": { - \\"value\\": \\"1\\", - \\"selected\\": true + \\"value\\": \\"1\\" }, \\"childNodes\\": [ { From f5f9ecd687d5569d0eefd56ef2bf36d18e11fe5f Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Tue, 25 Jan 2022 16:29:41 +1100 Subject: [PATCH 2/2] style: add description of mechanism --- packages/rrweb/src/record/observer.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts index bd348cc205..752ac9478e 100644 --- a/packages/rrweb/src/record/observer.ts +++ b/packages/rrweb/src/record/observer.ts @@ -373,6 +373,10 @@ function initInputObserver( function eventHandler(event: Event) { let target = getEventTarget(event); const userTriggered = event.isTrusted; + /** + * If a site changes the value 'selected' of an option element, the value of its parent element, usually a select element, will be changed as well. + * We can treat this change as a value change of the select element the current target belongs to. + */ if (target && (target as Element).tagName === 'OPTION') target = (target as Element).parentElement; if (