I'm implementing ajax navigation and I'd like to utilize the html5 history API, so I stumled upon History.js.
I'm not exactly sure how to go about using it and the documentation for it is not very good, nore are the examples.
I tried to mess with it...
$("a[href]:not(.no-ajax-navigate)").click(function(e){
e.preventDefault();
var path = $(this).attr('data-path');//this is "path/to/page" (clean url's)
//so I got the path.. now what? pushState?
});
I'm clueless as to what to do now.. thanks in advance!
update 1
I looked at the [source code that]#OneTrickPony commented](http://html5.gingerhost.com/new-york) that #OneTrickPony commented and I got it working. But it seems fire fire a popstate event on page load. Is this supposed to happen? My page basically fade in/out which is kinda annoying when you initially load (refresh) the browser.
It's important to note you can use HTML5 history without using History.js (see this).
History.js does give you some nice features like being able to store additional data in the history call stack. The following will set your URL path without a refresh.
$("a[href]:not(.no-ajax-navigate)").click(function(e){
e.preventDefault();
var path = $(this).attr('data-path');//this is "path/to/page" (clean url's)
// Set the URL using History.js (note the capital H)
var History = window.History;
History.pushState(null, null, path);
});
You can use the first two arguments in pushState to store additional data (see here for more details).
To go back use: History.back();
To catch the URL change event you can use:
History.Adapter.bind(window,'statechange',function(){
var History = window.History;
var State = History.getState();
// Do something here with State
});
Related
I have a function named back() which will be used for ajax calls. Actually I have an array stack contains last 5 search results and that back function will switch to the previous result set (according to that array stack) and it even changes the URL using window.history.pushState() when you click on the back button.
That back button I was talking about, is an element inside the browser which revokes back() function. Now I want to revoke back() function also when user click on the back button of the browser. Something like this:
window.onhashchange = function() {
back(); // this function also changes the url
}
But sadly window.onhashchange will be revokes twice when I click on the back of the browser. Because window.onhashchange will be revoked when you change the URL using window.history.pushState().
Anyway, how can I detect what things changes the URL? Either my JS code or the back button of the browser?
You can use performance.navigation.type
At any given point, for example on document.onload, you can read the value of type and, if it's:
0 The page was accessed by following a link, a bookmark, a form submission, a script, or typing the URL in the address bar.
1 The page was accessed by clicking the Reload button or via the Location.reload() method.
2 The page was accessed by navigating into the history.
255 any other way.
Just beware that support is limited according to the compatibilty table.
However, from the looks of it, it seems the table is outdated. It says it is not supported on chrome and I just tested it and works as expected on my chrome version (67.0)
One of solution is to implement onunload event with localstorage option.
This is from my head maybe you will need correction but this is base !
var history = [];
window.onload = function(){
var handler;
if ( localStorage.getItem('history') == null ) {
// FIRST TIME
history[0] = window.location.href;
localStorage.setItem("history", JSON.stringify(history));
}
else {
handler = localStorage.getItem('history');
handler = JSON.parse(handler);
history = handler;
// Just compare now
if (history[history.length-1] == window.location.href) {
// no change
} else {
history.push(window.location.href);
}
}
}
window.onunload = function(){
localStorage.setItem('history', JSON.stringify(history));
}
Note :
Since 25 May 2011, the HTML5 specification states that calls to
window.alert(), window.confirm(), and window.prompt() methods may be
ignored during this event. See the HTML5 specification for more
details.
I'm trying to be clever and considerate towards users, but once again, I've run into a "design" issue.
I have a number of pages on a website where I've chosen to remove the default navigation and replace it with a simple "back" button.
The back button functions like this:
href="javascript:history.back()"
I've also "no-indexed" these pages, so in theory all is good.
However, I've one more concern - it's probably never going to happen, but it would be good to know how to resolve it.
Suppose the user bookmarks the page. At present there's no way back, so I was wondering if it was possible to create a default href="/" but override it in some way if there is history. In fact amending the JavaScript function would suffice if I was able to determine if any history existed.
Is this possible? I'm no JS guru, so I might be trying to achieve something that isn't achievable.
Set the href to the specific URL, then use javascript to override this behaviour if a history record exists.
<a id="backbtn" href="/specific/url">Back</a>
document.getElementById("backbtn").addEventListener("click", function(){
if (history.length){
history.back();
return false;
}
});
http://jsfiddle.net/d1gz8ue9/8/
That way your link is still valid without javascript and can be opened in a new window.
Well, you can always try the 'bad' practise in the eyes of some, of overwiting hte native function with your own if the history is empty. Thats the quick and dirty if your code relies on auto generated code that implements history.back.
if(!history.length) {
history.back = function(){location.href='mydefaultpage';};
}
But if I were you, i'd just make my own personalised back function that checks the length of history like curt has shown in his answer.
Also for your back button I would forgo the
href="javascript:history.back()"
and replace it with
href="#" onclick="!history.length?location.href='foobar.html':history.back()"
or define it in a function
<a href="#" class="back-button">
(function(){
var leave = function() {
!history.length ? location.href='foobar.html' : history.back();
};
var arr = document.getElementsByClassName('back-button');
for(var i=0;i<arr.length;++i) {
arr[i].addEventListener('click',leave);
}
}
})();
I know it's an old question but i had to solve the problem recently and needed it to work with windows opened with a _blank target in the opener link (in this case the history.length is already 1 and not 0), and I came up with this:
if (history.length > 1) {
if (history.back() === undefined) {
history.pushState({}, '', defaultRouteUrl);
}
} else {
history.pushState({}, '', defaultRouteUrl);
}
Basically if there is a history it tries to go back, but if something went wrong then navigate to the defaultRouteUrl
For more infos refer to: https://developer.mozilla.org/en-US/docs/Web/API/History
I've got PJax up and running on my test site - it works a treat. However it relies heavily on a lot of javascript widgets and hence leaks memory.
Since I don't have time right now to re-write every widget, I thought that a simple solution would be to do a normal page load after, say 20 pjax page transitions. A simple plan....but it doesn't seem to be possible.
$.pjax.disable();
....still fetches the content via AJAX, but doesn't change the page.
$(document).pjax();.
...doesn't change the behaviour
$.pjax.handleClick = function (event, container, options) { return; };
...doesn't change the behaviour
$.pjax.state.timeout = 0;
...doesn't change the behaviour
delete $.pjax;
...breaks navigation
$.pjax.defaults.timeout=0;
...doesn't change the behaviour
How do I suspend pjax?
If you add a listener for pjax:beforeSend, you can capture the requested URL, set location.href yourself and return false to cancel the pjax behavior. That is how I'm doing it with the following code:
var pageLoadCounter = 0;
var MAX_PAGE_LOADS = 20;
$(".pjaxContainer").on("pjax:beforeSend", function (e, xhr, settings) {
if (++pageLoadCounter > MAX_PAGE_LOADS) {
// URI can be found at https://github.com/medialize/URI.js
var uri = URI(settings.url);
// Remove _pjax from query string before reloading
uri.removeSearch("_pjax");
location.href = uri.toString();
return false;
}
});
I've discovered that changing the id of the pjax container div gives me the desired result - although this seems like a bit of a kludge. It would also be possible by changing the timeout of the ajax request to 0 - but I still need to work out how to do this.
I did ask on the PJax github page about this but so far have not received a response.
I'm using a backbone router to handle a client clicking various options on a single page. Among other things, this router also behaves similarly to simple, same-page anchor tag links.
The issue that I'm having is that if a user clicks one of the options (say, "details") then scrolls away, they may want to click "details" again. If they do so, nothing happens - the app has already routed to details and won't reroute. I would just use simple links, such as Details, but there is more going on than just jumping around the page. Is there a way to force the reroute to happen?
Building off Alexey's answer, it's possible to force Backbone's History / Router to "reroute" the current URL, as if it trigger a reload.
You can actually detect if the typical call to navigate fails / returns nothing due to already being on the same url, and call loadUrl to force it in those cases.
For example, my site-wide internal link handler looks like this:
// `Backbone.history.navigate` is sufficient for all Routers and will
// trigger the correct events. The Router's internal `navigate` method
// calls this anyways.
var ret = Backbone.history.navigate(href, true);
// Typically Backbone's history/router will do nothing when trying to load the same URL.
// In some cases (links with .allow-reload), we want it to re-fire the same route.
// We can detect when Backbone.history.navigate did nothing, and force the route.
if (ret === undefined && $link.hasClass('allow-reload')) {
Backbone.history.loadUrl(href);
}
It will always return, there are no parameters to force it. I'm looking for the solution now and one that I see is silently replacing current route with new and then try to navigate to older one.
UPD: actually, you can use Backbone.history.loadUrl
If you bind the click event in your view, you can manually fire the route.
View.js
SearchView = Backbone.View.extend({
initialize: function(options){
alert("Alerts suck.");
this.router = options.router;
},
events : {
"click a.detail_link" : "showDetails"
},
showDetails : function(){
this.router.navigate("/#details", true);
}
});
I'm using history.JS (latest) with Jquery (latest) to load and replace just a portion of a website, this is all working, currently I'm only trying to get it working in modern browsers so I'm not fiddling with the hash changes.
Everything seems to work, however when I click the back button on the browser (latest FF & Chrome) the page does not change (although the url and title do change). I've had a google and a look on here but I can't see what is happening.
Looking on stack overflow I found this page: Restoring content when clicking back button with History.js which seems to be asking a similar question. I've add the loaded contents of the #left_col (which is the div being replaced) to the state data, but I'm not really sure where to go from there, I know I need to reload that data when the state changes, but I can't see how.
var History = window.History;
var origTitle = document.title;
if ( !History.enabled ) {
return false;
}
History.Adapter.bind(window,'statechange',function(){
var State = History.getState();
History.log(State.data, State.title, State.url);
});
$('.ajaxload').live("click", function() {
History.pushState({state:1,leftcol:$('#left_col').html()}, origTitle, $(this).attr("href"));
$('#left_col').load($(this).attr("rel"));
return false;
});
I'd really appreciate any help!
update:
I managed to get the page to change on the user clicking back, but it doesn't load the right state (it seems to go two states back rather than one), the code I've added to the above code is:
window.addEventListener('popstate', function(event) {
var State = History.getState();
$('#left_col').html(State.data.leftcol);
});
It turns out I needed to update the page on statechange using History.js, not poState as I'd thought. below is my full (and working) code for anyone who may be having the same issue:
var History = window.History;
var origTitle = document.title;
if ( !History.enabled ) { return false; }
History.pushState({state:$(this).attr('data-state'),leftcol:$('#left_col').html()}, origTitle, $(this).attr("href")); // save initial state to browser history
function updateContent(data) {
if(data == null) return; // check if null (can be triggered by Chrome on page load)
$('#left_col').html(data); // replace left col with new (or old from history) data
History.pushState({state:$(this).attr('data-state'),leftcol:$('#left_col').html()}, origTitle, $(this).attr("href")); // save this state to browser history
}
History.Adapter.bind(window,'statechange',function(){ // use this NOT popstate (history.JS)
var State = History.getState();
//History.log(State.data, State.title, State.url);
updateContent(State.data.leftcol); // call update content with data for left col from saved state
});
$('.ajaxload').live("click", function() { // attach click event, get html and send it to updateContent
$.get($(this).attr("rel"), updateContent);
return false;
});
You are correct when you say that you need to reload the data when the state changes, in that you will have to have the javascript undo the changes made or render the contents again from the original state.
This will probably better suit your agenda:
https://github.com/thorsteinsson/jquery-routes
Edit:
You might also consider using backbone.js (http://backbonejs.org/) as it will help you create structure and abstract code in a way that makes it easier to understand what needs to be done.
Backbone comes with it's own url router and so called views.