Manage timer between windows, browser tabs. localStorage events - javascript

I was searching for a way how to communicate between multiple tabs or windows in a browser (on the same domain, not CORS).
I am using timer and it should working on all site pages. If there's a way to stop/pause/play timer on all pages (new windows) simultaneously ?
For example I have 2 tabs - page 1 and page 2... on both pages I have running the same timer. If I click pause timer on page 1 it must pause on page 2 too.
I can't find a solution all day long ... help please.
My timer code:
const Timer = easytimer.Timer;
const getSavedTime = () => JSON.parse(localStorage.getItem('time')) || {};
const instance = new Timer({ startValues: getSavedTime() });
instance.addEventListener('secondsUpdated', () => {
document.querySelector('.example').textContent = instance.getTimeValues().toString();
localStorage.setItem('time', JSON.stringify(instance.getTimeValues()));
});
instance.addEventListener('started', () => localStorage.setItem('running', '1'));
instance.addEventListener('paused', () => localStorage.removeItem('running'));
instance.addEventListener('stopped', () => {
localStorage.removeItem('time');
localStorage.removeItem('running');
document.querySelector('.saved').textContent = '';
document.querySelector('.example').textContent = '';
});
document.querySelector('.saved').textContent = localStorage.getItem('time');
document.querySelector('.example').textContent = instance.getTimeValues().toString();
document.querySelector('.start-button').addEventListener('click', () => instance.start({ startValues: getSavedTime() }));
document.querySelector('.pause-button').addEventListener('click', () => instance.pause());
document.querySelector('.stop-button').addEventListener('click', () => instance.stop());
if (localStorage.getItem('running') === '1') {
instance.start({ startValues: getSavedTime() });
}
listen event:
window.addEventListener('storage', function (e) {
console.log("storage event occured here");
},false);

You can use BroadcastChannel to communicate between different tabs within the same origin.
The harder part would be managing which tab gets to "run" the timer code and which tabs just "listen" to the events. Then, if the tab that was running the timer got closed, how would you coordinate deciding which other tab should take over that role.
A better option might be to put the timer code in a Web Worker (specifically a SharedWorker), and then just use BroadcastChannel (and/or maybe the port property of the SharedWorker) to send the events out to the tabs.
If you're lucky, you might be able to get your easytimer.Timer class to run within a Web Worker without any modifications - although you would still need to do some work to hook it up to the BroadcastChannel.

Related

Click A button faster

We are playing a clicker game on a website where a button will be displayed at a random time, first 7 players to click it will be shown as the winners of that round. I wrote the clicker function bellow and always use it on my Chrome console.
(function () {
setInterval(() => {
const button = document.querySelector(“.click-button);
if (!button) return;
button.click();
}, 5);
})();
I was playing it with mobile network of 50 - 70 mbps of speed and was among the top 5 until when other players started using VPS Machine which have over 4gbps of internet speed. I now have a VPS running Windows server 2022 but cant still perform better. So My question is, Is it that my VPS speed is slower than their own or My Laptop have less specification than their own or I need a better javascript code than the one above?
Running the above code on a VPS server with a download speed of 4.3gbps and upload speed of 4.1gbps through a browser console and click faster
Instead of polling for the button, you could use a MutationObserver that will notify you when the button changes or is inserted into the document. Unless you can get to the event that triggers the button activation, this is probably the fastest you can do.
Assuming that the button is inserted into a <div id="button-container"></div>:
const callback = mutations => {mutations.forEach(m => m.addedNodes.forEach(node => node.tagName === 'BUTTON' && node.click()))}
const observer = new MutationObserver(callback)
const container = document.getElementById('button-container')
observer.observe(container, {childList: true})
Have a look at this codepen
Replacing the forEach() with a for will give you a few additional nanoseconds.
Your code can be a little faster by caching the function and the button outside the interval
(function() {
const button = document.querySelector(".click-button");
const buttonClick = () => button.click();
if (button) setInterval(buttonClick, 5);
})();
If the button does not exist at all, then the code above will not work. Then you do need the mutation oberver

how to clear session when the user leaves my site

