9
\$\begingroup\$

I'm building an event recorder. So far the code looks clean, but I feel like I'm missing something that can make this code even better.

Is composition a better solution? If yes, how can I do it properly in this context. Do you know a better way to share the Recorder across all other components? Will it be better to initialize the recorder in app.js and then pass it to the other components (mouse, keyboard)?

Mouse.js and Keyboard.js look pretty much the same, except they record different event types. Is there a way to avoid repetition (stop and record method)? Just tell me what you think about the logic.

This needs to be flexible because I have no idea how many events I will need to record in the future.

app.js

export default (params) => {
 const container = document.getElementById(params.container),
 recorder = Recorder('session'),
 keyboard = Keyboard(),
 mouse = Mouse(container);
 const startRecording = () => {
 recorder.record('Start recording');
 keyboard.record();
 mouse.record();
 }
 const stopRecording = () => {
 keyboard.stop();
 mouse.stop();
 recorder.record('Stop recording');
 }
 const init = () => {
 document.getElementById('start').onclick = (e) => {
 e.preventDefault();
 startRecording();
 };
 document.getElementById('stop').onclick = (e) => {
 e.preventDefault();
 stopRecording();
 };
 }
 return {
 init
 }
}

recorder.js

import Data from '../db/eventsLog';
import moment from 'moment';
export default (type) => {
 const record = (input) => {
 const t = moment().unix();
 Data[type].push({t, event: input});
 }
 return {
 record
 }
}

keyboard.js

import Recorder from './recorder';
export default () => {
 const recorder = Recorder('keyboard');
 const initRecorder = (recording) => {
 document.onkeydown = (e) => {
 if (recording) {
 recorder.record(e);
 }
 };
 };
 const record = () => initRecorder(true);
 const stop = () => initRecorder(false);
 return {
 record,
 stop
 }
}

mouse.js

import Recorder from './recorder';
export default (container) => {
 const recorder = Recorder('mouse');
 const startRecording = (recording, e) => {
 if (recording) {
 recorder.record(e);
 }
 };
 const initRecorder = (recording) => {
 // Mouse mouve
 container.onmousemove = (e) => startRecording(recording, e);
 // Mouse Double click
 container.ondblclick = (e) => startRecording(recording, e);
 // Mouse Click
 container.onclick = (e) => startRecording(recording, e);
 };
 const record = () => initRecorder(true);
 const stop = () => initRecorder(false);
 return {
 record,
 stop
 }
}
Mast
13.8k12 gold badges55 silver badges127 bronze badges
asked Apr 5, 2016 at 3:23
\$\endgroup\$
2
  • \$\begingroup\$ I am just wondering, is this in the format for ECMAScript 6? \$\endgroup\$ Commented Jun 17, 2016 at 15:59
  • \$\begingroup\$ @ObinnaNwakwue Yes it's factory function with fat arrow in order to avoid 'this' keyword \$\endgroup\$ Commented Jun 22, 2016 at 18:02

2 Answers 2

2
\$\begingroup\$

I was going to put this as a comment, but could not show formatted code, so this will have to be an answer.

If you ever want to adapt your script to records "all" events, you could use something like this:

var Events = Object.keys(window).reduce(function(events, prop){
 if( prop.substring(0,2) === 'on' ){
 events.push(prop.slice(2));
 }
 return events;
 }, []);
 Events.forEach(function(event){
 window.addEventListener(event, function(e){
 console.log(event + " fired");
 })
 })

You would definitely want to debounce your event recordings, otherwise you will eat up a lot of the event loop.

There are other events, in the browser, but these are almost all of them, for a complete list checkout MDN Events and MDN Event - Web APIs for more details.

answered Apr 14, 2017 at 17:24
\$\endgroup\$
0
\$\begingroup\$

Why not take advantage of ES2015's classes / inheritance?

'use strict';
class Recorder {
 constructor(type, element, data) {
 this._type = type;
 this._element = element;
 this._data = data;
 if(!this._data[this._type]) {
 this._data[this._type] = [];
 }
 }
 
 _record(input, msg) {
 const t = new Date().getTime();
 this._data[this._type].push({t, event: input});
 console.log(t,`${msg} - Data has ${this._data[this._type].length} ${this._type} entries`);
 }
 
 _startStop(start) {
 const addOrRemoveEventListener = (start ? 'add' : 'remove') + 'EventListener';
 
 for( let eventName of Object.keys(this._handlers)) {
 this._element[addOrRemoveEventListener](eventName, this._handlers[eventName]);
 }
 
 }
 
 start() {
 this._startStop(true);
 }
 
 stop() {
 this._startStop(false);
 } 
}
class KeyboardRecorder extends Recorder{
 constructor() {
 super('keyboard', ...arguments);
 this._handlers = {
 keydown: this._handleKeyDown.bind(this)
 // add more handlers...
 };
 }
 _handleKeyDown(e) {
 this._record(e,'record keydown : '+String.fromCharCode(e.keyCode));
 }
 
}
class MouseRecorder extends Recorder{
 constructor() {
 super('mouse', ...arguments);
 this._handlers = {
 mousemove: this._handleMouseMove.bind(this),
 click: this._handleClick.bind(this),
 dblclick: this._handleDblClick.bind(this)
 // add more handlers...
 };
 }
 _handleMouseMove(e) {
 this._record(e,'record mousemove');
 }
 
 _handleClick(e) {
 this._record(e,'record click');
 }
 
 _handleDblClick(e) {
 this._record(e,'record dblclick');
 }
 
}
class App {
 constructor(params = {}) {
 this._container = params.container;
 
 this._data = {};
 this._keyboardRecorder = new KeyboardRecorder(this._container, this._data);
 this._mouseRecorder = new MouseRecorder(this._container, this._data);
 
 
 document.getElementById('start').onclick = (e) => {
 e.preventDefault();
 this.startRecording();
 console.log('started');
 };
 
 document.getElementById('stop').onclick = (e) => {
 e.preventDefault();
 this.stopRecording();
 console.log('stop');
 };
 
 }
 
 startRecording() {
 this._keyboardRecorder.start();
 this._mouseRecorder.start();
 }
 stopRecording() {
 this._keyboardRecorder.stop();
 this._mouseRecorder.stop();
 }
}
const app = new App({container: document});
#cont {
 width: 400px;
 height: 200px;
 background-color: yellow;
}
* {
 -moz-user-select: none;
 -khtml-user-select: none;
 -webkit-user-select: none;
 -ms-user-select: none;
 user-select: none;
}
<button id="start">Start</button>
<button id="stop">Stop</button>
Try moving, clicking, double clicking the mouse, press keys here.

answered Mar 2, 2017 at 21:01
\$\endgroup\$
1
  • 3
    \$\begingroup\$ You have presented an alternative solution, but haven't reviewed the code. Please explain your reasoning (how your solution works and why it is better than the original) so that the author and other readers can learn from your thought process. \$\endgroup\$ Commented Apr 12, 2017 at 11:10

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.