Can I prevent history.popstate from triggering on initial page-load? - javascript

I'm working on a site that serves content via AJAX.
If you click an item in the menu, a content div gets updated with $.get response, nothing fancy.
I'm implementing history.pushState to allow navigation with the browser's back/forward button.
I have the following to load content on history navigation:
$(function() {
$(window).bind("popstate", function() {
$.getScript(location.href);
});
});
The problem is, when a page is loaded for the first time, this function does $.getScript so the page is loaded immediately again. The first time the page is loaded it renders the initial HTML view, then on the second load it renders the JS view, since its a JS request.
How could I prevent this event from firing on pages with HTML requests?

Using the native HTML5 History API you're going to run into some problems, every HTML5 browser handles the API a little bit differently so you can't just code it once and expect it to work without implementing workarounds. History.js provides a cross-browser API for the HTML5 History API and a optional hashchange fallback for HTML4 browsers if you want to go down that route.
For upgrading your website into a RIA/Ajax-Application you can use this code snippet:
https://github.com/browserstate/ajaxify
Which is part of the longer article Intelligent State Handling which goes into explanations about hashbang, hashes and the html5 history api.
Let me know if you need any further help :) Cheers.

You need to get the event.originalEvent
// Somewhere in your previous code
if (history && history.pushState) {
history.pushState({module:"leave"}, document.title, this.href);
}
$(window).bind("popstate", function(evt) {
var state = evt.originalEvent.state;
if (state && state.module === "leave") {
$.getScript(location.href);
}
});

When the popstate event is fired on page load it will not have a state property in the event object. This allows you to check if the event is fired for a page load or not.
window.onpopstate = function (event) {
if (event.state) {
// do your thing
} else {
// triggered by a page load
}
}

When the browser first loads, it always fires a popstate event. So you need to determine if this popstate is yours or not.
When you do your pushState, make sure you have a state object. That way you can check it later.
Then on the popstate handler, check the state object :)
$(function() {
$(window).bind("popstate", function(data) {
if (data.state.isMine)
$.getScript(location.href);
});
});
// then add the state object
history.pushState({isMine:true},title,url);
Let me know if you need any more help :)

I use the below approach to change the address bar and save the current state including the current HTML body, and I reload it on the back button click without any other AJAX call. All gets saved in your browser:
<script type="text/javascript">
$(document).ajaxComplete(function(ev, jqXHR, settings) {
var stateObj = { url: settings.url, innerhtml: document.body.innerHTML };
window.history.pushState(stateObj, settings.url, settings.url);
});
window.onpopstate = function (event) {
var currentState = history.state;
document.body.innerHTML = currentState.innerhtml;
};
</script>

Related

Got some trouble Implementing History.js for the site i am creating

I came across history.js as a possible solution to some issues i am having. For the website i am building i decided to create a static page where my content would be loaded into. So i would have 3 Divs, (one for the header, one for the menubar and one for the content) When clicking a link in the menu that page would be loaded into the content div. Because of this all traffic stays in the index.php page so clicking on the back button i would go to the last visited content but last visited page. Its my understanding i can solve this with history.js.
So on my page i have several links in the menu bar where onclick a function is called upon. For instance
<li><a href="javascript:void(0);" onclick="myprofile()" ><span>my profile</span></a></li>
<li><a href="javascript:void(0);" onclick="mysettings()"><span>Settings<small>
A function would look something like this.
function myprofile() {
$('#content').load('members.php');
}
function mysettings() {
$('#content').load('settings.php');
}
I added some javascript for history.js (found on this forum) And although it does change the urls inside my index.php but when clicking back it doesnt load the pages. How would i let history.js work when functions are used? (some links i do really need functions so just putting the onload inside link would not be an option for me)
<script>
$(function() {
// Prepare
var History = window.History; // Note: We are using a capital H instead of a lower h
if ( !History.enabled ) {
// History.js is disabled for this browser.
// This is because we can optionally choose to support HTML4 browsers or not.
return false;
}
// Bind to StateChange Event
History.Adapter.bind(window,'statechange',function() { // Note: We are using statechange instead of popstate
var State = History.getState();
$('#content').load(State.url);
/* Instead of the line above, you could run the code below if the url returns the whole page instead of just the content (assuming it has a `#content`):
$.get(State.url, function(response) {
$('#content').html($(response).find('#content').html()); });
*/
});
// Capture all the links to push their url to the history stack and trigger the StateChange Event
$('a').click(function(evt) {
evt.preventDefault();
History.pushState(null, $(this).text(), $(this).attr('onclick'));
});
});
</script>
The first parameter of the History.pushState is "data", which is for additional information.
So to save the History entry you could do this in your click event:
History.pushState({ href: 'members.php' }, $(this).text(), $(this).attr('onclick'));
And then when reading it back out you would do this in your History.Adapter.bind function:
$('#content').load(State.data.href);

