rrweb icon indicating copy to clipboard operation
rrweb copied to clipboard

Cannot record web Component css

Open GoncaloDuarte98 opened this issue 3 years ago • 8 comments

I cannot record the css inside a lit Element web Component. I can see the component but no style which breaks the recording

GoncaloDuarte98 avatar Aug 16 '21 08:08 GoncaloDuarte98

@GoncaloDuarte98 Could you provide an example code for debugging?

Yuyz0112 avatar Aug 16 '21 08:08 Yuyz0112

This is the web component: image And this is the recording using rrweb: image

GoncaloDuarte98 avatar Aug 16 '21 08:08 GoncaloDuarte98

[ { "type": 0, "data": {}, "timestamp": 1629103007609, "delay": -5.300048828125 }, { "type": 1, "data": {}, "timestamp": 1629103007637, "delay": 22.699951171875 }, { "type": 4, "data": { "href": "http://localhost:8000/", "width": 1041, "height": 889 }, "timestamp": 1629103007638, "delay": 23.699951171875 }, { "type": 2, "data": { "node": { "type": 0, "childNodes": [ { "type": 1, "name": "html", "publicId": "", "systemId": "", "id": 2 }, { "type": 2, "tagName": "html", "attributes": { "lang": "en-GB" }, "childNodes": [ { "type": 2, "tagName": "head", "attributes": {}, "childNodes": [ { "type": 3, "textContent": "\n ", "id": 5 }, { "type": 2, "tagName": "meta", "attributes": { "charset": "utf-8" }, "childNodes": [], "id": 6 }, { "type": 3, "textContent": "\n ", "id": 7 }, { "type": 2, "tagName": "style", "attributes": {}, "childNodes": [ { "type": 3, "textContent": "\n body {\n background: #fafafa;\n }\n ", "isStyle": true, "id": 9 } ], "id": 8 }, { "type": 3, "textContent": "\n", "id": 10 }, { "type": 2, "tagName": "link", "attributes": { "rel": "stylesheet", "href": "https://cdn.jsdelivr.net/npm/rrweb@latest/dist/rrweb.min.css" }, "childNodes": [], "id": 11 }, { "type": 3, "textContent": "\n ", "id": 12 }, { "type": 2, "tagName": "script", "attributes": { "src": "https://cdn.jsdelivr.net/npm/rrweb@latest/dist/rrweb.min.js" }, "childNodes": [], "id": 13 }, { "type": 3, "textContent": "\n ", "id": 14 } ], "id": 4 }, { "type": 3, "textContent": "\n\n ", "id": 15 }, { "type": 2, "tagName": "body", "attributes": {}, "childNodes": [ { "type": 3, "textContent": "\n ", "id": 17 }, { "type": 2, "tagName": "div", "attributes": { "id": "demo" }, "childNodes": [], "id": 18 }, { "type": 3, "textContent": "\n ", "id": 19 }, { "type": 2, "tagName": "h1", "attributes": {}, "childNodes": [ { "type": 3, "textContent": "Teste", "id": 21 } ], "id": 20 }, { "type": 3, "textContent": "\n ", "id": 22 }, { "type": 2, "tagName": "my-component", "attributes": {}, "childNodes": [ { "type": 5, "textContent": "", "id": 24, "isShadow": true }, { "type": 3, "textContent": "\n ", "id": 25, "isShadow": true }, { "type": 2, "tagName": "button", "attributes": {}, "childNodes": [ { "type": 5, "textContent": "", "id": 27 }, { "type": 3, "textContent": "Copia", "id": 28 }, { "type": 5, "textContent": "", "id": 29 } ], "id": 26, "isShadow": true }, { "type": 3, "textContent": "\n ", "id": 30, "isShadow": true }, { "type": 2, "tagName": "p", "attributes": { "style": "visibility: hidden" }, "childNodes": [ { "type": 3, "textContent": "\n Copiado!\n ", "id": 32 } ], "id": 31, "isShadow": true }, { "type": 3, "textContent": "\n ", "id": 33, "isShadow": true }, { "type": 5, "textContent": "", "id": 34, "isShadow": true } ], "id": 23, "isShadowHost": true }, { "type": 3, "textContent": "\n ", "id": 35 }, { "type": 2, "tagName": "button", "attributes": { "id": "play" }, "childNodes": [ { "type": 3, "textContent": "Log events", "id": 37 } ], "id": 36 }, { "type": 3, "textContent": "\n ", "id": 38 }, { "type": 2, "tagName": "script", "attributes": {}, "childNodes": [ { "type": 3, "textContent": "SCRIPT_PLACEHOLDER", "id": 40 } ], "id": 39 }, { "type": 3, "textContent": "\n\n ", "id": 41 }, { "type": 2, "tagName": "script", "attributes": { "type": "module", "src": "http://localhost:8000/build/index.js" }, "childNodes": [], "id": 42 }, { "type": 3, "textContent": "\n ", "id": 43 }, { "type": 5, "textContent": " injected by web-dev-server ", "id": 44 }, { "type": 3, "textContent": "\n", "id": 45 }, { "type": 2, "tagName": "script", "attributes": { "type": "module", "src": "http://localhost:8000/__web-dev-server__web-socket.js" }, "childNodes": [], "id": 46 }, { "type": 3, "textContent": "\n\n", "id": 47 } ], "id": 16 } ], "id": 3 } ], "id": 1 }, "initialOffset": { "left": 0, "top": 0 } }, "timestamp": 1629103007642, "delay": 27.699951171875 }, { "type": 3, "data": { "source": 2, "type": 7, "id": 36, "x": 41.726104736328125, "y": 187.15872192382812 }, "timestamp": 1629103011359, "delay": 3744.699951171875 }, { "type": 3, "data": { "source": 2, "type": 9, "id": 36, "x": 41.726104736328125, "y": 187.15872192382812 }, "timestamp": 1629103011464, "delay": 3849.699951171875 }, { "type": 3, "data": { "source": 1, "positions": [ { "x": 42, "y": 181, "id": 36, "timeOffset": 0 } ] }, "timestamp": 1629103011466, "delay": 3851.699951171875 }, { "type": 3, "data": { "source": 2, "type": 1, "id": 36, "x": 42, "y": 181 }, "timestamp": 1629103011466, "delay": 3851.699951171875 }, { "type": 3, "data": { "source": 2, "type": 5, "id": 36 }, "timestamp": 1629103011466, "delay": 3851.699951171875 }, { "type": 3, "data": { "source": 2, "type": 0, "id": 36, "x": 42, "y": 181 }, "timestamp": 1629103011467, "delay": 3852.699951171875 }, { "type": 3, "data": { "source": 2, "type": 2, "id": 36, "x": 42, "y": 181 }, "timestamp": 1629103011467, "delay": 3852.699951171875 }, { "type": 3, "data": { "source": 0, "texts": [], "attributes": [], "removes": [], "adds": [ { "parentId": 16, "nextId": null, "node": { "type": 2, "tagName": "div", "attributes": { "class": "replayer-wrapper" }, "childNodes": [], "id": 48 } }, { "parentId": 48, "nextId": null, "node": { "type": 2, "tagName": "iframe", "attributes": { "sandbox": "allow-same-origin", "scrolling": "no", "style": "display: none; pointer-events: none;" }, "childNodes": [], "id": 49 } }, { "parentId": 48, "nextId": 49, "node": { "type": 2, "tagName": "canvas", "attributes": { "class": "replayer-mouse-tail", "style": "display: inherit;" }, "childNodes": [], "id": 50 } }, { "parentId": 48, "nextId": 50, "node": { "type": 2, "tagName": "div", "attributes": { "class": "replayer-mouse" }, "childNodes": [], "id": 51 } } ] }, "timestamp": 1629103011483 }, { "type": 3, "data": { "source": 0, "texts": [], "attributes": [ { "id": 49, "attributes": { "style": { "display": "inherit" }, "width": "1041", "height": "889" } }, { "id": 50, "attributes": { "width": "1041", "height": "889" } } ], "removes": [], "adds": [] }, "timestamp": 1629103011487 }, { "type": 3, "data": { "source": 0, "adds": [ { "parentId": 49, "nextId": null, "node": { "type": 0, "childNodes": [ { "type": 2, "tagName": "html", "attributes": {}, "childNodes": [ { "type": 2, "tagName": "head", "attributes": {}, "childNodes": [], "rootId": 52, "id": 54 }, { "type": 2, "tagName": "body", "attributes": {}, "childNodes": [], "rootId": 52, "id": 55 } ], "rootId": 52, "id": 53 } ], "id": 52 } } ], "removes": [], "texts": [], "attributes": [], "isAttachIframe": true }, "timestamp": 1629103011488 }, { "type": 3, "data": { "source": 0, "texts": [], "attributes": [], "removes": [], "adds": [ { "parentId": 1, "nextId": 3, "node": { "type": 1, "name": "html", "publicId": "", "systemId": "", "id": 2 } }, { "parentId": 1, "nextId": null, "node": { "type": 2, "tagName": "html", "attributes": { "lang": "en-GB" }, "childNodes": [], "id": 3 } }, { "parentId": 3, "nextId": 15, "node": { "type": 2, "tagName": "head", "attributes": {}, "childNodes": [], "id": 4 } }, { "parentId": 4, "nextId": 6, "node": { "type": 3, "textContent": "\n ", "id": 5 } }, { "parentId": 4, "nextId": 7, "node": { "type": 2, "tagName": "meta", "attributes": { "charset": "utf-8" }, "childNodes": [], "id": 6 } }, { "parentId": 4, "nextId": 8, "node": { "type": 3, "textContent": "\n ", "id": 7 } }, { "parentId": 4, "nextId": 10, "node": { "type": 2, "tagName": "style", "attributes": {}, "childNodes": [], "id": 8 } }, { "parentId": 8, "nextId": null, "node": { "type": 3, "textContent": "\n body {\n background: #fafafa;\n }\n ", "isStyle": true, "id": 9 } }, { "parentId": 4, "nextId": 11, "node": { "type": 3, "textContent": "\n", "id": 10 } }, { "parentId": 4, "nextId": 12, "node": { "type": 2, "tagName": "link", "attributes": { "rel": "stylesheet", "href": "https://cdn.jsdelivr.net/npm/rrweb@latest/dist/rrweb.min.css" }, "childNodes": [], "id": 11 } }, { "parentId": 4, "nextId": 13, "node": { "type": 3, "textContent": "\n ", "id": 12 } }, { "parentId": 4, "nextId": 14, "node": { "type": 2, "tagName": "noscript", "attributes": { "src": "https://cdn.jsdelivr.net/npm/rrweb@latest/dist/rrweb.min.js" }, "childNodes": [], "id": 13 } }, { "parentId": 4, "nextId": null, "node": { "type": 3, "textContent": "\n ", "id": 14 } }, { "parentId": 3, "nextId": 16, "node": { "type": 3, "textContent": "\n\n ", "id": 15 } }, { "parentId": 3, "nextId": null, "node": { "type": 2, "tagName": "body", "attributes": {}, "childNodes": [], "id": 16 } }, { "parentId": 16, "nextId": 18, "node": { "type": 3, "textContent": "\n ", "id": 17 } }, { "parentId": 16, "nextId": 19, "node": { "type": 2, "tagName": "div", "attributes": { "id": "demo" }, "childNodes": [], "id": 18 } }, { "parentId": 16, "nextId": 20, "node": { "type": 3, "textContent": "\n ", "id": 19 } }, { "parentId": 16, "nextId": 22, "node": { "type": 2, "tagName": "h1", "attributes": {}, "childNodes": [], "id": 20 } }, { "parentId": 20, "nextId": null, "node": { "type": 3, "textContent": "Teste", "id": 21 } }, { "parentId": 16, "nextId": 23, "node": { "type": 3, "textContent": "\n ", "id": 22 } }, { "parentId": 16, "nextId": 35, "node": { "type": 2, "tagName": "my-component", "attributes": {}, "childNodes": [], "id": 23 } }, { "parentId": 16, "nextId": 36, "node": { "type": 3, "textContent": "\n ", "id": 35 } }, { "parentId": 16, "nextId": 38, "node": { "type": 2, "tagName": "button", "attributes": { "id": "play" }, "childNodes": [], "id": 36 } }, { "parentId": 36, "nextId": null, "node": { "type": 3, "textContent": "Log events", "id": 37 } }, { "parentId": 16, "nextId": 39, "node": { "type": 3, "textContent": "\n ", "id": 38 } }, { "parentId": 16, "nextId": 41, "node": { "type": 2, "tagName": "noscript", "attributes": {}, "childNodes": [], "id": 39 } }, { "parentId": 39, "nextId": null, "node": { "type": 3, "textContent": "SCRIPT_PLACEHOLDER", "id": 40 } }, { "parentId": 16, "nextId": 42, "node": { "type": 3, "textContent": "\n\n ", "id": 41 } }, { "parentId": 16, "nextId": 43, "node": { "type": 2, "tagName": "noscript", "attributes": { "type": "module", "src": "http://localhost:8000/build/index.js" }, "childNodes": [], "id": 42 } }, { "parentId": 16, "nextId": 44, "node": { "type": 3, "textContent": "\n ", "id": 43 } }, { "parentId": 16, "nextId": 45, "node": { "type": 5, "textContent": " injected by web-dev-server ", "id": 44 } }, { "parentId": 16, "nextId": 46, "node": { "type": 3, "textContent": "\n", "id": 45 } }, { "parentId": 16, "nextId": 47, "node": { "type": 2, "tagName": "noscript", "attributes": { "type": "module", "src": "http://localhost:8000/__web-dev-server__web-socket.js" }, "childNodes": [], "id": 46 } }, { "parentId": 16, "nextId": null, "node": { "type": 3, "textContent": "\n\n", "id": 47 } }, { "parentId": 3, "nextId": 4, "node": { "type": 2, "tagName": "style", "attributes": { "_cssText": ".rr-block { background: rgb(204, 204, 204); }noscript { display: none !important; }html.rrweb-paused * { animation-play-state: paused !important; }" }, "childNodes": [], "id": 56 } } ] }, "timestamp": 1629103011496 }, { "type": 3, "data": { "source": 2, "type": 6, "id": 36 }, "timestamp": 1629103014415 }, { "type": 3, "data": { "source": 0, "texts": [], "attributes": [ { "id": 51, "attributes": { "style": { "left": "41.7261px", "top": "187.159px" }, "class": "replayer-mouse active" } } ], "removes": [], "adds": [] }, "timestamp": 1629103015251 }, { "type": 3, "data": { "source": 0, "texts": [], "attributes": [ { "id": 36, "attributes": { "class": ":hover" } }, { "id": 16, "attributes": { "class": ":hover" } }, { "id": 3, "attributes": { "class": ":hover" } } ], "removes": [], "adds": [] }, "timestamp": 1629103015251 }, { "type": 3, "data": { "source": 0, "texts": [], "attributes": [ { "id": 51, "attributes": { "class": "replayer-mouse active", "style": { "left": "42px", "top": "181px" } } } ], "removes": [], "adds": [] }, "timestamp": 1629103015358 }, { "type": 3, "data": { "source": 0, "texts": [], "attributes": [ { "id": 36, "attributes": { "class": ":hover" } }, { "id": 16, "attributes": { "class": ":hover" } }, { "id": 3, "attributes": { "class": ":hover" } } ], "removes": [], "adds": [] }, "timestamp": 1629103015358 } ]

