Page reloads are a thing. Sometimes we refresh a page when we think it’s unresponsive, or believe that new content is available. Sometimes we’re just mad at the dang site and rage-refresh to let it know we’re displeased.
Wouldn’t be nice to know when a user refreshes the page? Not just that, but how many times? That data can help us trigger some sort of behavior after a certain number of reloads.
A sports site is a good example. If I want to check the score of a game that’s in progress but the scores aren’t live-updated, then I might find myself refreshing a bunch.
Our goal is to break users out of that habit. We’ll use our page-refresh-counting powers to let folks know that refreshes are unnecessary, thanks to real-time score updates. And if they reload more than three times? We’ll kick ‘em out of their session. That’ll show them.
Here’s a simple demo of that concept.
Let’s re-create it together. But before we get going, there are few questions we need to answer before we start coding:
- How can we persist the number of times user reloaded the site? We need a place to keep the number of times user reloaded the site (
reloadCount
), this place needs to persist that value between the reloads —localStorage
sounds like a good solution. - How do we detect if user reloaded the site or just came back after few hours? If we store the
reloadCount
inlocalStorage
it will persist the value between the reloads, but it will keep that value until we remove programmatically or clear the browser storage. It means that if we come back after few hours the site will still remember lastreloadCount
and may perform logout after first refresh without warning. We want to avoid that and allow user to reload the site two times each time the user comes back after some period of time. That last sentence holds the answer to the question. We need to store the time when the user left the site and then when the site loads again check when that happened. If that time period wasn’t long enough, we activate the reload counting logic. - How do we know when the user leaves the site? To store that time, we use
beforeunload
window event and store that value inlocalStorage
.
OK, now that we have the answers, let’s dive into the code.
Step 1: We’ve gotta store the last reload time
We will store the time of last reload using a beforeunload
window event. We need two things: (1) an event listener that will listen to the event and fire the appropriate method, and (2) our beforeUnloadHandler
method.
First, let’s create a function called initializeReloadCount
that will set our event listener using the addEventListener
method on the window object.
function initializeReloadCount() {
window.addEventListener("beforeunload", beforeUnloadHandler)
}
Then we create a second method that will be fired before we leave the site. This method will save the time the refresh happens in localStorage
.
function beforeUnloadHandler() {
localStorage.setItem("lastUnloadAt", Math.floor(Date.now() / 1000))
window.removeEventListener("beforeunload", beforeUnloadHandler);
}
Step 2: We need a way to handle and store the reload count
Now that we have the time when the site was last closed, we can proceed and implement logic that’s responsible for detecting and counting how many times the site was reloaded. We need a variable to hold our reloadCount
and tell us how many times user reloaded the site.
let reloadCount = null
Then, in our initializeReloadCount
function, we need to do two things:
- Check if we already have a
reloadCount
value stored in ourlocalStorage
, and if so, get that value and save it in ourreloadCount
. If the value doesn’t exist, it means that the user loaded the site for the first time (or at least did not reload it). In that case, we set thereloadCount
to zero and save that value tolocalStorage
. - Detect if the site was reloaded or the user came back to the site after longer period of time. This is the place where we need our
lastUnloadAt
value. To detect if the site was actually reloaded, we need to compare the time when the site gets loaded (the current time) with thelastUnloadAt
value. If those two happened within, say, five seconds (which is totally arbitrary), that means the user reloaded the site and we should run reload count logic. If the time period between those two events is longer, we reset thereloadCount
value.
With that, let’s create a new function called checkReload
and keep that logic there.
function checkReload() {
if (localStorage.getItem("reloadCount")) {
reloadCount = parseInt(localStorage.getItem("reloadCount"))
} else {
reloadCount = 0
localStorage.setItem("reloadCount", reloadCount)
}
if (
Math.floor(Date.now() / 1000) - localStorage.getItem("lastUnloadAt") <
5
) {
onReloadDetected()
} else {
reloadCount = 0;
localStorage.setItem("reloadCount", reloadCount)
}
}
The last function we need in this step is a method responsible for what happens when we confirm that the user reloaded the site. We call that function onReloadDetected
, and inside it, we increment the value of reloadCount
. If the user refreshed the site third time, we drop the bomb and call our logout
logic.
function onReloadDetected() {
reloadCount = reloadCount + 1
localStorage.setItem("reloadCount", reloadCount)
if (reloadCount === 3) {
logout()
}
}
Step 3: “Dear user, why you didn’t listen?!”
In this step, we implement the logic responsible for the situation when the user reloads the site to the point of breaching our three-limit threshold, despite our clear warnings to stop doing it.
When that happens, we call our API to log the user out, then we clean up all properties related to the reload count logic. That will allow the user to come back and have a clean account of reloads. We can also redirect the user somewhere useful, like the login screen. (But wouldn’t it be funny to send them here instead?)
function logout(params) {
// logout API call
resetReloadCount()
}
function resetReloadCount() {
window.removeEventListener("beforeunload", beforeUnloadHandler)
localStorage.removeItem("lastUnloadAt")
localStorage.removeItem("reloadCount");
}
Bonus: Let’s re-Vue it!
Now that we have the logic implemented, let’s see how can move that logic to a Vue site based on this example:
First, we need to move all of our variables into our component’s data
, which is where all reactive props live.
export default {
data() {
return {
reloadCount: 0,
warningMessages: [...]
}
},
Then we move all our functions to methods
.
// ...
methods: {
beforeUnloadHandler() {...},
checkReload() {...},
logout() {...},
onReloadDetected() {...},
resetReloadCount() {...},
initializeReloadCount() {...}
}
// ...
Since we are using Vue and its reactivity system, we can drop all direct DOM manipulations (e.g. document.getElementById("app").innerHTML
) and depend on our warningMessages
data property. To display the proper warning message we need to add a computed property that will re-calculate each time our reloadCount
is changed so that we can return a string from our warningMessages
.
computed: {
warningMessage() {
return this.warningMessages[this.reloadCount];
}
},
Then we can access our computed property directly in the component’s template.
<template>
<div id="app">
<p>{{ warningMessage }}</p>
</div>
</template>
Last thing we need to do is find a proper place to activate the reload prevention logic. Vue comes with component lifecycle hooks that are exactly what we need, specifically the created
hook. Let’s drop that in.
// ...
created() {
this.initializeReloadCount();
},
// ...
Nice.
Wrapping up
And there it is, the logic that checks and counts how many times a page has been refreshed. I hope you enjoyed the ride and you find this solution useful or at least inspiring to do something better. 🙂
Nice guide.
It seems like you could save a lot of trouble by just using sessionStorage instead of faking it with localStorage.
I don’t think sessionStorage will work as each time user reloads a new session will be generated. Please correct me if I’m wrong.
sessionStorage survives page reloads. From the MDN docs:
And the problem with reloads is……? Surely your extra code, served to everyone, negates most or all of any savings?
Agreed. Not sure why I would care if the user refreshes twice or 200 times.
The problem with forced reloads is that they might trigger some ‘execution’ in a backend again and again.
Therefore it is recommendet to consider some meaningful responds towards the user and probably the backend, in case of a reload.
Of course, it all depends on the application and the architecture.
I don’t think this is the way to break the habit. The user reloads for some reason. If he starts reloading a bunch, that means he feels that the UI isn’t updating, or responding to his actions. Reload is an easy fix for most js-related bugs where a page ends up in the broken state. IMHO, we should show that the page is responsive (no, not in the context of browser screen size, the other responsive). Show him, that you are checking stuff, even if this is not changing app state by itself.
And of course, if the reloads are a problem for you, you should already be looking for the helpers, which would lessen reload time and the server-side costs – query performance, fast caching layer, or even horizontal scalability if your server is that close to being overloaded, that even several consecutive reloads would impact it.
Finally, saying to the user “you are not allowed to reload the page” would not change his habits. You might end up even annoying him. As I said, he is doing that for the reason, and in most cases, he’d feel justified to do so. If I saw such warning, especially after I experienced app crashing, connectivity issues, or UI not being updated I’d rather rage quit and find something that just works.
I’d say, the only thing that would justify such mechanisms DOS and DDOS protection, but that is already built into stuff like Cloudflare.
This might have just been for demo purposes but kicking your users out for not “behaving appropriately” is probably never the right solution. Not to mention, it’s probably discriminatory since the less technically literate people are way more likely to be affected.
The ideal solution here seems to be a user interface and experience that encourages the user not to reload implicitly. This might mean having “last updates at 1:22pm” with a refreshing indicator.
I totally agree. This is extremely hostile to the user, and while it serves as an okay technical example, it would be much nicer replaced with something else; perhaps a tutorial on WebSockets to update the page and keep it live? Capturing Ctrl+R to reload the content without refreshing the whole page? Demonstrating how to greet return users and show them what’s new since their last session?
Using Vue.js for this seems like overkill to me.
Here is a codepen with the above code included in a simple HTML , with some minor tweaking.
Regards
“And if they reload more than three times? We’ll kick ‘em out of their session. That’ll show them.”
I’m hoping this really was a joke? Please.