I am writing a single page javascript application using the HTML5 History API. The application loads content via Ajax and internally maintains state information on the foreground screen using a screen stack.
I want to enable navigation with the back button, but I never want to the forward button to be enabled.
A couple quick bits of information:
The user should only ever be able to go back, never forward
Pressing browser back button closes the current page screen user is on and reloads the previous one
Project is targeted towards the latest version of Chrome only, so other browsers implementations are not important
I am using native JavaScript and jQuery only, I would like to do this without History.js
When I load a new screen I run the following line:
history.pushState(screenData, window.document.title, "#");
I bind to the popstate event via jQuery:
$(window).bind("popstate", function(event) {
if (event.originalEvent.state != null) {
// Logic that loads the previous screen using my screen stack
}
});
My application's history management is working, however when I go back the forward button is enabled. I need to figure out how to remove data from history on the popstate event.
Can I do this with replaceState? I'm not sure how to go about doing this...
The accepted answer solves the problem to disable the forward button, but creates a new annoying issue "the page navigated back to" is inserted in duplicate into the history (as indicated in the answers comments).
Here is how solve the question "diabled forward button" and to avoid the "duplicate" back-button-issue.
//setup the popstate EventListener that reacts to browser history events
window.addEventListener("popstate",function(event){
// In order to remove any "forward"-history (i.e. disable forward
// button), this popstate's event history state (having been navigated
// back to) must be insert _again_ as a new history state, thereby
// making it the new most forwad history state.
// This leaves the problem that to have this popstate event's history
// state to become the new top, it would now be multiple in the history
//
// Effectively history would be:
// * [states before..] ->
// * [popstate's event.state] ->
// * [*newly pushed _duplicate_ of popstate's event.state ]
//
// To remove the annoyance/confusion for the user to have duplicate
// history states, meaning it has to be clicked at least twice to go
// back, we pursue the strategy of marking the current history state
// as "obsolete", as it has been inserted _again_ as to be the most
// forward history entry.
//
// the popstate EventListener will hence have to distinguish 2 cases:
//
// case A) "popstate event is _not_ an obsolete duplicate"...
if( typeof event.state == "object"
&& event.state.obsolete !== true)
{
//...so we _firstly_ mark this state as to from now on "obsolete",
// which can be done using the history API's replaceState method
history.replaceState({"obsolete":true},"");
// and _secondly_ push this state _one_more_time_ to the history
// which will solve the OP's desired "disable forward button" issue
history.pushState(event.state,"");
}
// case B: there is the other case that the user clicked "back" and
// encounters one of the duplicate history event entries that are
// "obsolete" now.
if( typeof event.state == "object"
&& event.state.obsolete === true)
{
//...in which case we simply go "back" once more
history.back()
// by this resolving the issue/problem that the user would
// be counter-intuively needing to click back multiple times.
// > we skip over the obsolete duplicates, that have been the
// the result of necessarily pushing a history state to "disable
// forward navigation"
}
},false);
Bad Part
To really disable the forward button, you would have to be able to delete browser history, which is not allowed by all javascript implementations because it would allow sites to delete the entire history, which would never be in the interest of the user.
Good Part
This is a bit tricky, but I guess it could work if you want to do custom history. You could just use pushState in the popstate event to make your actual page the topmost history entry. I assume the way you handle your history, your window will never unload. This allows you to keep track of the user history yourself:
var customHistory = [];
Push every page you load with history.pushState(screenData, window.document.title, "#");, like you did before. Only you add the state to your custom history, too:
history.pushState(screenData, window.document.title, "#");
customHistory.push({data: screenData, title: window.document.title, location: '#'});
now if you have a popstate event, you just pop you custom history and push it to the topmost entry:
window.onpopstate = function(e) {
var lastEntry = customHistory.pop();
history.pushState(lastEntry.data, lastEntry.title, lastEntry.location);
// load the last entry
}
Or in jQuery
$(window).on('popstate', function(e) {
var lastEntry = customHistory.pop();
history.pushState(lastEntry.data, lastEntry.title, lastEntry.location);
// load the last entry
});
Just use following jquery to disable forward button:
$( document ).ready( function(){
history.pushState(null, document.title, location.href);
});
NOTE:
This code was tested and worked fine without showing any problems, however
I would incentivize developers to test it more before going to production with the code.
If HTML5 history.replaceState() is used anywhere in your application,
the code below might now work.
I created a custom function in order to disable the forward button.
Here is the code (it doesn't work with the hash routing strategy):
<script>
(function() {
// function called after the DOM has loaded
disableForwardButton();
})();
function disableForwardButton() {
var flag, loop = false;
window.addEventListener('popstate', function(event) {
if (flag) {
if (history.state && history.state.hasOwnProperty('page')) {
loop = true;
history.go(-1);
} else {
loop = false;
history.go(-1);
}
} else {
history.pushState({
page: true
},
null,
null
);
}
flag = loop ? true : !flag;
});
window.onclick = function(event) {
flag = false;
};
}
</script>
As Redrif pointed out in the comments of the accepted answer, the problem is that you have to double click the back button in order to navigate back to the page which is tedious and impractical.
Code explanation: each time you click the back button you need to create an additional history element so that the the current page which you are located on
points to the newly created history page. In that way there is no page to go forward to since the pushState is the last state (picture it as the last element in the array) therefore your forward button will always be disabled.
The reason why it was mandatory to introduce the loop variable is because you can have a scenario where you go back to a certain page and the pushState code occurs which creates the last history element and instead going back again you choose to click on some link again and again go back the previous page which now creates an additional history element. In other words, you have something like this:
[page1, page2, page2, page2]
now, once on page2 (index 3 element) and you click the back button again you will get to the page2 again index 1 element and you do not want that. Remember that you can have an array of x page2 elements hence the loop false variable was introduced to resolve that particular case, with it you jump all the way from page2 to page 1 no matter how many page2 elements are their in the array.
Related
Hi have the following jQuery event:
listItems.on('click', function(ev) {
ev.preventDefault();
reload = true;
var url = jQuery(this).attr('data-url');
history.pushState({}, '', url);
...
}
Which load dynamic content via AJAX and pushes history state to modify URL in browser from www.domain.com to www.domain.com/page-x. The only reason im loading content via AJAX is that i need to animate page transitions. If you go to www.domain.com/page-x you get normal HTML page.
The problem occurs if after clicking listItem user clicks back button on it's browser. The url changes back from www.domain.com/page-x to www.domain.com, but the page doesn't reload. Thats why i added this event:
window.onpopstate = function() {
if (reload == true) {
reload = false;
location.reload();
}
}
It reloads page after url has changed, if current page was loaded via AJAX. It's working fine, but now i have other problem:
User in frontpage clicks list item;
Browser URL changes, content is loaded via AJAX and animated;
User clicks BACK browser button;
Browser URL changes to previous and page is reloaded;
User clicks FORWARD browser button, URL changes but nothing happens;
Any ideas/solutions would be highly appreciated.
Rather than using reload=true you should rely on event.state so that when popstateoccurs you get a snapshot of what you recorded for that URL.
Manipulating the browser history (MDN)
For example:
listItems.on('click', function(ev) {
ev.preventDefault();
var url = jQuery(this).attr('data-url');
history.pushState({ action: 'list-item-focused' }, '', url);
...
}
And then:
window.onpopstate = function(e) {
/// this state object will have the action attribute 'list-item-focused'
/// when the user navigates forward to the list item. Meaning you can
/// do what you will at this point.
console.log(e.state.action);
}
You probably should avoid a full page reload when the user hits back, and instead animate your content back that used to be there, that way you aren't trashing the page. You can then tell the difference between the landing page and the list pages by checking for event.state.action and code your responses accordingly.
window.onpopstate = function(e) {
if ( e.state.action == 'list-item-focused') {
/// a list item is focused by the history item
}
else {
/// a list item isn't focused, so therefore landing page
/// obviously this only remains true whilst you don't
/// make further pushed states. If you do, you will have to
/// extend your popstate to take those new actions into account.
}
}
I'm sure you are aware of this, but pushState isn't fully cross-browser, so you should also anticipate what could happen for users if these methods aren't supported.
further enhancements
Also, as you are using jQuery, it makes it quite easy for your to store further useful information in the state object, that may help you enhance your reactions in popstate:
history.pushState({
action: 'list-item-focused',
listindex: jQuery(this).index()
});
You have to bear in mind that any data you store is serialised, meaning that it will most likely be converted to some form of non-interactive string or binary data. This means you can't store references to elements or other "live" instances; hence the fact I'm storing the list items index instead.
With the above you now know what action was occurring and on what list item, which you can retrieve at the other end by using:
listItems.eq(event.state.listindex)
what to do onload?
MDN has this to say about what you should do onload.
Reading the current state
When your page loads, it might have a non-null state object. This can happen, for example, if the page sets a state object (using pushState() or replaceState()) and then the user restarts her browser. When your page reloads, the page will receive an onload event, but no popstate event. However, if you read the history.state property, you'll get back the state object you would have gotten if a popstate had fired.
You can read the state of the current history entry without waiting for a popstate event using the history.state property like this:
Put simply they are just recommending that you should listen out for the current history.state when the page loads, and act accordingly based on what your state.action describes. This way you support both events that are triggered within a page's life-time i.e. popstate and when a user directly jumps to a history state which causes a page load.
So with the above in mind it would probably be best to structure things like so:
window.onpopstate = function(e) {
reactToState(e.state);
}
window.onload = function(){
history.state && reactToState(history.state);
}
var reactToState = function(state){
if ( state.action == 'list-item-focused') {
/// a list item is focused by the history item
}
else {
/// a list item isn't focused, so therefore landing page
/// obviously this only remains true whilst you don't
/// make further pushed states. If you do, you will have to
/// extend your popstate to take those new actions into account.
}
}
I've used inline event listeners for simplicity and because your examples do too, however it would be advised to use the addEventListener and attachEvent (IE only) methods instead... or better still, because you are using jQuery.
jQuery(window)
.on('popstate', function(e) {
reactToState(e.originalEvent.state);
}
.on('load', function(){
history.state && reactToState(history.state);
}
;
switching states
Obviously in order to move between two states, you need to know what both those states are; this means having some way to record your current state — so you can compare with the new state and act accordingly. As you only have two states this may not be imperative, but I'd urge you to always be thinking forward about the possibility of having more complexity than you currently have.
I do not know the layout of your code, so it makes it tricky recommending where you should place variables and other such items. However, no matter how you store the information, it doesn't change the fact that it will make your life and code easier if you do:
var reactToState = function(state){
var currentState = reactToState.currentState || {};
if ( !currentState.action ) { currentState.action = 'start'; }
if ( state.action == 'list-item-focused') {
if ( currentState.action == 'start' ) {
/// here we know we are shifting from the start page to list-item
}
}
else {
if ( currentState.action == 'list-item-focused' ) {
/// here we know we are shifting from the list-item to the start
}
}
/// as the state will be global for your application there is no harm
/// in storing the current state on this function as a "static" attribute.
reactToState.currentState = state;
}
Better yet, if you're not averse to switch statements, you can make the above more readable:
var reactToState = function(state){
/// current state with fallback for initial state
var currentState = reactToState.currentState || {action: 'start'};
/// current to new with fallback for empty action i.e. initial state
var a = (currentState.action||'start');
var b = (state.action||'start');
switch ( a + ' >> ' + b ) {
case 'start >> list-item-focused':
/// animate and update here
break;
case 'list-item-focused >> start':
/// animate and update here
break;
}
/// remember to store the current state again
reactToState.currentState = state;
}
I'm trying out the HTML5 history API with ajax loading of content.
I've got a bunch of test pages connected by relative links. I have this JS, which handles clicks on those links. When a link is clicked the handler grabs its href attribute and passes it to ajaxLoadPage(), which loads content from the requested page into the content area of the current page. (My PHP pages are set up to return a full HTML page if you request them normally, but only a chunk of content if ?fragment=true is appended to the URL of the request.)
Then my click handler calls history.pushState() to display the URL in the address bar and add it to the browser history.
$(document).ready(function(){
var content = $('#content');
var ajaxLoadPage = function (url) {
console.log('Loading ' + url + ' fragment');
content.load(url + '?fragment=true');
}
// Handle click event of all links with href not starting with http, https or #
$('a').not('[href^=http], [href^=https], [href^=#]').on('click', function(e){
e.preventDefault();
var href = $(this).attr('href');
ajaxLoadPage(href);
history.pushState({page:href}, null, href);
});
// This mostly works - only problem is when popstate happens and state is null
// e.g. when we try to go back to the initial page we loaded normally
$(window).bind('popstate', function(event){
console.log('Popstate');
var state = event.originalEvent.state;
console.log(state);
if (state !== null) {
if (state.page !== undefined) {
ajaxLoadPage(state.page);
}
}
});
});
When you add URLs to the history with pushState you also need to include an event handler for the popstate event to deal with clicks on the back or forward buttons. (If you don't do this, clicking back shows the URL you pushed to history in the address bar, but the page isn't updated.) So my popstate handler grabs the URL saved in the state property of each entry I created, and passes it to ajaxLoadPage to load the appropriate content.
This works OK for pages my click handler added to the history. But what happens with pages the browser added to history when I requested them "normally"? Say I land on my first page normally and then navigate through my site with clicks that do that ajax loading - if I then try to go back through the history to that first page, the last click shows the URL for the first page, but doesn't load the page in the browser. Why is that?
I can sort of see this has something to do with the state property of that last popstate event. The state property is null for that event, because it's only entries added to the history by pushState() or replaceState() that can give it a value. But my first loading of the page was a "normal" request - how come the browser doesn't just step back and load the initial URL normally?
This is an older question but there is a much simpler answer using native javascript for this issue.
For the initial state you should not be using history.pushState but rather history.replaceState.
All arguments are the same for both methods with the only difference is that pushState creates a NEW history record and thus is the source of your problem. replaceState only replaces the state of that history record and will behave as expected, that is go back to the initial starting page.
I ran into the same issue as the original question. This line
var initialPop = !popped && location.href == initialURL;
should be changed to
var initialPop = !popped;
This is sufficient to catch the initial pop. Then you do not need to add the original page to the pushState. i.e. remove the following:
var home = 'index.html';
history.pushState({page:home}, null, home);
The final code based on AJAX tabs (and using Mootools):
if ( this.supports_history_api() ) {
var popped = ('state' in window.history && window.history.state !== null)
, changeTabBack = false;
window.addEvent('myShowTabEvent', function ( url ) {
if ( url && !changingTabBack )
setLocation(url);
else
changingTabBack = false;
//Make sure you do not add to the pushState after clicking the back button
});
window.addEventListener("popstate", function(e) {
var initialPop = !popped;
popped = true;
if ( initialPop )
return;
var tabLink = $$('a[href="' + location.pathname + '"][data-toggle*=tab]')[0];
if ( tabLink ) {
changingTabBack = true;
tabLink.tab('show');
}
});
}
I still don't understand why the back button behaves like this - I'd have thought the browser would be happy to step back to an entry that was created by a normal request. Maybe when you insert other entries with pushState the history stops behaving in the normal way. But I found a way to make my code work better. You can't always depend on the state property containing the URL you want to step back to. But stepping back through history changes the URL in the address bar as you would expect, so it may be more reliable to load your content based on window.location. Following this great example I've changed my popstate handler so it loads content based on the URL in the address bar instead of looking for a URL in the state property.
One thing you have to watch out for is that some browsers (like Chrome) fire a popstate event when you initially hit a page. When this happens you're liable to reload your initial page's content unnecessarily. So I've added some bits of code from the excellent pjax to ignore that initial pop.
$(document).ready(function(){
// Used to detect initial (useless) popstate.
// If history.state exists, pushState() has created the current entry so we can
// assume browser isn't going to fire initial popstate
var popped = ('state' in window.history && window.history.state !== null), initialURL = location.href;
var content = $('#content');
var ajaxLoadPage = function (url) {
console.log('Loading ' + url + ' fragment');
content.load(url + '?fragment=true');
}
// Handle click event of all links with href not starting with http, https or #
$('a').not('[href^=http], [href^=https], [href^=#]').on('click', function(e){
e.preventDefault();
var href = $(this).attr('href');
ajaxLoadPage(href);
history.pushState({page:href}, null, href);
});
$(window).bind('popstate', function(event){
// Ignore inital popstate that some browsers fire on page load
var initialPop = !popped && location.href == initialURL;
popped = true;
if (initialPop) return;
console.log('Popstate');
// By the time popstate has fired, location.pathname has been changed
ajaxLoadPage(location.pathname);
});
});
One improvement you could make to this JS is only to attach the click event handler if the browser supports the history API.
I actually found myself with a similar need today and found the code you provided to be very useful. I came to the same problem you did, and I believe all that you're missing is pushing your index file or home page to the history in the same manner that you are all subsequent pages.
Here is an example of what I did to resolve this (not sure if it's the RIGHT answer, but it's simple and it works!):
var home = 'index.html';
history.pushState({page:home}, null, home);
Hope this helps!
I realize this is an old question, but when trying to manage state easily like this, it might be better to take the following approach:
$(window).on('popstate',function(e){
var state = e.originalEvent.state;
if(state != null){
if(state.hasOwnProperty('window')){
//callback on window
window[state.window].call(window,state);
}
}
});
in this way, you can specify an optional callback function on the state object when adding to history, then when popstate is trigger, this function would be called with the state object as a parameter.
function pushState(title,url,callback)
{
var state = {
Url : url,
Title : title,
};
if(window[callback] && typeof window[callback] === 'function')
{
state.callback = callback;
}
history.pushState(state,state.Title,state.Url);
}
You could easily extend this to suit your needs.
And Finally says:
I'd have thought the browser would be happy to step back to an entry that was created by a normal request.
I found an explanation of that strange browser's behavior here. The explanation is
you should save the state when your site is loaded the first time and thereafter every time it changes state
I tested this - it works.
It means there is no need in loading your content based on window.location.
I hope I don't mislead.
I'm building a tool that uses AJAX and pushState/replaceState on top of a non-javascript fallback (http://biologos.org/resources/find). Basically, it's a search tool that returns a list of real HTML links (clicking a link takes you out of the tool).
I am using onpopstate so the user can navigate through their query history created by pushState. This event also fires when navigating back from a real link (one NOT created with pushState but by actual browser navigation). I don't want it to fire here.
So here's my question: how can I tell the difference between a onpopstate event coming from a pushState history item, vs one that comes from real navigation?
I want to do something like this:
window.onpopstate = function(event){
if(event.realClick) return;
// otherwise do something
}
I've tried onpopstate handler - ajax back button but with no luck :(
Thanks in advance!
EDIT:
A problem here is the way different browsers handle the onpopstate event. Here's what seems to be happening:
Chrome
Fires onpopstate on both real and virtual events
Actually re-runs javascript (so setting loaded=false will actually test false)
The solution in the above link actually works!
Firefox
Only fires onpopstate on virtual events
Actually re-runs javascript (so setting loaded=false will actually test false)
For the linked solution to actually work, loaded needs to be set true on page load, which breaks Chrome!
Safari
Fires onpopstate on both real and virtual events
Seems to NOT re-run javascript before the event (so loaded will be true if previously set to be true!)
Hopefully I'm just missing something...
You may be able to use history.js. It should give you an API that behaves consistently across all major platforms (though it's possible that it does not address this specific issue; you'll have to try it to find out).
However, in my opinion, the best way to handle this (and other related issues too) is to design your application in such a way that these issues don't matter. Keep track of your application's state yourself, instead of relying exclusively on the state object in the history stack.
Keep track of what page your application is currently showing. Track it in a variable -- separate from window.location. When a navigation event (including popstate) arrives, compare your known current page to the requested next page. Start by figuring out whether or not a page change is actually required. If so, then render the requested page, and call pushState if necessary (only call pushState for "normal" navigation -- never in response to a popstate event).
The same code that handles popstate, should also handle your normal navigation. As far as your application is concerned, there should be no difference (except that normal nav includes a call to pushState, while popstate-driven nav does not).
Here's the basic idea in code (see the live example at jsBin)
// keep track of the current page.
var currentPage = null;
// This function will be called every time a navigation
// is requested, whether the navigation request is due to
// back/forward button, or whether it comes from calling
// the `goTo` function in response to a user's click...
// either way, this function will be called.
//
// The argument `pathToShow` will indicate the pathname of
// the page that is being requested. The var `currentPage`
// will contain the pathname of the currently visible page.
// `currentPage` will be `null` if we're coming in from
// some other site.
//
// Don't call `_renderPage(path)` directly. Instead call
// `goTo(path)` (eg. in response to the user clicking a link
// in your app).
//
function _renderPage(pathToShow) {
if (currentPage === pathToShow) {
// if we're already on the proper page, then do nothing.
// return false to indicate that no actual navigation
// happened.
//
return false;
}
// ...
// your data fetching and page-rendering
// logic goes here
// ...
console.log("renderPage");
console.log(" prev page : " + currentPage);
console.log(" next page : " + pathToShow);
// be sure to update `currentPage`
currentPage = pathToShow;
// return true to indicate that a real navigation
// happened, and should be stored in history stack
// (eg. via pushState - see `function goTo()` below).
return true;
}
// listen for popstate events, so we can handle
// fwd/back buttons...
//
window.addEventListener('popstate', function(evt) {
// ask the app to show the requested page
// this will be a no-op if we're already on the
// proper page.
_renderPage(window.location.pathname);
});
// call this function directly whenever you want to perform
// a navigation (eg. when the user clicks a link or button).
//
function goTo(path) {
// turn `path` into an absolute path, so it will compare
// with `window.location.pathname`. (you probably want
// something a bit more robust here... but this is just
// an example).
//
var basePath, absPath;
if (path[0] === '/') {
absPath = path;
} else {
basePath = window.location.pathname.split('/');
basePath.pop();
basePath = basePath.join('/');
absPath = basePath + '/' + path;
}
// now show that page, and push it onto the history stack.
var changedPages = _renderPage(absPath);
if (changedPages) {
// if renderPage says that a navigation happened, then
// store it on the history stack, so the back/fwd buttons
// will work.
history.pushState({}, document.title, absPath);
}
}
// whenever the javascript is executed (or "re-executed"),
// just render whatever page is indicated in the browser's
// address-bar at this time.
//
_renderPage(window.location.pathname);
If you check out the example on jsBin, you'll see that the _renderPage function is called every time the app requests a transition to a new page -- whether it's due to popstate (eg. back/fwd button), or it's due to calling goTo(page) (eg. a user action of some sort). It's even called when the page first loads.
Your logic, in the _renderPage function can use the value of currentPage to determine "where the request is coming from". If we're coming from an outside site then currentPage will be null, otherwise, it will contain the pathname of the currently visible page.
With unload event of window, it is possible to show the user a confirmation dialog , let's say in a situation where there is an ongoing request you are waiting for to finish and navigating away from the page will terminate that request.
Is there a way to accomplish this with onpopstate of HTML5 history API? Or any other way with the same outcome?
I guess you could modify the behavior of pushState to ask for confirmation before pushing a new state :
// Store original pushState
var _pushState = window.history.pushState;
// Some bad global variable to determine if confirmation is needed
var askForConfirm = true;
// Modify pushState behavior
window.history.pushState = function() {
if(!askForConfirm || confirm('Are you sure you want to quit this page ?')) {
// Call original pushState
_pushState.apply(window.history,arguments);
}
};
I don't know if it helps in your situation, but Sammy.js, a popular hash-routing library has a before handler. I've used it in my application to record the previously accessed hash, and if it's the hash I want to stop them from leaving, return false will keep them on that page. You still need to rewrite the URL to display the previous page, but it seems to be working.
See my answer in this other thread for more info.
Is it possible check if there is a value for history.go(-1)? I know you can't access history.previous directly.
I am trying to stay away from document.referrer because I know it can be blocked in some instances.
Here is what I am trying to do. I have an error page, on this page I would like to either have a BACK button (if it's not the only item in history) or a close button (if it is).
if (history.length) {
//There is history to go back to
history.go(-1);
}
Actually, history.length is always one or more, since the current page counts. Also, if you have a forward history (i.e. you used the back button), those pages also count. So you need a more complicated check:
if( (1 < history.length) && document.referrer ) {
There is no cross-browser approach to accomplish this. Document.Referrer may be set even if no history entry exists.
I came up with the following "hack". It utilizes the onbeforeunload event to detect whether the browser starts leaving the page or not. If it does not in a certain timespan it'll just redirect to the fallback.
window.goBack = function goBack(fallback){
var useFallback = true;
window.onbeforeunload = function(){
useFallback = false;
}
window.history.back();
setTimeout(function(){
if (useFallback){ window.location.href = fallback; }
}, 100);
}
You can call this function using goBack("fallback.example.org").
One of the use cases is that you may want to add a back button to any page and also want to make sure that this back button works even if the user goes directly to this page (e.g. by bookmark, direct link etc).
So either it does perform a history.back() or if there is no entry, it'll redirect to a fallback.
If the history has a length greater than 0, then it contains at least one history point.
if (history.length)
function test() {
document.URL = document.referrer;
}