Im trying to clear my session when the user leave my site.
when i try to do like below using 'beforeunload' event, even when i try to give enter on my site/application url also my handleBeforeUnload is getting called. Ideally i want to clear if the url(host/origin) is entered is different from my site then only i want to clear my session.
how to escape calling handleBeforeUnload - if the user enter my site specific urls ? or any other possible solution to achieve this request would be appreciated.
Below code i tried with my app.js
const getAppOrigin = window.location.origin;
useEffect(() => {
window.addEventListener('beforeunload', handleBeforeUnload);
return () => {
window.removeEventListener('beforeunload', handleBeforeUnload);
};
}, [getAppOrigin]);
const handleBeforeUnload = ev => {
if (ev || window.event) {
// clear sessions code here
}
};

ReactJS : How to detect network idle between XHR requests

I'm fairly new to the JS world and working with React JS. Couldn't find anything for the exact solution.
I want to implement an App level idle timer which would be activated when the user does not make any server request for a specific time period. The function should be triggered after X mins of the last XHR request irrespective of whether the user navigates between components or not.
For example - If user clicks on a button which calls an API and just plays around the screen without making another service call for X mins, the function should be triggered. At this stage, though user is not idle on the website, there's an idleness in the network activity. I want to detect this and perform some actions.
Also, would like to know the performance impact of the solution. Thanks!
Use PerformanceObserver to detect the fetch and then add/update timer using the setTimeout
let timer;
const observer = new PerformanceObserver((items) => {
items
.getEntries()
.filter(({ initiatorType }) => initiatorType === "fetch")
.forEach((entry) => {
console.log("Made fetch request", entry.name);
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => console.log("(idle) After 2 sec"), 2000);
});
});
observer.observe({
entryTypes: ["resource"],
});
fetch("https://swapi.dev/api/planets/1/").then((res) => res.json());
setTimeout(
() => fetch("https://swapi.dev/api/planets/2/").then((res) => res.json()),
1000
);
setTimeout(
() => fetch("https://swapi.dev/api/planets/3/").then((res) => res.json()),
3000
);
The Network Information Api.
I'm not sure but you can try subtract downlink from downlinkMax.
But this Api is not well supported Can i use

Scroll to bottom of the page loadMore gets called more than once

I have a page where a list of profiles are shown and when the user scrolls to the bottom of the page, the query should load more data. The loadMore function works as expected but my error is in the listener as when the user scrolls to the bottom quickly, it gets called more than 5 times.
const listener = useCallback(() => {
if (window.innerHeight + window.scrollY >= document.body.scrollHeight) {
loadMore();
}
}, [loadMore]);
useEffect(() => {
window.addEventListener('scroll', listener);
return () => window.removeEventListener('scroll', listener);
}, [listener]);
This causes the same items in the new query to be shown as it sends the loadMore the same data.
When i slowly scroll it works. When I scroll to the bottom fast loads the same profiles 5 different times. Its in the if statement of the listener.
Are you using a mac computer? If you scroll to the bottom quickly using the mac mouse, it always stops after the fraction wears out which will keeps triggering the event when the scroll position is in your detect zone.
Use debounce
Here is a good explanation of how to use debounce to prevent quick repeated triggering in your code.
or
Use a variable and a timeout
// Set a variable that indicates the state of pulling profile
var pulling_data = false;
// Ignore if it's true, otherwise et to true when users scroll into your detect zone
if (pulling_data) return;
// An example of fetching data
Ajax({
before() {
pulling_data = true;
}
}).then(res => {
// Handles data
}).catch(err => {
// Handles error
}).then(() => {
let timer_id = setTimeout(() => {
pulling_data = false;
}, 1000); // 1 second
});
In above example, no matter how quickly users repeatedly trigger to pull profile data, it always run once and make the next request only available 1 second after the current request is completed. Of course you can change the timer or not to use the timer.
Use custom event to detect when the scroll is end
Here are some examples of creating detecting if the scrolling event ends. You can trigger your fetch when the event is fired.

Is it possible to detect idle time even across tabs?