GoncaloDuarte98 avatar Aug 16 '21 08:08 GoncaloDuarte98

@GoncaloDuarte98 Could you provide the source code of this web component?

Yuyz0112 avatar Oct 01 '21 07:10 Yuyz0112

@Yuyz0112 you can try these examples lit playground, starter kit and material wc. It doesn't work with any web component using lit

GoncaloDuarte98 avatar Oct 04 '21 08:10 GoncaloDuarte98

Proposal: record adoptedStyleSheets

Investigate conclusion

After looking into the demos provided by @GoncaloDuarte98, I found lit is using the Construct Stylesheet to style shadow DOM when it's supported(e.g, in Chrome).

Since the constructed stylesheet is in the DOM tree within link, style, or other DOM nodes, the current recording implementation will not record them.

Inline the adoptedStyleSheets into events

A simple solution is to access the adoptedStyleSheets property via JS API and inline the stylesheet object into a rrweb event, then we can replay it by creating a stylesheet and assign it to the corresponding document's adoptedStyleSheets property.

But looking into the real-world use case of Construct Stylesheet, this way may cause a lot of duplicate inline styles and bump the size of a session.

Construct Stylesheet is created for reusing

In this blog, we can see people used to reuse Construct Stylesheet across shadow DOM.

For example, this demo has the same stylesheet object in every button DOM(every button is a shadow-DOM based web component). If we inline every adoptedStyleSheets, there will be tens of duplicate stylesheets on this page.

And things can be much worse if an application has a big shared stylesheet in every component.

Optimization

A way to optimize this is by adding unique ids to Construct Stylesheet during replay and only inline the content of a stylesheet object once.

When iterating the DOM tree, if a shadow document root has adoptedStyleSheets, it will be recorded as the ids of Construct Stylesheet.

Yuyz0112 avatar Oct 07 '21 07:10 Yuyz0112

Any update on adding this feature? We have encountered this problem on some sites as well, where the video replay is missing styling due to a constructed stylesheet.

dbseel avatar May 17 '22 17:05 dbseel

Ran into this issue with shadow DOM recordings as well, @Yuyz0112's plan looks like a good one. It seems like one of the few remaining shortcomings of the current shadow dom record mechanism. Is there any current plan to implement this? (@Mark-Fenng). Thanks!

lele0108 avatar Jul 15 '22 20:07 lele0108

However, the compatibility of adoptedStyleSheets is relatively poor. Is there any other solution for low-version browsers (such as ios 12)? image

xujiujiu avatar Jun 05 '23 08:06 xujiujiu