301

How would I have a JavaScript action that may have some effects on the current page but would also change the URL in the browser so if the user hits reload or bookmark, then the new URL is used?

It would also be nice if the back button would reload the original URL.

I am trying to record JavaScript state in the URL.

Steven Noble
  • 9,304
  • 12
  • 42
  • 56
  • 1
    This would be so nice. Of course it would be limited to same-domain modifications. But some client-side control of the path (and not just hash) is a logical step now that page reloads are a kind of "last resort" for many apps. – harpo Mar 26 '10 at 17:25
  • 1
    A "sort-of" good use of `pushState`: `for(i=1;i<50;i++){var txt="..................................................";txt=txt.slice(0,i)+"HTML5"+txt.slice(i,txt.length);history.pushState({}, "html5", txt);}` – Derek 朕會功夫 Mar 07 '12 at 03:34
  • example of this effect in action: http://www.dujour.com/ – Ben Aug 31 '12 at 17:26
  • 2
    Example of this effect: facebook.com (When opening images in the lightbox) –  Nov 07 '12 at 12:06
  • this is a good question. however, its a sad situation in that if thiis question had been asked in this way today, it would have been downvoted and jumped on by the "this is not the kind of site you get people to write all your code for you". – dewd May 04 '14 at 08:49

14 Answers14

187

If you want it to work in browsers that don't support history.pushState and history.popState yet, the "old" way is to set the fragment identifier, which won't cause a page reload.

The basic idea is to set the window.location.hash property to a value that contains whatever state information you need, then either use the window.onhashchange event, or for older browsers that don't support onhashchange (IE < 8, Firefox < 3.6), periodically check to see if the hash has changed (using setInterval for example) and update the page. You will also need to check the hash value on page load to set up the initial content.

If you're using jQuery there's a hashchange plugin that will use whichever method the browser supports. I'm sure there are plugins for other libraries as well.

One thing to be careful of is colliding with ids on the page, because the browser will scroll to any element with a matching id.

Matthew Crumley
  • 95,375
  • 24
  • 103
  • 125
  • 2
    Don't search engines ignore these internal links (hashes)? In my scenario I want spiders to pick up on these links too. – Drew Noakes May 10 '09 at 14:51
  • 3
    @Drew, as far as I know, there's no way for search engines to treat a part of a page as a separate result. – Matthew Crumley May 11 '09 at 14:48
  • 8
    There is now a proposal to make search engines see hashes: http://googlewebmastercentral.blogspot.com/2009/10/proposal-for-making-ajax-crawlable.html – LKM Oct 08 '09 at 21:03
  • 5
    Instead of using `setInterval` to inspect the `document.location.hash` one can use the `hashchange` event, as it's much more adopted. [Check here for what browsers support each standard](http://caniuse.com/#search=hash|pushstate). My current approach is to use push/popState on browsers that support it. On the others I set the hash and only watch the `hashchange` in supporting browsers. This leaves IE<=7 without history support, but with usefull permalink. But who cares for IE<=7 history? – Irae Carvalho Feb 09 '11 at 22:25
  • Take a look at this demo of using window.location.hash: http://jsbin.com/ezopap/5#Hello%20World – ripper234 Feb 15 '12 at 17:22
  • If you ajax request the new page and re-implement the javascript tag that google gives you for google analytics will this not get the hashtag information and register this as a new page visit? – gabeio Jul 13 '12 at 20:58
  • 2
    @gabeDel Yes, although I don't think Analytics records the hash, so it would have the same URL. A better way would be to manually call `_trackPageview` with the "real" URL of the new page. – Matthew Crumley Jul 14 '12 at 03:26
120

With HTML 5, use the history.pushState function. As an example:

<script type="text/javascript">
var stateObj = { foo: "bar" };
function change_my_url()
{
   history.pushState(stateObj, "page 2", "bar.html");
}
var link = document.getElementById('click');
link.addEventListener('click', change_my_url, false);
</script>

and a href:

<a href="#" id='click'>Click to change url to bar.html</a>

If you want to change the URL without adding an entry to the back button list, use history.replaceState instead.

Flimm
  • 97,949
  • 30
  • 201
  • 217
