I am just getting into the world of javascript, coming from the world of classical inheritance. The following is a library I wrote to track when a webapp is idling and I wrote it like I write classical inheritance. But what I have heard about javascript is you have to completely forget how classical inheritance works.
I would love to see how a real javascript developer would write the following using prototypes and the likes.
var IdleListeners = new function(){
var self = this;
window.onmousemove = resetTimer;
window.onmousedown = resetTimer;
window.onclick = resetTimer;
window.onscroll = resetTimer;
window.onkeypress = resetTimer;
function resetTimer() {
for (var i = 0; i < self.listeners.length; i++) {
self.listeners[i].reset();
}
}
}
IdleListeners.listeners = [];
IdleListeners.add = function(idleFunction, timeout){
this.listeners.push(new IdleListener(idleFunction, timeout));
}
var IdleListener = function(idleFunction, timeout){
this.idleFunciton = idleFunction;
this.timeout = timeout;
this.reset = function(){
if (this.hasOwnProperty('interval')){
clearInterval(this.interval);
};
this.interval = setInterval(this.idleFunciton, this.timeout);
}
}
It is used by simply calling the following in your html/js file
IdleListeners.add(function(){
console.log("Idle!")
}, 1000);
"Idle!" would be called once every 1000 miliseconds when the mouse hasn't moved or no keys have been pressed.
1 Answer 1
window.onmousemove = resetTimer;
window.onmousedown = resetTimer;
window.onclick = resetTimer;
window.onscroll = resetTimer;
window.onkeypress = resetTimer;
First, I'd suggest not binding handlers this way. The problem is that some other code might override the property and completely replace the handler. Even if you take steps not to override an existing one, there's no guarantee that yours won't get overridden. Use addEventListener
instead.
Also, document
and window
have different events. document
related to the DOM elements (i.e.: click, mouseover) and window
related to window events (i.e. resize, scroll etc.). See this answer for details.
var IdleListeners = new function(){
You're not benefiting from the perks of constructor-created objects. What your code essentially does is create an instance of an anonymous constructor which has no methods nor properties. You merely created a closure that wraps the initialization code and your timer reset.
var IdleListener = function(idleFunction, timeout){
For this one, you almost got it, except for the this.reset
. The goal of prototypal inheritance is to share methods among instances. Defining reset
in this
instead of the prototype means that each instance gets its own copy of reset
, not shared.
this.interval = setInterval(this.idleFunciton, this.timeout);
I believe you want setTimeout
. You wouldn't want the handlers to fire periodically, especially when they're bound to an alert
. It would be endless.
But what I have heard about javascript is you have to completely forget how classical inheritance works.
Not really. You just need to know how classical inheritance is different from prototypal inheritance. Also, you shouldn't force JS to do what it doesn't do natively. While it can emulate, it's not pretty.
Keep the code simple. As much as possible, work with just functions, objects (maps) and arrays (lists). Don't jump into OOP or inheritance directly. For instance, I can simplify your code with a singleton and a list of handlers.
;(function(ns){
// An array for our "listeners"
var listeners = [];
// An add function which accepts a delay and handler
ns.add = function add(handler, timeout){
// All we're doing here is create an entry in the array containing our
// settings, and preparing the timer as null. Reset will build it for us.
listeners.push({
handler: handler,
timeout: timeout,
timer: null
});
}
// Now when you call reset (as a result of an action), it loops through all
// the handler definitions, clears any existing timers, and rebuilds them.
ns.reset= function reset(){
listeners.forEach(function(listener){
clearTimeout(listener.timer);
listener.timer = setTimeout(listener.handler, listener.timeout);
});
}
// Add handlers
document.addEventListener('click', reset);
// Use an existing global or create one
}(this.IdleListener = this.IdleListener || {}));
// Usage
IdleListener.add(function(){
console.log('idle!');
}, 1000);
-
\$\begingroup\$ Question about this though, when we call
ns.add(...)
the timer is never set. So that means if the page is loaded and the user doesn't click/move mouse idle will never be run, even though the page is idling. To fix this, instead of passing null into timer, I tried passingsetTimeout(handler, timeout)
. Is there a better way of doing this? \$\endgroup\$mattNit– mattNit2016年05月05日 03:59:18 +00:00Commented May 5, 2016 at 3:59 -
\$\begingroup\$ @mattNit Yes, that should work. But then, who doesn't move when the page loads anyways? Unless we're talking about a kiosk, but then this code wouldn't make sense on a kiosk. :P \$\endgroup\$Joseph– Joseph2016年05月05日 04:01:41 +00:00Commented May 5, 2016 at 4:01
Explore related questions
See similar questions with these tags.