rrweb-io / rrweb Goto Github PK
View Code? Open in Web Editor NEWrecord and replay the web
Home Page: https://www.rrweb.io/
License: MIT License
record and replay the web
Home Page: https://www.rrweb.io/
License: MIT License
Hi, using tampermonkey I inserted recording code to all websites visited by me through my browser. The recording code sends event JSONs to a server which stores them in a file. Each event in a single row. I have 294 events recorded. I want to further replay the record but it doesn't work. The player is there and the duration of the record is 21:59 so it seems that it resolves the data, however the cursor doesn't move and the player screen is blank. When I removed first events from the file until the events of type 4 and then 2, leaving 199, the duration is only 2:11, cursors moves first half of second and then stops, screen is also blank.
I get an error TypeError: n is null
at e.previousId && (o = E.getNode(e.previousId)), e.nextId && (s = E.getNode(e.nextId)), -1 !== e.previousId && -1 !== e.nextId ? (o && o.nextSibling && o.nextSibling.parentNode ? n.insertBefore(t, o.nextSibling) : s && s.parentNode ? n.insertBefore(t, s) : n.appendChild(t), (e.previousId || e.nextId) && i.resolveMissingNode(r, n, t, e)) : r[e.node.id] = {
How can I make this work or where can be a problem? I'm attaching replay code.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Player</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/rrweb-player@latest/dist/style.css">
<script src="https://cdn.jsdelivr.net/npm/rrweb-player@latest/dist/index.js"></script>
<script>
'use strict';
var FR = new FileReader();
var textToArr = res => res.split(/\n/).reduce((arr, row) => {
try {
var e = JSON.parse(row);
return [...arr, e];
} catch(e){
return arr;
}
}, []);
FR.onload = function(e) {
var events = textToArr(e.target.result)
new rrwebPlayer({
target: document.body,
data: { events },
});
};
fetch('./output')
.then(res => res.blob())
.then(res => FR.readAsText(res));
</script>
</head>
<body>
</body>
</html>
I captured events on an react app (using the antd
component library) and if I change an input text, I get the error during playback (using cdn rr-webplayer@latest
with basically just the example code from the guide).
Uncaught TypeError: Cannot read property 'blur' of null
at e.applyIncremental (index.js:1)
at i (index.js:1)
at Object.doAction (index.js:1)
at r (index.js:1)
It stops the playback, but if I click play again, it will continue (although after I can't replay it again).
The code base is for a private client so I can't share for replay events (will try to reproduce with sharable code alter)... Just wondering if you have any ideas why this might happen?
Running some of the npm scripts, including npm run repl
, currently fails in a Windows environment.
This appears to be related to the fact that most Windows command prompts don't deal well with envs set using the FOO=BAR myCommand
syntax.
Running npm run repl
in PowerShell under Windows 10:
PS C:\git\rrweb> npm run repl
> rrweb@0.7.2 repl C:\git\rrweb
> npm run bundle:browser && TS_NODE_CACHE=false TS_NODE_FILES=true ts-node scripts/repl.ts
> rrweb@0.7.2 bundle:browser C:\git\rrweb
> BROWSER_ONLY=true rollup --config
'BROWSER_ONLY' is not recognized as an internal or external command,
operable program or batch file.
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! rrweb@0.7.2 bundle:browser: `BROWSER_ONLY=true rollup --config`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the rrweb@0.7.2 bundle:browser script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm ERR! A complete log of this run can be found in:
npm ERR! C:\Users\STA92668\AppData\Roaming\npm-cache\_logs\2018-12-28T19_06_11_024Z-debug.log
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! rrweb@0.7.2 repl: `npm run bundle:browser && TS_NODE_CACHE=false TS_NODE_FILES=true ts-node scripts/repl.ts`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the rrweb@0.7.2 repl script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm ERR! A complete log of this run can be found in:
npm ERR! C:\Users\STA92668\AppData\Roaming\npm-cache\_logs\2018-12-28T19_06_11_099Z-debug.log
I suggest introducing the cross-env
command line tool, which abstracts away the environment specifics around setting envs.
This should work under any modern environment.
For example:
{
"scripts": {
"test": "npm run bundle:browser && cross-env 'TS_NODE_CACHE=false TS_NODE_FILES=true' mocha -r ts-node/register test/**/*.test.ts",
"repl": "npm run bundle:browser && cross-env 'TS_NODE_CACHE=false TS_NODE_FILES=true' ts-node scripts/repl.ts",
"bundle:browser": "cross-env BROWSER_ONLY=true rollup --config",
"bundle": "rollup --config"
}
}
I am using rrweb.record from this CDN
<script src="https://cdn.jsdelivr.net/npm/rrweb@latest/dist/rrweb.min.js"></script>
This is how I record my events
let events = [];
rrweb.record({
emit(event) {
events.push(event);
}
});
I call this function every 10 sec
function save() {
console.log(events)
events = [];
}
setInterval(save, 10 * 1000);
The first record is recorded correctly and displays the dom elements
other wise all the other events are corrupted
and data is like this data: {source: 2, type: 0, id: 40, x: 286, y: 126}
when I try to play it, it only displays a courser position
this is the whole code page
<html>
<head>
<!-- <script src="https://cdn.jsdelivr.net/npm/rrweb@latest/dist/record/rrweb-record.min.js"></script> -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/rrweb@latest/dist/rrweb.min.css" />
<script src="https://cdn.jsdelivr.net/npm/rrweb@latest/dist/rrweb.min.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/rrweb-player@latest/dist/index.js"></script> -->
<!--
<script src="https://cdn.jsdelivr.net/npm/rrweb@latest/dist/record/rrweb-record.min.js"></script> -->
</head>
<script>
let eventsArray = [];
let events = [];
rrweb.record({
emit(event) {
events.push(event);
},
});
function save() {
eventsArray.push(events)
if (eventsArray.length == 1) {
// const replayer = new rrweb.Replayer(events);
// replayer.play();
}
console.log(events)
events = [];
}
setInterval(save, 10 * 1000);
</script>
<body>
<ul>
<li>Coffee</li>
<li>Tea</li>
<li>Milk</li>
</ul>
</body>
</html>
Any plans on adding heatmap functionality like Hotjar?
This might be a trivial question but I'm trying to require the rrweb
package using:
const rrweb = require('rrweb');
but I'm getting the following error:
ReferenceError: HTMLInputElement is not defined
at Object.<anonymous> (/path/to/node_modules/rrweb/lib/rrweb.js:773:6)
at Module._compile (internal/modules/cjs/loader.js:689:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
at Module.load (internal/modules/cjs/loader.js:599:32)
at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
at Function.Module._load (internal/modules/cjs/loader.js:530:3)
at Module.require (internal/modules/cjs/loader.js:637:17)
at require (internal/modules/cjs/helpers.js:20:18)
Is this not how we should be importing the node package? It seems like this is expecting to be executed in a browser where HTMLInputElement
is defined.
hover
is a native DOM event which we cannot trigger by JS. So some styles relative to the :hover
selector will be broken when replay.
We can use an alternative approach to simulate the hover event with the following steps:
\:hover
class name selector to the CSS rules which contains the :hover
selector during rebuilding.\:hover
class name when applying mousemove event.can this be used for pure screenshots? not video recording sessions ? I couldn't find a function in the to do that, im sure it could be hacked on.
hi, I want to record a short video for my dynamic chart coding by echarts
Could you give me some advice whether can I use rrweb. thx
Is there a way to make the replayer module responsive? Right now, it expands to a set size no matter what the size of the parent container.
Is it possible to allow some kind of configuration with the player to load it as paused. When it currently loads it automatically starts playing
I think GitHub is better for tracking feature requests and reproducible bugs. But as a new project, rrweb's users may have many ideas in early stage or meeting some small bugs which need help.
So how about a chatting room, like:
Of course, we will only choose one so any suggestion is welcome.
I have already described something we would like to add into rrweb in the README, but all of that is about the internal implementations. So I would like to share some high-level opinions about rrweb's future in this issue, and of course, any feedback is appreciated.
First I would not like to add too many built-in functions about analytics, maybe most of the feature requests can be done via a custom event
option.
But some something like the heatmap may be added into rrweb-player soon because the currently recorded data is already enough to implement it.
Besides user analytics, I'm more keen on the scenarios like combine rrweb with some E2E test frameworks and use rrweb to implement some demo tooling.
For example, we can use rrweb to record E2E test cases in CI, so developers no longer need to configure something like xvfb, ffmpeg, etc to do that. And personally, I use Cypress to test some projects, and I think it can use rrweb to replace its current DOM snapshot which may save a lot of memory.
There are already some demo tools have a similar concept of rrweb, but may only work in some specific situations. With rrweb, we may make some toolings like asciinema but for the web.
Related to #38, but for iframe.
<link rel="preload" href="https://static.parastorage.com/services/santa/1.6419.17/dist/packages-bin/core/core.min.js" as="script">
usage of this may still load scripts, should
The doc is not outdated, there is a built-in replayer rrweb.Replayer
and a standalone replayer rrwebPlayer
which is provided by the rrweb-player project.
Anyway, this looks like a bug and I'm trying to reproduce it.
Originally posted by @Yuyz0112 in #28 (comment)
This is very cool! Please add a LICENSE
When replaying web-page with element, the area of canvas has nothing.
Is it possible to support canvas recording?
scenario: by using repl record a demo page like https://codepen.io/chriscoyier/full/rwvjVN
expected: the page should show the elements
actual: the page shows up empty
code like this:
mounted() {
const self = this;
this.events = [];
this.record = rrweb.record({
emit(event) {
console.log(1);
self.events.push(event);
}
});
},
methods: {
stopRecord() {
this.record();
setTimeout(()=> {
const replayer = new rrweb.Replayer(this.events);
replayer.play();
}, 2000);
}
}
I do some actions, then stop recording and use replayer.play. Then the page stops working,as the picture shows. no console errors
I'd like to include this library in my error capture handler for an internal sales tool.
However, users might be on this page for quite some time, so I was hoping it would be possible to implement a "circular buffer" so I could capture just the last x events from when an error is logged.
Due to platform limitations, this would be more ideal than just saving every x seconds as your example shows.
Is it possible to start a replay for any point? I've done some quick testing and it doesn't seem to work, but maybe I'm just missing something?
import rrweb from 'rrweb'
console.log(rrweb )
//underfined
Great work so far! 🍺
Does it not useful if we could run as bookmarklet? For local recording ?
javascript:(function(){
// here
})();
Just an idea 😄
It would be cool to export a replay session as a video file, but I dont know what api's I would need to do that and if its event possible
Currently, we cannot record the following situation:
el1
el1
The added node event was recorded by MutationObserver in an async callback when the click event was recorded synchrony.
So the click event will not have target node id since el1
was not serialized at that moment.
Maybe we need serializing the node when it was recorded in some event, and check whether a node was serialized when handling added node mutations to avoid duplicate serialize.
Currently there are two-class names that can be added to hide data being input into them. It would be great if we could configure these class names
Wow - nice project! works really great.
If this project is MIT as the package.json
file suggests, please also add an official LICENSE file with the copyright and license so this can be used by the community.
When playing replays recorded from Firefox I get the error in the title.
It is reproducible every time with the following steps:
After that when you replay the saved events, it will show the correct session length, however when the player reaches the place where the browser window looses the focus it will throw this error in the console and will stop to play.
Here are all json events recorded and used in the replay. The error occurs on 16th second.
More info:
It happens only when the recording is done in Firefox. The same page recorded in other browsers does not break the player.
Firefox 64.0.2 on macOS
Related issues: #13
I tried to use the rrweb replayer, followed the documentation
let player = new rrweb.Replayer({
target: document.body, // customizable root element
data: {
events,
},
});
I saw that the documentation is a bit outdated, there are two different examples of how to configure the replayer, but neither work. I tried the 'target' option, the 'root' option, I tried both the rrweb.Replayer
and the rrwebPlayer
constructor, but the second one is undefined.
My whole script:
<head>
... cruft ...
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/rrweb@latest/dist/rrweb.min.css"
/>
<script src="https://cdn.jsdelivr.net/npm/rrweb@latest/dist/rrweb.min.js"></script>
... cruft ...
</head>
<body>
... stuff ...
<script>
const events = {}; // the events I got from a recording session
let player = new rrweb.Replayer({
root: document.body, // customizable root element
data: {
events,
},
});
player.play(events);
</script>
</body>
And then I get the error this.config.root is null
. Note, the error happens at the constructor, the script doesn't get to the player.play
part.
If some elements were in the ignore list, stop observe and record its value.
We also need a default to ignore list, including some elements like password input and so on.
First of all, thank you very much for the excellent job you did!
We realised that the tooling provided by RRWeb is not only useful for recording sessions, but also for analysing user behaviour. However, there are some events that we are interested in that are not captured, such as form submissions. To capture these events, we are registering listeners that share the same array of events as the recorder's callback to maintain the order in which the events occurred. It works fine, but we have two use cases that we do not manage to solve: 1) enrich the events supported by RRWeb with extra information, such as the element ID and 2) include the node ID into our custom events. To do so, we need to retrieve the node ID from a DOM Node, as well as retrieve the DOM Node from a node ID.
Could you give us some guidance if it is possible and how to expose such features?
I compared rrweb's current mouse highlight with the visually very strong animated tail from Demo LINK: Yandex Metrica and I think psychologically it is easier to follow the user's intent while on the site with the tail.
I'm sure you've heard this complaint many times when you did some presentation, people always complain that you shouldn't move the mouse so fast - why? Because it's hard to follow for people, even if you enlarge the screen by a lot.
The small circle in my opinion is not enough, the way that Yandex does it allows me to kinda see the history of the movement even if I didn't manage to follow the mouse the moment it zig zagged. However it may be very important to see these nuanced movements of the mouse, it gives you an idea of how the user felt while moving the mouse.
Seems like it would make sense to have the ability to stop recording.
If you try to import rrweb from 'rrweb';
in a typescript project, you get an error:
File '.../node_modules/rrweb/index.d.ts' is not a module.ts(2306)
When defining the configuration for the playback player rrwebPlayer, is there a way to trigger a function after playback ends?
For example, a function on finish, on pause, on start?
It would be nice to be able to configure the value, so a user would be able to set how detailed and how often would events be emitted for higher precision or higher data savings.
Line 232 in 65fd314
Hi everyone !
I have started to use rrweb on a website and it's awesome !
I just have a small problem with the record. Some images are not correctly displayed in the replay, when they use the "srcset" attribute with relative URLs.
As you can see in the image below, in the HTML page, relative URLs are used for the images.
In the screenshot below, it's a part of the event formatted by rrweb. As you can see, the "src" attribute is now absolute URL and it's good ! But the "srcset" attribute remains relative and it's causing trouble in the replay.
Is there a way to fix that ?
Thanks for the help ! :)
DOM attributes have many corner cases, every JS UI framework handles these.
For rrweb, we need improve our setAttribute
and removeAttribute
calls.
Never used TypeScript before and I'm completely lost with all these .ts files.
That would be nice if the guide could offer some explanation on how to convert the whole project in JS files that old-school developers like me could handle more easily.
You can't use this library without Webpack ?
We need to improve the 'play at any time' feature which supports rrweb-player's DND interactions.
Currently, we do this by introducing sync
mode which will rebuild snapshot before time offset synchronously and then play under the timer. But this may cause a performance issue when we try to play at a large time offset.
For example, if we have 100 snapshots and we play in the third snapshot's time offset, we need to rebuild 3 snapshots synchronously which is fine. When we are trying to play at the 99th snapshot's time offset, we need to rebuild 99 snapshots synchronously which will block everything.
So the solution is to build checkpoints when we create a replayer. We may cache the result of rebuilding every 50 snapshots, so the maximize synchronously snapshots rebuilding number is 50.
So I'm wondering if there is documentation breaking down the types of events logged in by the recorder. For example - some cool features of fullstory that would be good to have include some variables that could be determined by the event history:
I see a few types of events (type 2, 3, 4, etc) but it's hard to understand which events are which, and how to extract information like that from the recorded events.
Any ideas?
I am going to provide a frontend monitor sdk (includes replaying). For some reasons, I need to block some elements in the recording stage.
Problems: I'd better not modify their code and dom (adding .rr-block). At the same time, they are probably not willing to modify and release a new version.
Is it possible to add a hook? Then users can decide whether an element should be blocked without adding some class. (transparent to them and their website)
Right now, the protocol doesn't have a standardized way for storing non-attribute data directly linked to an element. One example of this is the isSVG
property, which is stored separately in the element object. Another example is element widths and heights, which are instead stored in a few internal attributes.
Now, conceivably as we extend rrweb, we'll have a need to add additional properties. In fact, I've been working on supporting transmitting of media state (whether a video is playing, paused, when the user seeks to another point in the video, etc) and I've ran into the same issue. (See here and here for that work-in-progress implementation.) Right now I've opted to add an additional property and store my data there, but it'd be nice to have a standardized way for storing such data.
I think using a structure similar to how currently events are modelled might be nice (using a object with a type
property referring to an enum). This will allow future extensions / support for additional properties without breaking existing implementations, which can just skip non-supported types.
We can then also move existing data such as the isSVG
flag or the width
and height
data to this new structure.
However, this approach might also introduce some overhead, and I'm not sure how much of a problem that is going to be.
@Yuyz0112 What do you think is the best way to deal with this?
I use this code:
////=====CODE
var events = [];
rrweb.record({
emit(event) {
events.push(event);
},
});
////=====CODE
I'd like to play in real time the record. Is that something possible?
I actually record a web page and send events to a backend that's broadcasting events with websocket.
What's the best way to add new fetched events to current replay?
I tried something like this:
mixed.on('progress.update', function (data) {
if (data.events && data.events.length) {
if (!player) {
player = new rrwebPlayer({
target: document.getElementById('player'),
data: data,
});
} else {
for (var i = 0; i < data.events.length; i++) {
player.options.data.events.push(data.events[i]);
}
}
}
});
If that's not possible is it a planned feature? I'd like to make a live debugging feature for our customer, something similar to www.fullstory.com.
Hi!
First I want to say that this project is amazing to use, simple and fast to implement! :)
I have 2 react SPA, one is relativley straight forward, while the other is much more complex. The simple site works perfectly! :)
But the slightly more complex site looks like this:
But in the records it looks like this instead:
This site uses styled-components heavily, and the grey color comes from the default index.scss.
My code looks like this:
const useRRWeb = () => {
const [allow, setAllow] = useState(false)
const eventsRef = useRef([])
const allowRecording = () => setAllow(true)
const getRandom = () => Math.random().toString(36).substring(7);
import("rrweb").then(({record}) => {
record({
emit: (event) => {
eventsRef.current.push(event)
}
})
})
useEffect(() => {
if (!allow)
return;
const sessionId = getRandom() + getRandom() + getRandom();
const save = () => {
const events = eventsRef.current
const body = JSON.stringify({events});
eventsRef.current = [];
fetch(getUrl(sessionId), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body,
}).catch(() => {
eventsRef.current.unshift(events)
});
}
const saveInterval = setInterval(save, 10 * 1000);
record({
emit: (event) => {
eventsRef.current.push(event)
}
})
return () => {
clearInterval(saveInterval)
save()
}
}, [allow])
return [allow, allowRecording]
}
export default useRRWeb
Any ideas about where I should start to look?
We are using requestAnimationFrame
to perform a more accurate timer than setTimeout
.
Currently, we init a timer for every recorded event, which means there are multiple requestAnimationFrame
checks at the same time.
According to the spec, the callbacks of RAF are stored in a list and executed in order, so multiple RAF should have no main difference to one.
But this benchmark shows the multiple ones was much slower.
Maybe we should also implement a callback list with an optimized data structure, which sorted the callbacks by its timestamp.
For example:
[
{ cb: fn1, timestamp: t1 },
{ cb: fn2, timestamp: t2 },
]
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.