I have a script that sends a time-sensitive notification to users when there is a new question directed to them. However, I found that some people leave their computers open and go grab lunch, therefore missing notifications.
I'm looking to put together a script that detects if the user is idle for 5 minutes, and if so, it would show them as 'offline' and close down notifications.
I was curious if it is possible to detect inactivity even across tabs? (for example if a user switches to another tab to Facebook.com and stays active there, they would be seen as 'active' even though they are not on our webpage specifically).
Everything that happens when the user is NOT on your side is impossible to track (luckily).
So not this is not possible (think about the security).
UPDATE
Now that I think of it. It is possible, however very unlikely that you can do it. If your name would have been Google you would have come a long way, because lots of websites use Google analytics. But other than that: NO not possible for reasons mentioned.
Store their last activity in a database table when they are active. You can use mouse movement, keypresses, or some other activity to update the timestamp. Periodically poll that table with an ajax call on the page on which the user would see their online/offline status. If the last active time is > 5 minutes, show them as offline or idle.
if I am on such a thing I use either the HTML5 Visibility API or fallback to blur and focus events observing when the user left the page and then returns... leaving means unfocus the browser window or tab (but still keeping the page open)
but since you wanna react on inactivity... hmmm you could start a timeout (of course that would need a global event delegation for many events to stop it if something happens like submit, click, change, mousemove and so on)
Code is:
var inactivityTime = function () {
var t;
window.onload = resetTimer;
document.onmousemove = resetTimer;
document.onkeypress = resetTimer;
function logout() {
alert("You are now logged out.")
//location.href = 'logout.php'
}
function resetTimer() {
clearTimeout(t);
t = setTimeout(logout, 3000)
// 1000 milisec = 1 sec
}
};
I wanted to implement this functionality on my clients website. Didnt find any idleal solution for this in web.Finally I had to twig my code,think of some logic and implement this.The code goes as below--
`/*Put this code inside script tag whereever you want to execute the inactivity popup*/
var t;
//set the timeout period
var timeoutPeriod = '${inactiveIntervalMillis}';
//detect various events
callUserEvents();
`
//remove the logged Out storage after popup is closed by user
function removeLocalStorage() {
localStorage.removeItem("loggedOut");
}
//call this function whenever we detect user activity
function resetUserActivity() {
resetTimer();
}
//If the user is logged out and it clicks on other tabs,the popup will be displayed there too
function checkIfUserLoggedOut() {
if (localStorage.getItem("loggedOut")) {
loadLoginModal("/includes/gadgets/popup-content.jsp", 400, 230,
undefined);
}
}
// Call this method when any window onloads,this helps to check if multiple tabs are opened by same site
function incrementCounter() {
checkIfUserLoggedOut();
if (localStorage.getItem("counter") == "NaN") {
localStorage.setItem("counter", "0");
} else {
var counter = parseInt(localStorage.getItem("counter")) + 1;
localStorage.setItem("counter", counter);
}
resetTimer();
}
//after time interval,this method will be called
function handleIdleTimedOut() {
//get the current localStorage Object
window.sharedCounter = localStorage.getItem("counter");
//If no tabs are opened,then popup will be shown here.
if (window.localCounter == window.sharedCounter) {
loadLoginModal("/includes/gadgets/popup-content.jsp", 400, 230,undefined);
localStorage.setItem("loggedOut", "true");
}
}
function resetTimer() {
//save counterin current Window object,and after timeout period you can match it,if by chance multiple tabs were opened,the counter will be different,popup wont be shown in current window at incorrect time.
window.localCounter = localStorage.getItem("counter");
clearTimeout(t);
t = setTimeout(handleIdleTimedOut, timeoutPeriod);
}
function callUserEvents(){
window.onload=incrementCounter
window.onscroll = resetUserActivity;
window.onmousemove = resetUserActivity;
window.ondblclick = resetUserActivity;
window.oncontextmenu = resetUserActivity;
window.onclick = resetUserActivity;
window.onkeypress = resetUserActivity;
window.onpageshow = resetUserActivity;
window.onresize = resetUserActivity;
window.onfocus = incrementCounter;
window.ondrag = resetUserActivity;
window.oncopy = resetUserActivity;
window.oncut = resetUserActivity;
window.onpaste = resetUserActivity;
}
`

Categories

Resources