rrweb
rrweb copied to clipboard
Cannot record web Component css
I cannot record the css inside a lit Element web Component. I can see the component but no style which breaks the recording
@GoncaloDuarte98 Could you provide an example code for debugging?
This is the web component:
And this is the recording using rrweb:
[ { "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 Could you provide the source code of this web component?
@Yuyz0112 you can try these examples lit playground, starter kit and material wc. It doesn't work with any web component using lit
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
.
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.
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!
However, the compatibility of adoptedStyleSheets is relatively poor. Is there any other solution for low-version browsers (such as ios 12)?