Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to efficiently record user typing using javascript?

Tags:

javascript

I'd like to be able to record then playback whatever happened in a textarea.

I've came across some solutions but they're not reliable, Like sending each keystroke via AJAX. In that case i'll end up having millions of rows in my DB.

The idea that i had in mind is to log the keystrokes to a variable in client side, updating that variable with the action, but keeping track of time between each keystoke. Also making sure that it supports deleting data as well.

At the end i'd send this whole variable to the db one time, then i can decode it later for playback.

Mind Map of what the variable would look like:

   hellooo[1.2][backspace][0.6][backspace]World![return]
Idle time __^     Removes one char __^

I believe that google docs is doing something like that to playback whatever users were typing.

Any Ideas?

like image 837
CodeOverload Avatar asked Nov 09 '11 20:11

CodeOverload


2 Answers

Luckily JavaScript events already take care of all the encoding issues for you. You can just toss the whole event object that a keyup/keydown/keypress/whatever was directly into an array.

Each object contains:

  • Type of event
  • Timestamp
  • What key was used (or combination of keys)
  • What was in focus
  • Some other stuff that might end up being useful.

You can then just encode the array and send it off with your favourite ajax method to the server for storage.

So your code only needs a function that can store data. Don't use this exact function it's just for demonstration purposes.

var handler = function (e) { 
    handler.data.push(e);
    console.log(handler.data);
}
handler.data = [];

window.addEventListener("keyup",    handler); 
window.addEventListener("keydown",  handler); 
window.addEventListener("keypress", handler);

Since it's an array, they should all be in order, but in the odd event it goofs, you have timestamp data on each event (which also lets you find out the delay between events, which is AWESOME if you have mixed keypresses.).

You can then replay events however you wish to design them -- but now you don't have to invent your own spec because the lovely fokes at w3c did all the hard work for you when they designed the DOM event spec.

like image 98
Incognito Avatar answered Nov 14 '22 23:11

Incognito


Store the time of each action and the result of that action and when you are finished serialise the log and store that.
It is over-complicated to replay each action individually. Say a user moves back a few characters and adds new ones there. You will need to keep track of the cursor position.
Just remember the entire value of the textarea for each keystroke. There is no need to remember how that occurred is there?

Here's an implementation. fiddle

<textarea id="recorder"></textarea>
<textarea id="playback"></textarea>

<script type="text/javascript">

var Playback = {
    //store the time an action occured and the resulting state in an object
    //don't use an array because they are not sparce - interstitial keys
    //will have to be iterated over
    record: {},
    init: function( recorderId, playbackId ) {
        this.recorder = document.getElementById( recorderId );
        this.playback = document.getElementById( playbackId );

        this.recorder.addEventListener( 'focus', function() {
            Playback.record = {};
            this.value = '';
        }, false );

        this.recorder.addEventListener( 'keyup', function( e ) {
            Playback.record[ (new Date()).getTime() ] = this.value;
        }, false );

        this.recorder.addEventListener( 'blur', function( e ) {
            Playback.playback.value = '';
            //store the time the sequence started
            //so that we can subtract it from subsequent actions
            var mark = null;
            for( var t in  Playback.record ) {
                if( mark ) {
                    var timeout = t - mark;
                } else {
                    var timeout = 0;
                    mark = t;
                }
                // We need to create a callback which closes over the value of t
                // because t would have changed by the time this is run
                setTimeout( Playback.changeValueCallback( Playback.record[t] ), timeout );
            }
        }, false );

    },

    changeValueCallback: function( val ) {
        return function() { Playback.playback.value = val }
    }
}

Playback.init( 'recorder', 'playback' );

</script>

Caveat: The event handling is for compliant browsers only, you'd need to accommodate internet explorer yourself

like image 24
meouw Avatar answered Nov 14 '22 21:11

meouw