How can I detect page navigation without reloading?

I have script, what work one time when window loaded, it work on this page, but site use some navigation links what not fully reload page (see this answer for example: Modify the URL without reloading the page). How can I detect that and run my script again?
I have one idea: storing URL (without anchor) in variable and check it periodically with current url, but I think this is bad solution. May be you know better one?
JavaScript or JQuery is possible to use.
Use window.onpopstate or window.onpushstate if u are using pushState or replaceState ( from ur given example).
ex:-
To Navigate Without reload ( you already did this )
// Your code to fetch new URL body and title
// update the DOM then use following code to update URL also (don't use location.href, otherwise the page reloads)
// sorry u already know this because u provided the example ;)
let data = { title : "Current Title",
body : document.body.innerHTML" } // to store current page data
window.history.pushState(data, 0, "newURL");
To detect navigation ( i.e., you wanna do )
window.onpushstate: when above code runs for navigation to a new url and load new content without reload ...
window.onpushstate(function() {
// detects ur navigation
})
window.onpopstate: when you press back button
window.onpopstate(function (e) {
let { data } = e.state;
// data object that u gave in pushState method when u called ur last / latest pushState method...
// use this data to retrieve previous data content and title
let { title, body } = data;
document.title = title;
document.body.innerHTML = body
})
for more detail mdn docs
That's because the new pages are either
1 ) Already at the ready and simply being brought in-sight by jQuery
2 ) Ajax called in.
If you scout for your navigation (the links you click on to go to the other page), you should find click me or so.
If you look for wherever this is is bound (i.e.: $('#navigation a').on("click", function(){});, you can simply wrap your script within a function, and trigger this function together with loading the new page every time. (after it, obviously).
I wish I could be more clear, but you did not provide any code yourself, so I have absolutely no idea of what kind of example I should be giving here.
-- the point: Those page changes are triggered by something in your javascript. Find the trigger that makes the page-change happen, and simply insert myCustomFunction();.
If you want to make your bindings update with a new DOM, you could use this:
function setBindings(){
//removing the old bindings prevents the "click" from triggering twice.
$('a').off("click");
$('a').on("click", function(){
//load page and such here
//Apply script you want to run here
setbindings(); //rerun the function to set the bindings.
});
}
I think you are looking for hashchanges you can listen to this event onhashchange
window.onhashchange = function(e) {
var sublink = window.location.hash.substring(1);
//do your thing here
}
You can also check what updated the url after the hashchange
var sublink = window.location.hash.substring(1);
I think the URL of script is cached,do you used Ajax get method?if it is,please
like this write url "wwww.baidu.com?"+Math.random();if not is ,in the firefox ,you can used pageshow event.

Making ajax call on navigating away from page

I am working on a site that has multiple links to other sites. What I want is to send an ajax call to report that the user is going away when someone clicks and navigates away from the page. I put an alert on click of the links, which works but for some reason the controller never gets the ping.
Any assistance will be appreciated on how to achieve it.
Can't be done.
When you go to navigate away, there is only one event that can catch that, the onbeforeunload event, and that is quite limited in what it can do.
Not to mention there are other ways of leaving the page without navigating away:
Losing network connection
Closing the browser.
Losing power.
The only thing you can do is to set up a heartbeat kind of thing that pings the server every so many milliseconds and says 'I'm Alive.'
Depending on what you are trying to do, there is usually a better option, however.
You can try to simply set click event handler which will check the href attribute of every link before navigating. If it goes to another website, the handler sends AJAX request and then (after server responding) redirects to the page.
var redirect = '';
$('a').click(function() {
if (this.href.host != document.location.host) {
if (redirect) return false; // means redirect is about to start, clicking other links has no effect
redirect = this.href;
$.ajax({
url: '/away',
success: function(){document.location.href = redirect;}
});
return false;
});
However it can't work properly, if user has opened your page in multiple tabs.
The only reliable way to do this these days is by hooking (i.e. add event listener) your code in so called sendBeacon method from Beacon API on beforeunload event (i.e. when user tries to navigate away from page).
The navigator.sendBeacon() method asynchronously sends a small amount of data over HTTP to a web server. It’s intended to be used for sending analytics data to a web server, and avoids some of the problems with legacy techniques for sending analytics, such as the use of XMLHttpRequest:
<script>
var URL = 'https://your.domain/your-page.php';
// on 'beforeunload'
window.addEventListener('beforeunload', function (event) {
navigator.sendBeacon(URL);
// more safely is to wait a bit
var wait_until = new Date().getTime() + 500;
while (new Date().getTime() <= wait_until);
});
</script>
You can try:
$(window).on('beforeunload', function(){
return "This should create a pop-up";
});
You can achieve it by capturing clicks on all the links on the page (or all the relevant links) and then call ev.preventDefault() on it to prevent the browser from navigating directly to that page.
Instead, you can make an AJAX call to your server and when that call returns, you can set window.location to the URL the user was trying to navigate to.
Here is a workaround you could try.
At the loading of the page, use jquery to move all href attributes to tempHref attribute. Then, attach a method to catch the click event.
This way, clicking on the links will not automatically move to the intended destination.
When the click occurs, simply perform the ajax call, and then using javascript, move to the other page.
$('a').each(function () {
var link = $(this);
link.attr('tempHref', link.attr('href'));
link.removeAttr('href');
});
$(document).on('click', 'a', function ()
{
//perform ajax call;
location.href = $(location).attr('tempHref');
});

What is the recommended ordering of an AJAX load and history.pushState?

For instance, in jQuery, setting up a link to load something into a region, should I load the content before calling history.pushState?
$('#link').click(function () {
$('#region').load('regionContent', function () {
history.pushState(null, null, 'newUrl');
});
return false;
});
Or should I load the content after calling history.pushState?
$('#link').click(function () {
history.pushState(null, null, 'newUrl');
$('#region').load('regionContent');
return false;
});
The former seems preferable to me, because I feel the URL shouldn't change until the content does, but I've seen the latter more often (e.g. https://github.com/blog/760-the-tree-slider), so I'm wondering which is considered best practice.
With both your examples, when the user hits the back button the content isn't going to change! As you haven't got anything inside your state change handler.
Essentially you'll want to do this:
$('#link').click(function () {
History.pushState(null,null,'newUrl');
});
$('body').bind('statechange',function(){
$('#content').load(History.getState().url);
});
This will make it when you click a link, the state of your page will change, and that given the state of your page has changed, it will load in the new content. Hitting the back button in your browser, will also cause the state to change, and thus load in your new content.
Now here I have used History.js's statechange and History.getState().url instead of the native popstate and State.url as different browsers will fire the state change event at different times. For instance safari will the state change event at page load, where chrome and firefox don't - causing in safari your content to load twice. History.js provides a consistent API across all browsers, and if you wish even HTML4 browsers by falling back to hashes.

Is there a way to catch the back button event in javascript?

Is there a way to respond to the back button being hit (or backspace being pressed) in javascript when only the location hash changes? That is to say when the browser is not communicating with the server or reloading the page.
Use the hashchange event:
window.addEventListener("hashchange", function(e) {
// ...
})
If you need to support older browsers, check out the hashChange Event section in Modernizr's HTML5 Cross Browser Polyfills wiki page.
I did a fun hack to solve this issue to my satisfaction. I've got an AJAX site that loads content dynamically, then modifies the window.location.hash, and I had code to run upon $(document).ready() to parse the hash and load the appropriate section. The thing is that I was perfectly happy with my section loading code for navigation, but wanted to add a way to intercept the browser back and forward buttons, which change the window location, but not interfere with my current page loading routines where I manipulate the window.location, and polling the window.location at constant intervals was out of the question.
What I ended up doing was creating an object as such:
var pageload = {
ignorehashchange: false,
loadUrl: function(){
if (pageload.ignorehashchange == false){
//code to parse window.location.hash and load content
};
}
};
Then, I added a line to my site script to run the pageload.loadUrl function upon the hashchange event, as such:
window.addEventListener("hashchange", pageload.loadUrl, false);
Then, any time I want to modify the window.location.hash without triggering this page loading routine, I simply add the following line before each window.location.hash = line:
pageload.ignorehashchange = true;
and then the following line after each hash modification line:
setTimeout(function(){pageload.ignorehashchange = false;}, 100);
So now my section loading routines are usually running, but if the user hits the 'back' or 'forward' buttons, the new location is parsed and the appropriate section loaded.
Check out history.js. There is a html 5 statechange event and you can listen to it.
onLocationChange may also be useful. Not sure if this is a Mozilla-only thing though, appears that it might be.
Did you took a look at this? http://developer.yahoo.com/yui/history/

Categories

Resources