I'm building a little CoffeeScript application with 10 buttons and a container (simple). When the user press on one of the button : the container change.
The buttons look like a navbar and instead of using links (that will reload the entire page), I used javascript (Coffeescript, jquery or whatever) to change the content of the page (with some Ajax query to load data).
The problem is that the back and forward button of the browser can't work with that solution... and I need to find a solution for that. Routing maybe ?
I really like the way Asana.com resolved this issue: actually the address change but the content seems not to be entirely reloaded.
What do you suggest ? Thanks for the help
Hashes. The simplest solution is to define an URL hash every time the user clicks on a button. For example:
location.href = "#" + button.id;
With that, you create a history entry, and the user can press back or forward in the browser.
But how can you check when this happens? There's the hashchange event:
window.onhashchange = function() {
var state = location.hash.substring(1); // chomps the initial #
...
};
Basing your code on the state variable, you can trigger your AJAX calls from there.
By the way, you can change your code altogether, using links instead of buttons with an hash as the href property, which does not reload the page, but creates an history entry and fires the hashchange event.
The hashchange event is supported by every modern browser (that support history.pushState too, a more flexible and powerful way to control your history) and IE8-9.
Related
I have a button on index page (index.php) that when you click it, it should take you to other page (login.php) without reloading the page simultaneously. Below is javascript with pushState.
document.getElementById('cta_btn').onclick = function() {
window.history.pushState('', '', 'login.php');
};
The problem is that I stay on same page (index.php) only url changes /login.php
pushState is a way of saying "I am using JavaScript to modify the DOM of this page so that it becomes the same as what you would get if you requested this URL". You don't appear to have written the JS to modify the DOM.
It doesn't cause new content to load by itself. It doesn't really navigate anywhere. It just pretends to so you can hook into popstate and change the DOM back to how it was before when the back button is pushed.
If you use pushState then you all need to change the DOM (with createElement and friends for simple cases and with frameworks like React for complex ones).
If you want to navigate to a new page, then assign a value to location.href.
If you don't want to load content from the server in order to do that navigation then make sure the user has previously visited the page and that caching headers were sent then.
I guess State is a Object, You should try this...
window.history.pushState({}, '', 'login.php');
I want to change html without reload. I do it like:
$('#left_menu_item').click(function(e) {
if (!!(window.history && history.pushState)) {
e.preventDefault();
history.pushState(null, null, newUrl);
}
});
It works correctly. But if I want to go back with "Back" button - browser change url on previous, but it not reload page. Why?
this behaviour is expected and is in accordance with the specifications of manipulating the history stack.
this is a relatively complex problem to explain. but in short think of it as this: any history entry the user pushes on the history stack (using pushState etc) doesn't merit a page load when you move from it because it is considered a fake (user generated) history entry.
why?
this behaviour is a good thing and is consistent with the intent of giving the developer more control over the page without being forced to reload it (think of it like ajax: you can do things that were previously only possible by page reloading like fetching data but now you can do it without reloading the page using the XMLHttpRequest object).. if you want to mimic the behaviour of reloading the page when clicking the back button.. you can simply call location.reload() when you handle the window.onpopstate event
how?
this may be outside the scope of your question but i just wanted to put it there to describe what we're talking about
let me explain by using an existing example here (excerpted text will be italicised):
Suppose http://mozilla.org/foo.html executes the following JavaScript:
var stateObj = { foo: "bar" };
history.pushState(stateObj, "page 2", "bar.html");
This will cause the URL bar to display http://mozilla.org/bar.html, but won't cause the browser to load bar.html or even check that bar.html exists.
think of it as that you are creating an entry in the history stack that is not associated with an actual page load.. rather a 'fake' page load (ie you are just using javascript to manipulate the dom and insert html)..
Suppose now that the user now navigates to http://google.com, then clicks back. At this point, the URL bar will display http://mozilla.org/bar.html, and the page will get a popstate event whose state object contains a copy of stateObj. The page itself will look like foo.html, although the page might modify its contents during the popstate event.
the point here is that bar.html is a fake history entry that sits on top of the original http://mozilla.org/foo.html.. so you will see on the url http://mozilla.org/bar.html but the contents will belong to foo (in this example notice that we didnt manipulate the content of the dom when we pushed bar.html.. if we did like in your example.. then that content will also show up). the key thing here is that the page reloads!.. because we are serving a page that has a genuin entry on the history stack (even if on the url.. we are displaying a url that is associated with a fake entry on the history stack).
also separate this discussion from the page manually handling the popstate event.. that's a different story and will just complicate things.
If we click back again, the URL will change to http://mozilla.org/foo.html, and the document will get another popstate event, this time with a null state object. Here too, going back doesn't change the document's contents from what they were in the previous step, although the document might update its contents manually upon receiving the popstate event.
here.. the page will not load!.. that's because we are making the transfer from a fake history stack entry to the real one (and the real one was already loaded in the previous step.. so the page reloaded and that's it).
that's it for the example. the concept is kind of hard to explain and i encourage you to test your code by clicking through a combination of real and fake pages and you will see a pattern of when the page actually loads and when it doesn't..
window.onpopstate = function(event) {
if(event && event.state) {
location.reload();
}
}
This is what I use :)
To quickly explain, all of the links on my page currently use the following format:
Link
What I'd like to do is upgrade the code to something like the following:
Link
My goal is two-fold:
Make the status bar show the URL that the link leads to
Allow users to copy the link location and have it be a valid URL
My problem is that this then ignores the onclick and actually redirects to the URL. Whats the best way to fix this?
If you're using jQuery, then I suggest using History.js or jQuery BBQ. They both provide good support for HTML5 pushState with a fallback for HTML4 browsers. Using pushState, you'll be able to update the URL (to make it bookmarkable) and you can use the statechange event to handle navigation (like when the user hits the back button). Take a look at the documentation for History.js to see some examples of how it works.
Update:
Per your comment, I suggest creating an event handler for links that you want handled via an AJAX request.
For example, lets say all links you want to be ajaxy have the "ajaxify" class.
$(document).on("click", "a.ajaxify", function(e) {
var url = $(this).attr("href");
History.pushState({}, null, url);
// do something, like make an ajax request to get the url
e.preventDefault();
});
In your loadPageWithAjax() function, you need to be able to take the event data that it is automatically called by the browser.
function loadPageWithAjax(e){
e.preventDefault(); stops page from clicking
//do your ajax stuff
}
You can't set the URL of the address bar without changing loading the page unless you use hashes using window.location.hash, or the new history.pushState, which is only available to HTML5 browsers.
I'm creating an HTML and Javascript client for running in browser which talks to REST API. I'm using RouteMap to set my URLs. So I've kept a convention something like this
http://mysite.com/#/{ResourceName}/[edit|view|list]/[Id]/
I've set just one route and I'm grabbing these parameters in the function bounded to hashchange. Most of the things work fine. Just two issues and I'm stuck because of them.
If the user clicks on the same link twice, hashchange event doesn't fire. Yes, hash has not changed so obviously it won't fire. But there should be something which can be done and I'm missing that.
If I change something in the UI (like bring up new divs and hide some) for which I don't want to change the hash link, I loose that history and can't go back by clicking the back button properly.
Any help will be grateful.
For #1, you probably want to attach a handler to the link click event. That way you can tell if the link is being clicked. When I use onhashchange, I always attach something to the click event to assist polyfills for onhashchange, so at least I can tell when it's failing.
For #2, I want to point out that having automatic stuff change the user's history is problematic. You could fill someone's history with minute, meaningless hash changes. I recommend only changing the history when the user actually interacts. Short of that, HTML5 does offer pushState and popState. Reference
When the browser clicks "back" button, I want to append "&page=3" to the URL.
How do I bind it, and then do this?
Edit: I want this very simple.
BIND the event{
window.location(that-url + "&page=3")
}
Can it be done that simply?
It sounds like you're trying to create a history plugin.
Have you tried using using the mikage history plugin?
I wouldn't recommend changing the URL when they navigate away from the current page (which is what the back button does), because you immediately erase the forward history (thus breaking the forward button). When trying to handle the back button with pagination and javascript/ajax it is more typical to use the browser hash to pass parameters. The JavaScript namespace doesn't get cleared when the forward and backward buttons are used and the hash is updated according to what navigation was used. These history plugins have a couple of methods to detect when navigation is used (as the doc load event doesn't fire).
So beware, writing a history plugin isn't straightforward because of the way browsers fail to consistently handle hash property of the location object (part of the window object). You will definitely want to look at what others have done.
We use the window.location.hash to handle the history in our app.
I guess it works well in single page apps and is very simple.
For multiple pages app, I don't think it's a good idea to try to control and change the natural page history of the browser.
When the user clicks "back" or "next", the hash key gets the previous or next value.
Because of IE7 you need to use a polling technique (but it is ok in all browsers), with a setInterval(...) and a fast function that checks for instance every 300ms if the hash has changed.
Then, if a change occurs, act accordingly.
ie: call the server and refresh some areas in the page.
It works very well, and does not kill at all the responsiveness of the application.