clu3
  • 782
  • 2
  • 7
  • 10
  • You're right, last time i experimented with Chrome. I just now tried with Firefox 3.6 on my Ubuntu, it didn't work. I guess we'll have to wait until HTML5 is fully supported in FF – clu3 Dec 17 '10 at 07:35
  • 15
    Sample code is cut and pasted from https://developer.mozilla.org/en/DOM/Manipulating_the_browser_history . Which actually bothers explaining what foo and bar mean in this case. – mikemaccana Feb 01 '11 at 11:45
  • 2
    The foo and bar don't mean anything, it's an arbitrarily defined state object that you can get back later. – Paul Dixon Feb 01 '11 at 12:11
  • 3
    @Paul: yep, the article above provides that info. – mikemaccana Feb 01 '11 at 17:09
  • 2
    AS of posting this comment it works on firefox chrome and safari. No luck with mobile safari on the ipad or iphone though :( – Sid Jun 04 '11 at 03:06
  • thank for response. i think replaceState would be a better choice: `history.replaceState({"test":"test}, document.title, "bar.html");` – Amin.Qarabaqi May 02 '19 at 12:48
  • i still don't 100% get what is/was the original purpose of the state object (after reading the linked article(s). How to make best use of it? – Viktor Borítás Mar 28 '20 at 10:07
37

window.location.href contains the current URL. You can read from it, you can append to it, and you can replace it, which may cause a page reload.

If, as it sounds like, you want to record javascript state in the URL so it can be bookmarked, without reloading the page, append it to the current URL after a # and have a piece of javascript triggered by the onload event parse the current URL to see if it contains saved state.

If you use a ? instead of a #, you will force a reload of the page, but since you will parse the saved state on load this may not actually be a problem; and this will make the forward and back buttons work correctly as well.

moonshadow
  • 75,857
  • 7
  • 78
  • 116
21

I would strongly suspect this is not possible, because it would be an incredible security problem if it were. For example, I could make a page which looked like a bank login page, and make the URL in the address bar look just like the real bank!

Perhaps if you explain why you want to do this, folks might be able to suggest alternative approaches...

[Edit in 2011: Since I wrote this answer in 2008, more info has come to light regarding an HTML5 technique that allows the URL to be modified as long as it is from the same origin]

HoldOffHunger
  • 10,963
  • 6
  • 53
  • 100
Paul Dixon
  • 277,937
  • 48
  • 303
  • 335
  • 6
    Not so much of an issue if non-reload changes were restricted to the path, query string, and fragment—i.e. not the authority (domain and such). – Ollie Saunders Jun 16 '10 at 14:16
  • 2
    http://www.youtube.com/user/SilkyDKwan#p/u/2/gTfqaGYVfMg is a example that it is possible, try switching videos :) – Spidfire Aug 19 '10 at 20:54
  • You can only change the location.hash (everything after the #), as Matthew Chumley notes in the accepted answer. – Paul Dixon Aug 19 '10 at 22:41
  • 2
    Do you use Facebook? Facebook changes the URL _without_ reloading, using `history.pushState()`. – Derek 朕會功夫 Mar 07 '12 at 03:20
  • Paul Dixon, you should notice facebook inbox, when you click on names on left side, just URL is changed and new chat is loaded in chat box, page does not refreshed. also there are lots more site on WebGL they do the same – Dheeraj Thedijje Jul 05 '14 at 08:02
8

Browser security settings prevent people from modifying the displayed url directly. You could imagine the phishing vulnerabilities that would cause.

Only reliable way to change the url without changing pages is to use an internal link or hash. e.g.: http://site.com/page.html becomes http://site.com/page.html#item1 . This technique is often used in hijax(AJAX + preserve history).

When doing this I'll often just use links for the actions with the hash as the href, then add click events with jquery that use the requested hash to determine and delegate the action.

I hope that sets you on the right path.

Jethro Larson
  • 2,211
  • 23
  • 24
8

jQuery has a great plugin for changing browsers' URL, called jQuery-pusher.

JavaScript pushState and jQuery could be used together, like:

history.pushState(null, null, $(this).attr('href'));

Example:

$('a').click(function (event) {

  // Prevent default click action
  event.preventDefault();     

  // Detect if pushState is available
  if(history.pushState) {
    history.pushState(null, null, $(this).attr('href'));
  }
  return false;
});

Using only JavaScript history.pushState(), which changes the referrer, that gets used in the HTTP header for XMLHttpRequest objects created after you change the state.

Example:

window.history.pushState("object", "Your New Title", "/new-url");

The pushState() method:

pushState() takes three parameters: a state object, a title (which is currently ignored), and (optionally) a URL. Let's examine each of these three parameters in more detail:

  1. state object — The state object is a JavaScript object which is associated with the new history entry created by pushState(). Whenever the user navigates to the new state, a popstate event is fired, and the state property of the event contains a copy of the history entry's state object.

    The state object can be anything that can be serialized. Because Firefox saves state objects to the user's disk so they can be restored after the user restarts her browser, we impose a size limit of 640k characters on the serialized representation of a state object. If you pass a state object whose serialized representation is larger than this to pushState(), the method will throw an exception. If you need more space than this, you're encouraged to use sessionStorage and/or localStorage.

  2. title — Firefox currently ignores this parameter, although it may use it in the future. Passing the empty string here should be safe against future changes to the method. Alternatively, you could pass a short title for the state to which you're moving.

  3. URL — The new history entry's URL is given by this parameter. Note that the browser won't attempt to load this URL after a call to pushState(), but it might attempt to load the URL later, for instance after the user restarts her browser. The new URL does not need to be absolute; if it's relative, it's resolved relative to the current URL. The new URL must be of the same origin as the current URL; otherwise, pushState() will throw an exception. This parameter is optional; if it isn't specified, it's set to the document's current URL.

Community
  • 1
  • 1
Ilia Rostovtsev
  • 12,128
  • 10
  • 47
  • 80
4

There is a Yahoo YUI component (Browser History Manager) which can handle this: http://developer.yahoo.com/yui/history/

powtac
  • 37,821
  • 25
  • 107
  • 164
3

Facebook's photo gallery does this using a #hash in the URL. Here are some example URLs:

Before clicking 'next':

/photo.php?fbid=496429237507&set=a.218088072507.133423.681812507&pid=5887027&id=681812507

After clicking 'next':

/photo.php?fbid=496429237507&set=a.218088072507.133423.681812507&pid=5887027&id=681812507#!/photo.php?fbid=496435457507&set=a.218088072507.133423.681812507&pid=5887085&id=681812507

Note the hash-bang (#!) immediately followed by the new URL.

Jonathon Hill
  • 3,047
  • 1
  • 31
  • 30
2

What is working for me is - history.replaceState() function which is as follows -

history.replaceState(data,"Title of page"[,'url-of-the-page']);

This will not reload page, you can make use of it with event of javascript

Veshraj Joshi
  • 3,128
  • 2
  • 20
  • 35
2

A more simple answer i present,

window.history.pushState(null, null, "/abc")

this will add /abc after the domain name in the browser URL. Just copy this code and paste it in the browser console and see the URL changing to "https://stackoverflow.com/abc"

sam
  • 1,417
  • 1
  • 20
  • 39
2

There's a jquery plugin http://www.asual.com/jquery/address/

I think this is what you need.

Hash
  • 41
  • 3
1

my code is:

//change address bar
function setLocation(curLoc){
    try {
        history.pushState(null, null, curLoc);
        return false;
    } catch(e) {}
        location.hash = '#' + curLoc;
}

and action:

setLocation('http://example.com/your-url-here');

and example

$(document).ready(function(){
    $('nav li a').on('click', function(){
        if($(this).hasClass('active')) {

        } else {
            setLocation($(this).attr('href'));
        }
            return false;
    });
});

That's all :)

Tusko Trush
  • 412
  • 3
  • 14
1

I was wondering if it will posible as long as the parent path in the page is same, only something new is appended to it.

So like let's say the user is at the page: http://domain.com/site/page.html Then the browser can let me do location.append = new.html and the page becomes: http://domain.com/site/page.htmlnew.html and the browser does not change it.

Or just allow the person to change get parameter, so let's location.get = me=1&page=1.

So original page becomes http://domain.com/site/page.html?me=1&page=1 and it does not refresh.

The problem with # is that the data is not cached (at least I don't think so) when hash is changed. So it is like each time a new page is being loaded, whereas back- and forward buttons in a non-Ajax page are able to cache data and do not spend time on re-loading the data.

From what I saw, the Yahoo history thing already loads all of the data at once. It does not seem to be doing any Ajax requests. So when a div is used to handle different method overtime, that data is not stored for each history state.

Drew Noakes
  • 266,361
  • 143
  • 616
  • 705
user58670
  • 1,424
  • 17
  • 17
0

I've had success with:

location.hash="myValue";

It just adds #myValue to the current URL. If you need to trigger an event on page Load, you can use the same location.hash to check for the relevant value. Just remember to remove the # from the value returned by location.hash e.g.

var articleId = window.location.hash.replace("#","");
Adam Hey
  • 1,041
  • 13
  • 17