Detecting Page Refreshes :: Using JavaScript on Client-Side



Motivation

I was once asked to add a feature to the highlighting script so that highlighting would be turned off when a page was refreshed. I could not think of a simple way to detect when a page was refreshed, and when I searched on-line I found that not many other people had come up with good solutions to this problem.

I ended up coming up with my own way to detect refreshes using cookies and implemented the search highlighting script so that refreshes would toggle highlighting on and off. See the highlighting script for more details. Here I give a common method using JavaScript and hidden form variables which I did not use as well as a unique (as far as I can tell) method that uses JavScript and cookies that I did use.

The Common Approach: Hidden Form Variables

In most web browsers, hidden form variables are maintained across refreshes. That is, even though a page is refreshed, if a hidden form variable was changed by JavaScript before the refresh, the changed value will be used after the refresh. Thus, on a page load JavaScript can check to see if a hidden variable that defaults to being unset is set. If it is unset, it assumes this is not a refresh and it sets it. If it is set, it assumes this is a refresh.

An example page that implements this might look like the following.

<html>
<head>

<script language="JavaScript"> <!--
function checkRefresh()
{
	if( document.refreshForm.visited.value == "" )
	{
		// This is a fresh page load
		document.refreshForm.visited.value = "1";

		// You may want to add code here special for
		// fresh page loads
	}
	else
	{
		// This is a page refresh

		// Insert code here representing what to do on
		// a refresh
	}
} -->
</script>

</head>

<body onLoad="JavaScript:checkRefresh();">

<form name="refreshForm">
<input type="hidden" name="visited" value="" />
</form>

</body>
</html>

However, the implementation of this seems to change from web browser to web browser and even from version to version. In some web browsers, this works with a refresh but it does not work with a reset (a "shift-refresh") while in others it works with both. Plus, the bulk of the coding for this method is buried in the body of the web page rather than in JavaScript that can be included in a single line in the header.

The Approach I Use: Session Cookies

I prefer not to use the above method. For one, its behavior is going to vary from web browser to web browser. Additionally, it requires a bulk of code in the body of the web page. I would prefer a method that can be wrapped up almost entirely in a <script>...</script> so that I can tuck it away in a .js file and only have to add a <body> event handler.

So I use JavaScript cookies. On a page unload, the web page sets a cookie to the name of the web page and another cookie to the current time in seconds. Then, on page load, the web page retrieves those cookies and checks the current time. If the name cookie matches the name of the page and the time cookie differs by the current time by no more than 5 seconds, then the load is assumed to be a refresh. Otherwise it is assumed to be a fresh load. In the case where the page posts data back to itself, it can prevent the setting of the cookie so that the next load won't look like a refresh. This method is longer, but I believe it is tighter and more robust.

An example page that implements this might look like the following.

<html>
<head>

<script language="JavaScript"> <!--
function checkRefresh()
{
	// Get the time now and convert to UTC seconds
	var today = new Date();
	var now = today.getUTCSeconds();

	// Get the cookie
	var cookie = document.cookie;
	var cookieArray = cookie.split('; ');

	// Parse the cookies: get the stored time
	for(var loop=0; loop < cookieArray.length; loop++)
	{
		var nameValue = cookieArray[loop].split('=');
		// Get the cookie time stamp
		if( nameValue[0].toString() == 'SHTS' )
		{
			var cookieTime = parseInt( nameValue[1] );
		}
		// Get the cookie page
		else if( nameValue[0].toString() == 'SHTSP' )
		{
			var cookieName = nameValue[1];
		}
	}

	if( cookieName &&
		cookieTime &&
		cookieName == escape(location.href) &&
		Math.abs(now - cookieTime) < 5 )
	{
		// Refresh detected

		// Insert code here representing what to do on
		// a refresh

		// If you would like to toggle so this refresh code
		// is executed on every OTHER refresh, then 
		// uncomment the following line
		// refresh_prepare = 0; 
	}	

	// You may want to add code in an else here special 
	// for fresh page loads
}

function prepareForRefresh()
{
	if( refresh_prepare > 0 )
	{
		// Turn refresh detection on so that if this
		// page gets quickly loaded, we know it's a refresh
		var today = new Date();
		var now = today.getUTCSeconds();
		document.cookie = 'SHTS=' + now + ';';
		document.cookie = 'SHTSP=' + escape(location.href) + ';';
	}
	else
	{
		// Refresh detection has been disabled
		document.cookie = 'SHTS=;';
		document.cookie = 'SHTSP=;';
	}
}

function disableRefreshDetection()
{
	// The next page will look like a refresh but it actually
	// won't be, so turn refresh detection off.
	refresh_prepare = 0;

	// Also return true so this can be placed in onSubmits
	// without fear of any problems.
	return true;
} 

// By default, turn refresh detection on
var refresh_prepare = 1;

-->
</script>

</head>

<body onLoad="JavaScript:checkRefresh();" onUnload="JavaScript:prepareForRefresh();">

<!-- The above is all that is needed. -->

<!-- Below is an example of the use of disableRefreshDetection() 
     to prevent false refreshes from being detected when forms 
     are submitted back to this page. If your web page does
     not use forms that post back to themselves, then the
     below does not matter to you. Omit it. -->

<!-- This is a dummy form. Focus on the onSubmit. -->

<form onSubmit="JavaScript:disableRefreshDetection()">
<input type="submit" />
</form>

</body>
</html>

The above example is long and there are chances that it will catch some false positives, but in my experience it is more consistent than other solutions. Additionally, nearly all false positives can be prevented with the disableRefreshDetection() function. However, there are still times when a page reset may be needed and most page resets will look like page refreshes to this script as well. Note that the first method that uses hidden variables can still have this same problem depending on the web browser.