diff --git a/guide.md b/guide.md
index b95b4c2da3..5e59fe9c4d 100644
--- a/guide.md
+++ b/guide.md
@@ -142,11 +142,16 @@ The parameter of `rrweb.record` accepts the following options.
| checkoutEveryNms | - | take a full snapshot after every N ms
refer to the [checkout](#checkout) chapter |
| blockClass | 'rr-block' | Use a string or RegExp to configure which elements should be blocked, refer to the [privacy](#privacy) chapter |
| blockSelector | null | Use a string to configure which selector should be blocked, refer to the [privacy](#privacy) chapter |
+| unblockSelector | null | Use a string to configure which selector should be unblocked, refer to the [privacy](#privacy) chapter |
| ignoreClass | 'rr-ignore' | Use a string or RegExp to configure which elements should be ignored, refer to the [privacy](#privacy) chapter |
+| ignoreClass | null | Use a string to configure which selector should be ignored, refer to the [privacy](#privacy) chapter |
| maskTextClass | 'rr-mask' | Use a string or RegExp to configure which elements should be masked, refer to the [privacy](#privacy) chapter |
| maskTextSelector | null | Use a string to configure which selector should be masked, refer to the [privacy](#privacy) chapter |
+| unmaskTextSelector | null | Use a string to configure which selector should be unmasked, refer to the [privacy](#privacy) chapter |
| maskAllInputs | false | mask all input content as \* |
| maskInputOptions | { password: true } | mask some kinds of input \*
refer to the [list](https://github.com/rrweb-io/rrweb/blob/588164aa12f1d94576f89ae0210b98f6e971c895/packages/rrweb-snapshot/src/types.ts#L77-L95) |
+| maskInputSelector | null | Use a string to configure which selector should be masked, refer to the [privacy](#privacy) chapter |
+| unmaskInputSelector | null | Use a string to configure which selector should be unmasked, refer to the [privacy](#privacy) chapter |
| maskInputFn | - | customize mask input content recording logic |
| maskTextFn | - | customize mask text content recording logic |
| slimDOMOptions | {} | remove unnecessary parts of the DOM
refer to the [list](https://github.com/rrweb-io/rrweb/blob/588164aa12f1d94576f89ae0210b98f6e971c895/packages/rrweb-snapshot/src/types.ts#L97-L108) |
diff --git a/packages/rrweb-snapshot/src/snapshot.ts b/packages/rrweb-snapshot/src/snapshot.ts
index 7a7602284c..10948ed4f3 100644
--- a/packages/rrweb-snapshot/src/snapshot.ts
+++ b/packages/rrweb-snapshot/src/snapshot.ts
@@ -263,7 +263,12 @@ export function _isBlockedElement(
element: HTMLElement,
blockClass: string | RegExp,
blockSelector: string | null,
+ unblockSelector: string | null,
): boolean {
+ if (unblockSelector && element.matches(unblockSelector)) {
+ return false;
+ }
+
if (typeof blockClass === 'string') {
if (element.classList.contains(blockClass)) {
return true;
@@ -387,6 +392,7 @@ function serializeNode(
doc: Document;
blockClass: string | RegExp;
blockSelector: string | null;
+ unblockSelector: string | null;
maskTextClass: string | RegExp;
maskTextSelector: string | null;
unmaskTextSelector: string | null;
@@ -406,6 +412,7 @@ function serializeNode(
doc,
blockClass,
blockSelector,
+ unblockSelector,
maskTextClass,
maskTextSelector,
unmaskTextSelector,
@@ -455,6 +462,7 @@ function serializeNode(
n as HTMLElement,
blockClass,
blockSelector,
+ unblockSelector
);
const tagName = getValidTagName(n as HTMLElement);
let attributes: attributes = {};
@@ -809,6 +817,7 @@ export function serializeNodeWithId(
map: idNodeMap;
blockClass: string | RegExp;
blockSelector: string | null;
+ unblockSelector: string | null;
maskTextClass: string | RegExp;
maskTextSelector: string | null;
unmaskTextSelector: string | null;
@@ -835,6 +844,7 @@ export function serializeNodeWithId(
map,
blockClass,
blockSelector,
+ unblockSelector,
maskTextClass,
maskTextSelector,
unmaskTextSelector,
@@ -859,6 +869,7 @@ export function serializeNodeWithId(
doc,
blockClass,
blockSelector,
+ unblockSelector,
maskTextClass,
maskTextSelector,
unmaskTextSelector,
@@ -928,6 +939,7 @@ export function serializeNodeWithId(
map,
blockClass,
blockSelector,
+ unblockSelector,
maskTextClass,
maskTextSelector,
unmaskTextSelector,
@@ -984,6 +996,7 @@ export function serializeNodeWithId(
map,
blockClass,
blockSelector,
+ unblockSelector,
maskTextClass,
maskTextSelector,
unmaskTextSelector,
@@ -1022,6 +1035,7 @@ function snapshot(
options?: {
blockClass?: string | RegExp;
blockSelector?: string | null;
+ unblockSelector?: string | null;
maskTextClass?: string | RegExp;
maskTextSelector?: string | null;
unmaskTextSelector?: string | null;
@@ -1045,6 +1059,7 @@ function snapshot(
const {
blockClass = 'rr-block',
blockSelector = null,
+ unblockSelector = null,
maskTextClass = 'rr-mask',
maskTextSelector = null,
unmaskTextSelector = null,
@@ -1114,6 +1129,7 @@ function snapshot(
map: idNodeMap,
blockClass,
blockSelector,
+ unblockSelector,
maskTextClass,
maskTextSelector,
unmaskTextSelector,
diff --git a/packages/rrweb-snapshot/typings/snapshot.d.ts b/packages/rrweb-snapshot/typings/snapshot.d.ts
index 160f4b795f..a49fa7db40 100644
--- a/packages/rrweb-snapshot/typings/snapshot.d.ts
+++ b/packages/rrweb-snapshot/typings/snapshot.d.ts
@@ -3,13 +3,14 @@ export declare const IGNORED_NODE = -2;
export declare function absoluteToStylesheet(cssText: string | null, href: string): string;
export declare function absoluteToDoc(doc: Document, attributeValue: string): string;
export declare function transformAttribute(doc: Document, tagName: string, name: string, value: string): string;
-export declare function _isBlockedElement(element: HTMLElement, blockClass: string | RegExp, blockSelector: string | null): boolean;
+export declare function _isBlockedElement(element: HTMLElement, blockClass: string | RegExp, blockSelector: string | null, unblockSelector: string | null): boolean;
export declare function needMaskingText(node: Node | null, maskTextClass: string | RegExp, maskTextSelector: string | null, unmaskTextSelector: string | null): boolean;
export declare function serializeNodeWithId(n: Node | INode, options: {
doc: Document;
map: idNodeMap;
blockClass: string | RegExp;
blockSelector: string | null;
+ unblockSelector: string | null;
maskTextClass: string | RegExp;
maskTextSelector: string | null;
unmaskTextSelector: string | null;
@@ -33,6 +34,7 @@ export declare function serializeNodeWithId(n: Node | INode, options: {
declare function snapshot(n: Document, options?: {
blockClass?: string | RegExp;
blockSelector?: string | null;
+ unblockSelector?: string | null;
maskTextClass?: string | RegExp;
maskTextSelector?: string | null;
unmaskTextSelector?: string | null;
diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts
index 4505cac452..210b0c89cd 100644
--- a/packages/rrweb/src/record/index.ts
+++ b/packages/rrweb/src/record/index.ts
@@ -45,7 +45,9 @@ function record(
checkoutEveryNth,
blockClass = 'rr-block',
blockSelector = null,
+ unblockSelector = null,
ignoreClass = 'rr-ignore',
+ ignoreSelector =null,
maskTextClass = 'rr-mask',
maskTextSelector = null,
maskInputSelector = null,
@@ -225,6 +227,7 @@ function record(
bypassOptions: {
blockClass,
blockSelector,
+ unblockSelector,
maskTextClass,
maskTextSelector,
unmaskTextSelector,
@@ -261,6 +264,7 @@ function record(
const [node, idNodeMap] = snapshot(document, {
blockClass,
blockSelector,
+ unblockSelector,
maskTextClass,
maskTextSelector,
unmaskTextSelector,
@@ -422,6 +426,7 @@ function record(
),
blockClass,
ignoreClass,
+ ignoreSelector,
maskTextClass,
maskTextSelector,
unmaskTextSelector,
@@ -438,6 +443,7 @@ function record(
maskInputFn,
maskTextFn,
blockSelector,
+ unblockSelector,
slimDOMOptions,
mirror,
iframeManager,
diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts
index 244ffd4e00..bcfea6ca6e 100644
--- a/packages/rrweb/src/record/mutation.ts
+++ b/packages/rrweb/src/record/mutation.ts
@@ -159,6 +159,7 @@ export default class MutationBuffer {
private mutationCb: observerParam['mutationCb'];
private blockClass: observerParam['blockClass'];
private blockSelector: observerParam['blockSelector'];
+ private unblockSelector: observerParam['unblockSelector'];
private maskTextClass: observerParam['maskTextClass'];
private maskTextSelector: observerParam['maskTextSelector'];
private unmaskTextSelector: observerParam['unmaskTextSelector'];
@@ -182,6 +183,7 @@ export default class MutationBuffer {
'mutationCb',
'blockClass',
'blockSelector',
+ 'unblockSelector',
'maskTextClass',
'maskTextSelector',
'unmaskTextSelector',
@@ -294,6 +296,7 @@ export default class MutationBuffer {
map: this.mirror.map,
blockClass: this.blockClass,
blockSelector: this.blockSelector,
+ unblockSelector: this.unblockSelector,
maskTextClass: this.maskTextClass,
maskTextSelector: this.maskTextSelector,
unmaskTextSelector: this.unmaskTextSelector,
diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts
index 69606c065e..ad2e0b24e2 100644
--- a/packages/rrweb/src/record/observer.ts
+++ b/packages/rrweb/src/record/observer.ts
@@ -326,6 +326,7 @@ function initInputObserver({
mirror,
blockClass,
ignoreClass,
+ ignoreSelector,
maskInputSelector,
unmaskInputSelector,
maskInputOptions,
@@ -351,7 +352,7 @@ function initInputObserver({
return;
}
const type: string | undefined = (target as HTMLInputElement).type;
- if ((target as HTMLElement).classList.contains(ignoreClass)) {
+ if ((target as HTMLElement).classList.contains(ignoreClass) || (ignoreSelector && (target as HTMLElement).matches(ignoreSelector))) {
return;
}
let text = (target as HTMLInputElement).value;
diff --git a/packages/rrweb/src/types.ts b/packages/rrweb/src/types.ts
index 9982c44958..43cec3acff 100644
--- a/packages/rrweb/src/types.ts
+++ b/packages/rrweb/src/types.ts
@@ -216,7 +216,9 @@ export type recordOptions = {
checkoutEveryNms?: number;
blockClass?: blockClass;
blockSelector?: string;
+ unblockSelector?: string;
ignoreClass?: string;
+ ignoreSelector?: string;
maskTextClass?: maskTextClass;
maskTextSelector?: string;
maskAllInputs?: boolean;
@@ -251,7 +253,9 @@ export type observerParam = {
mediaInteractionCb: mediaInteractionCallback;
blockClass: blockClass;
blockSelector: string | null;
+ unblockSelector: string | null;
ignoreClass: string;
+ ignoreSelector: string | null;
maskTextClass: maskTextClass;
maskTextSelector: string | null;
unmaskTextSelector: string | null;
@@ -288,6 +292,7 @@ export type MutationBufferParam = Pick<
| 'mutationCb'
| 'blockClass'
| 'blockSelector'
+ | 'unblockSelector'
| 'maskTextClass'
| 'maskTextSelector'
| 'unmaskTextSelector'
diff --git a/packages/rrweb/typings/record/mutation.d.ts b/packages/rrweb/typings/record/mutation.d.ts
index 485cba0e4b..444ea07c71 100644
--- a/packages/rrweb/typings/record/mutation.d.ts
+++ b/packages/rrweb/typings/record/mutation.d.ts
@@ -13,6 +13,7 @@ export default class MutationBuffer {
private mutationCb;
private blockClass;
private blockSelector;
+ private unblockSelector;
private maskTextClass;
private maskTextSelector;
private unmaskTextSelector;
diff --git a/packages/rrweb/typings/types.d.ts b/packages/rrweb/typings/types.d.ts
index a779ae3f81..bf529fa294 100644
--- a/packages/rrweb/typings/types.d.ts
+++ b/packages/rrweb/typings/types.d.ts
@@ -137,7 +137,9 @@ export declare type recordOptions = {
checkoutEveryNms?: number;
blockClass?: blockClass;
blockSelector?: string;
+ unblockSelector?: string;
ignoreClass?: string;
+ ignoreSelector?: string;
maskTextClass?: maskTextClass;
maskTextSelector?: string;
maskAllInputs?: boolean;
@@ -170,7 +172,9 @@ export declare type observerParam = {
mediaInteractionCb: mediaInteractionCallback;
blockClass: blockClass;
blockSelector: string | null;
+ unblockSelector: string | null;
ignoreClass: string;
+ ignoreSelector: string | null;
maskTextClass: maskTextClass;
maskTextSelector: string | null;
unmaskTextSelector: string | null;
@@ -201,7 +205,7 @@ export declare type observerParam = {
options: unknown;
}>;
};
-export declare type MutationBufferParam = Pick;
+export declare type MutationBufferParam = Pick;
export declare type hooksParam = {
mutation?: mutationCallBack;
mousemove?: mousemoveCallBack;