I'm working on a private site with important data, and I want to log out any user who hasn't done anything for 10min (forgot the tab open in the background as an example). How can I do that, is just running an event listener to the mouse clicks with a timer is fine, or are there other better solutions for this?
This can be achieved by JavaScript only.
Since our web app can be open in multiple tab, so its better to store last activity of user in localStorage
First lets declare events which we consider as user activity and store time of user activity in localStorage
document.addEventListener("mousemove", () =>{
localStorage.setItem('lastActvity', new Date())
});
document.addEventListener("click", () =>{
localStorage.setItem('lastActvity', new Date())
});
Next lets create interval which will be checked on every given interval.
let timeInterval = setInterval(() => {
let lastAcivity = localStorage.getItem('lastActvity')
var diffMs = Math.abs(new Date(lastAcivity) - new Date()); // milliseconds between now & last activity
var seconds = Math.floor((diffMs/1000));
var minute = Math.floor((seconds/60));
console.log(seconds +' sec and '+minute+' min since last activity')
if(minute == 10){
console.log('No activity from last 10 minutes... Logging Out')
clearInterval(timeInterval)
//code for logout or anything...
}
},1000)
event listeners for mouse movement (not clicks), and event listeners for keyboard clicks as well should do the job.
Let's say you want to log out the user after 10mins of inactivity. Simply start a timer (for 10mins) the moment user logs in, every time the user makes any mouse movement or any keyboard clicks, use the event listener to reset the timer.
If there is no activity, after 10mins, the timer should execute log out functionality.
Update :
Like the case suggested by Vishnu Bhadoriya, in that case, the idle solution should be to implement a heartbeat b/w the client and the server. Now, in this case, your mouse and keyboard listeners would send a lifeline event to the server, and the server will keep its session alive. When the lifeline event is not received by the server for more than 10mins (threshold for activity), the server can emit an event to the client which can log him out or can simple invalidate the client's auth token
Related
I'm creating some kind of UI, and I had an idea to create a spotlight in JS. That would mean that if user completely wouldn't interact with the website for example 2 minutes execute a function. That brings me to the question: how to trigger a function after any event
I would like something like that:
document.addEventListener("all", spotlight)
I know that I can do it, but it would be annoying and I know that there must be some more elegant solution
JS:
document.addEventListener(----, spotlight)
function spotlight() {
spotlightCanvas.classList.remove("hide")
}
Thanks.
You could try something like: List of events
const event = ['click','focus'];
let watchdog = new Date();
event.forEach(ev=>{
document.addEventListener(ev,_=>{
watchdog = new Date();
});
});
setInterval(_=>{
if(new Date() - watchdog > 120000){
spotlight();
}
},100);
There is no one event that represents all events but you certainly can do what you are seeking. Since there are many ways to interact with a website (mouse clicks, keyboard clicks, mouse movements, etc.) you'll need to set up a timer and when any of these events happen, reset it. If the timer reaches 120 seconds, then some other function can happen.
See the comments in the working example below that only waits 5 seconds.
// Make an array of all the event names you want to listen for
const event = ['click','mousemove', 'touchstart', 'keydown'];
let maxSeconds = 5; // Amount of time document has to be inactive
let inactive = 0; // Amount of time document has currently been inactive
// This is a function that will run approximately every second
let timer = setInterval(function(){
// If we add one to the inactive time and have reached the max...
if(++inactive === maxSeconds){
spotlight(); // Run the desired function
}
}, 1000); // Run again in about a second
// Loop over each of the strings in the array
event.forEach(ev=>{
// Add an event listener for each event name
document.addEventListener(ev, function(){
inactive = 0; // reset the amount of inactive time
});
});
function spotlight(){
alert("Time's up!");
}
A simple solution would be to just listen for mousemove events. The user will almost always move the mouse with any interaction.
Reset a 2 minute timer every time the mouse moves. When there is no movement for 2 minutes, the spotlight function runs.
let timer
window.addEventListener("mousemove",function(){
clearTimeout(timer)
timer = setTimeout(spotlight, 120000)
})
function spotlight(){
console.lgo("the mouse disn't move for 2 minutes")
}
You might include an option for keydown events for users who don't use the mouse, but I don't think you need to listen to ALL events.
I found an example of what I need at Google Firebase documentation here.
Still, I want to modify it a bit to make it check the presence of user every second/10 seconds or at least every minute depending on how this will affect the load on the server so I came up with this:
TestApp.prototype.initFirebase = function() {
this.database = firebase.database();
this.database.ref(".info/connected").on("value", this.isOnline.bind(this));
};
TestApp.prototype.isOnline = function(snap) {
var i=0;
console.log(snap.val());
setInterval(function() {
if (snap.val() === true) {
console.log("connected"+(i+=10));
} else {
console.log("not connected");
}
}, 10000);
}
But here is what happens in the console if I run it:
main.js:34 false
main.js:91 User signed out
main.js:34 true
main.js:39 not connected
main.js:37 connected10
main.js:39 not connected
main.js:37 connected20
main.js:39 not connected
main.js:37 connected30
main.js:39 not connected
main.js:37 connected40
It triggers the function every 10 seconds but it shows me both results which are connected and disconnected at the same time. (actually, there is delay around 1 second) Moreover, it completely ignores if user is logged in or not and shows me the same logs every time.
I want it to run isOnline method only if the user is logged in(with email and password). And if that is true isOnline should send request to the server if user is online every N seconds. Is there anything I can do?
It would be much better if I could check if user is connected from the server side because I need to make some actions as long as user remains online. But I am not sure if that's possible so I think the best way is to check on frontend and trigger actions with HTTP triggers.
The current behavior is due to the values of snap being captured in the timer closures.
When you are setting up isOnline to trigger on value event, Firebase invokes the method every time the value of the key changes. In this case, Firebase invoked isOnline first time when it determined that the value is false, and then second time after login was established and the value became true.
Now inside isOnline, you are starting off timeouts. As the function was called twice with different snap objects, two timeouts were created. But both of them have their own snap objects in their closures, which are fixed to the values when isOnline was invoked.
As the end-result, you have two perpetual timers running which keep printing the historic snap values :).
As you mentioned that you only want to do something periodically if and only if the user is online, you should try this instead:
isOnline : function(snap){
let test = snap.val();
// If the user is not online,
// check if we had the timer set,
// as we should clear it now.
// It essentially means user went offline.
if (!test && this.whenOnline) {
clearTimeout(this.whenOnline);
this.whenOnline = null;
return;
}
// If the user is not online, return.
if (!test){
return;
}
// User is online. Install the timer if we haven't.
if (!this.whenOnline){
this.whenOnline = setTimeout(this.doSomethingWhenOnline, 10000);
}
}
doSomethingWhenOnline : function(){
// whatever;
// Cue the next timer again, notice we only install if
// the previous instance has not been cleared by the
// isOnline handler.
if (this.whenOnline){
this.whenOnline = setTimeout(this.doSomethingWhenOnline, 10000);
}
}
I have used a few of the jquery keepalive session plugins with out problem.
I have been asked for something a bit different.
We have some forms (built before I started here) that are fairly large and users work in them for a while. The page is never refreshed, so they click save and the session is expired and redirect to the login page.
I suggested one of these plugin, that just prompt the user a few minutes before the session expires which would make an ajax call to keep the session alive.
However, they said, will what if they dont see the prompt and miss it all together and logs them out.
They would like me to check
Has the user had any interaction with the page in the last 5 minutes.
If Yes=Ajax call to keep alive the session, and reset timer.
if No, continue to wait until we get within 2 minutes of session time out and prompt user.
They are trying to avoid the prompt.
Is there anyway with JS/Jquery to know if the page has had any client side interaction?
Rather than using a timer to check if they've had any interaction in the last 5 minutes, couldn't you just send your keepalive any time the form has changed? It would eliminate a need for a timer loop and a small payload ajax call just to keep the session alive shouldn't hurt performance at all.
If you still want to keep the timer loop, I would still recommend using the change event on your form elements. Changing the form implies they're interacting with it, and I think that satisfies their requirement.
Edit: Update to use timer
var idle = true;
function finalIdleCheck(prompt){
if(idle){
if(prompt){
alert("last warning!");
}
//Tighten the idle check time loop; may even want to go < 30s
setTimeout(finalIdleCheck, 30*1000);
} else {
//Ajax stuff to keep session alive
idle = true; //Reset idle flag
}
}
function checkIdle(){
if(idle){
//Warn them
alert("You've been idle");
setTimeout(function(){
finalIdleCheck(true);
}, 60*2*1000);
} else {
//Ajax stuff to keep session alive
idle = true; //Reset idle flag
}
}
$(document).ready(function(){
$("form input").on("change", function(){
idle = false;
}
setTimeout(idleCheck, 60*5*1000);
}
I've got a field A in my webpage which, when edited by the user, invokes an API call (using jQuery), which updates field B. After the edit, the API should be called every 10 seconds to update the field B again. I currently do this using:
setTimeout(thisFunction, 10000);
The problem is that this timeout is set every time the user edits field A, which after editing field A a couple times causes the timeout to be set multiple times and the API to be called many many times. This makes the website look pretty stressy.
What I would rather like to do, is set a new timeout every time the field is edited, be it either by the user editing field A, or by the interval reaching 10 seconds and thereby polling the API. In other words; the field should be updated if field B wasn't updated for 10 or more seconds.
Lastly, if the user then clicks button C, the polling should stop.
So my question; how can I run a function to update field B if that field B wasn't updated for 10 or more seconds and how do I stop the polling when I want to (when the user clicks another button) All tips are welcome!
A timer can be cancelled with clearTimeout, so:
var timer = null;
if (timer) {
clearTimeout(timer); //cancel the previous timer.
timer = null;
}
timer = setTimeout(thisFunction, 10000);
var update;
// something happened
clearTimeout(update);
update = setTimeout(thisFunction, 10000);
I am building a live bidding website using Php and Pusher. There is a countdown timer in the site.
If the timer goes below 15 secs and if someone bids for a auction, the timer is reset back to 15 again. I am using Pusher to update timer in all open browsers. When the timer reaches 0 a function is called to end the auction.
Here is the problem I am facing:
If someone bids when the timer is around 1 secs left, the timer is updated in the browser in which the bid occurred. But as Pusher takes around a second to update the other browsers, the timer ends on other computer and the function to end the auction is called.
Here is sample code on what I am currently doing:
var pusher = new Pusher('key');
channel = pusher.subscribe('wppa');
pusher.connection.bind('connected', function(){
});
channel.bind('bid_added_event', function(response) {
StreamedAuction.update(response, AuctionModel);
});
channel.bind('end_auction_event', function(response) {
StreamedAuction.end(response, AuctionModel);
});
channel.bind('update_timer_event', function(response){
StreamedAuction.updateEndDate(response, AuctionModel);
});
If someone bids and the timer is less than 15 updat_timer_event is called to update the timer in all channels.
If the timer reaches 0, the the end auction_event is called. The problem is if someone bids when timer is 1 sec, the auction is ended even before the update_timer_event is called.
I am wondering if there is a way in pusher where you could listen if the event is fired in all channels. If this is possible the I was thinking I could do something like:
if the event is fired in all browsers
then end the auction
else
reset the counter to 15 in all channels
as somebody bided and the auction ending event wasnt fired from
one of the channel
Please let me know if you require more details or code.
Thanks.