diff --git a/guide.md b/guide.md index 76f781d096..5560238522 100644 --- a/guide.md +++ b/guide.md @@ -158,6 +158,7 @@ The parameter of `rrweb.record` accepts the following options. | sampling | - | refer to the [storage optimization recipe](./docs/recipes/optimize-storage.md) | | recordCanvas | false | Whether to record the canvas element. Available options:
`false`,
`true` | | recordCrossOriginIframes | false | Whether to record cross origin iframes. rrweb has to be injected in each child iframe for this to work. Available options:
`false`,
`true` | +| recordAfter | 'load' | If the document is not ready, then the recorder will start recording after the specified event is fired. Available options: `DOMContentLoaded`, `load` | | inlineImages | false | whether to record the image content | | collectFonts | false | whether to collect fonts in the website | | userTriggeredOnInput | false | whether to add `userTriggered` on input events that indicates if this event was triggered directly by the user or not. [What is `userTriggered`?](https://github.com/rrweb-io/rrweb/pull/495) | diff --git a/guide.zh_CN.md b/guide.zh_CN.md index 1093dbb386..1272cf4772 100644 --- a/guide.zh_CN.md +++ b/guide.zh_CN.md @@ -154,6 +154,7 @@ setInterval(save, 10 * 1000); | dataURLOptions | {} | Canvas 图像快照的格式和质量,这个参数将传递给 OffscreenCanvas.convertToBlob(),使用这个参数能有效减小录制数据的大小 | | recordCanvas | false | 是否记录 canvas 内容, 可用选项:`false`, `true` | | recordCrossOriginIframes | false | 是否记录 cross origin iframes。 必须在每个子 iframe 中注入 rrweb 才能使其工作。 可用选项:`false`, `true` | +| recordAfter | 'load' | 如果 document 还没有加载完成,recorder 将会在指定的事件触发后开始录制。可用选项: `DOMContentLoaded`, `load` | | inlineImages | false | 是否将图片内容记内联录制 | | collectFonts | false | 是否记录页面中的字体文件 | | userTriggeredOnInput | false | [什么是 `userTriggered`](https://github.com/rrweb-io/rrweb/pull/495) | diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts index c28ec28a2c..6c79007690 100644 --- a/packages/rrweb/src/record/index.ts +++ b/packages/rrweb/src/record/index.ts @@ -76,6 +76,9 @@ function record( mousemoveWait, recordCanvas = false, recordCrossOriginIframes = false, + recordAfter = options.recordAfter === 'DOMContentLoaded' + ? options.recordAfter + : 'load', userTriggeredOnInput = false, collectFonts = false, inlineImages = false, @@ -408,16 +411,6 @@ function record( try { const handlers: listenerHandler[] = []; - handlers.push( - on('DOMContentLoaded', () => { - wrappedEmit( - wrapEvent({ - type: EventType.DomContentLoaded, - data: {}, - }), - ); - }), - ); const observe = (doc: Document) => { return initObservers( @@ -583,6 +576,17 @@ function record( ) { init(); } else { + handlers.push( + on('DOMContentLoaded', () => { + wrappedEmit( + wrapEvent({ + type: EventType.DomContentLoaded, + data: {}, + }), + ); + if (recordAfter === 'DOMContentLoaded') init(); + }), + ); handlers.push( on( 'load', @@ -593,7 +597,7 @@ function record( data: {}, }), ); - init(); + if (recordAfter === 'load') init(); }, window, ), diff --git a/packages/rrweb/src/types.ts b/packages/rrweb/src/types.ts index fb3a300b40..0ab8269854 100644 --- a/packages/rrweb/src/types.ts +++ b/packages/rrweb/src/types.ts @@ -61,6 +61,7 @@ export type recordOptions = { dataURLOptions?: DataURLOptions; recordCanvas?: boolean; recordCrossOriginIframes?: boolean; + recordAfter?: 'DOMContentLoaded' | 'load'; userTriggeredOnInput?: boolean; collectFonts?: boolean; inlineImages?: boolean; diff --git a/packages/rrweb/test/__snapshots__/integration.test.ts.snap b/packages/rrweb/test/__snapshots__/integration.test.ts.snap index 631580b1aa..ea037dff38 100644 --- a/packages/rrweb/test/__snapshots__/integration.test.ts.snap +++ b/packages/rrweb/test/__snapshots__/integration.test.ts.snap @@ -8408,6 +8408,89 @@ exports[`record integration tests should record DOM node movement 2 1`] = ` ]" `; +exports[`record integration tests should record after DOMContentLoaded event 1`] = ` +"[ + { + \\"type\\": 0, + \\"data\\": {} + }, + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 3 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 5 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 7 + } + ], + \\"id\\": 6 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\", + \\"id\\": 8 + } + ], + \\"id\\": 4 + } + ], + \\"id\\": 2 + } + ], + \\"compatMode\\": \\"BackCompat\\", + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 1, + \\"data\\": {} + } +]" +`; + exports[`record integration tests should record canvas mutations 1`] = ` "[ { diff --git a/packages/rrweb/test/integration.test.ts b/packages/rrweb/test/integration.test.ts index 516d3a289a..0371914fe6 100644 --- a/packages/rrweb/test/integration.test.ts +++ b/packages/rrweb/test/integration.test.ts @@ -984,4 +984,19 @@ describe('record integration tests', function (this: ISuite) { )) as eventWithTime[]; assertSnapshot(snapshots); }); + + it('should record after DOMContentLoaded event', async () => { + const page: puppeteer.Page = await browser.newPage(); + await page.goto('about:blank'); + await page.setContent( + getHtml.call(this, 'blank.html', { + recordAfter: 'DOMContentLoaded', + }), + ); + + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; + assertSnapshot(snapshots); + }); }); diff --git a/packages/rrweb/test/utils.ts b/packages/rrweb/test/utils.ts index 1d9b7a861e..0ece25e5d0 100644 --- a/packages/rrweb/test/utils.ts +++ b/packages/rrweb/test/utils.ts @@ -601,6 +601,7 @@ export function generateRecordSnippet(options: recordOptions) { userTriggeredOnInput: ${options.userTriggeredOnInput}, maskTextFn: ${options.maskTextFn}, recordCanvas: ${options.recordCanvas}, + recordAfter: '${options.recordAfter || 'load'}', inlineImages: ${options.inlineImages}, plugins: ${options.plugins} });