Closed evoactivity closed 2 years ago
Correct. Though the loading in the page is something that you would code. History.js will just fire the statechange event for you :)
this is my statechange event
$(window).bind('statechange',function(){
var
State = window.History.getState(),
url = State.url,
relativeUrl = url.replace(rootUrl,'');
$body.addClass('loading');
$content.animate({height: "toggle", opacity: "toggle"}, getNewPage);
function getNewPage(){
$.get(url,function(data){
$content.html($(data).find('#loadingBlock').html());
if ($(data).find('#leftArea')){ //knowing there is a left area, we know breadcrumb and rightArea exist
$leftArea.html($(data).find('#leftArea').html());
$rightArea.html($(data).find('#rightArea').html());
$breadcrumb.html($(data).find('#breadcrumb').html());
}
$content.animate({height: "toggle", opacity: "toggle"});
$content.ScrollTo();
$body.removeClass('loading');
if ( typeof pageTracker !== 'undefined' ) {
pageTracker._trackPageview(relativeUrl);
}
}); // end get
};
}); // end onStateChange
I would have thought this would have loaded the page?
Let me just make it clear, that browsing the site, clicking links all work, all ajaxed, back and forward works, it's just directly going to a hash url where it seems to fail.
Anyone have any ideas about this, as I can't figure it out, from what I can tell the statechange event should be firing on page load, and my handler should be running.
I have the same issue, been struggling with it for two days no with no answers.
Basically I don't think the statechange function is firing when a hashed URL is found.
This is the site I am testing on http://www.alexandralegouixandthesunflowers.com/testsite1 and as you can see all the links are working and back and forwards etc. Going to an unhashed link works to an extent in HTML4 browsers (clicking on links adds another # to the unhashed url ie. going to www.mysite.com/page1 loads that page but in html4 browsers going to page2 after that gives url www.mysite.com/page1#page2) and going to a hashed link in html5 browsers does not work. This is a Joomla site and I know Joomla has Mootools and other components put other js in there that may conflict so I have tried stopping Joomla from outputting anything in the head completely and also on the front page it is a simple article so no other js is added. When you go to a link you see that the content want xajax but it is not there so I don't think that can be stopping the statechange fire either as on the home page there is nothing loaded apart from the JS listed in the instructions and my template.js which is basically the same as the code on the installation page with a few extras (and just making it exactly the same doesn't help either...)
The only glimer of a solution I can find is that when you load a hashed link directly, that menu item is disabled properly so it won't click, so going to 'blog' and copying the url and pasting it into another tab (in FF 3.5 for example) will not take you to that page but 'blog' in the menu will not be clickable.
My code has extra things in it to check if pictures are loaded and transition etc, but even if I get rid of all that and just use the following, I still cannot get it to work... (function(window,undefined){
// Prepare our Variables
var
History = window.History,
$ = window.jQuery,
document = window.document;
// Check to see if History.js is enabled for our Browser
if ( !History.enabled ) {
return false;
}
// Wait for Document
$(function(){
// Prepare Variables
var
/* Application Specific Variables */
contentSelector = '#content',
$content = $(contentSelector),
contentNode = $content.get(0),
$menu = $('.mainmenu'),
activeClass = 'active selected current youarehere',
activeSelector = '.active,.selected,.current,.youarehere',
menuChildrenSelector = '> li,> ul > li',
/* Application Generic Variables */
$body = $(document.body),
rootUrl = History.getRootUrl() + 'testsite1/',
scrollOptions = {
duration: 800,
easing:'swing'
};
// Ensure Content
if ( $content.length === 0 ) {
$content = $body;
}
// Internal Helper
$.expr[':'].internal = function(obj, index, meta, stack){
// Prepare
var
$this = $(obj),
url = $this.attr('href')||'',
isInternalLink;
// Check link
isInternalLink = url.substring(0,rootUrl.length) === rootUrl || (/[^\:]/).test(url);
// Ignore or Keep
return isInternalLink;
};
// HTML Helper
var documentHtml = function(html){
// Prepare
var result = String(html)
.replace(/<\!DOCTYPE[^>]*>/i, '')
.replace(/<(html|head|body|title|meta|script)/gi,'<div class="document-$1"')
.replace(/<\/(html|head|body|title|meta|script)/gi,'</div')
;
// Return
return result;
};
// Ajaxify Helper
$.fn.ajaxify = function(){
// Prepare
var $this = $(this);
// Ajaxify
$this.find('a:internal').click(function(event){
// Prepare
var
$this = $(this),
url = $this.attr('href'),
title = $this.attr('title')||null;
// Continue as normal for cmd clicks etc
if ( event.which == 2 || event.metaKey ) { return true; }
// Ajaxify this link
History.pushState(null,title,url);
event.preventDefault();
return false;
});
// Chain
return $this;
};
// Ajaxify our Internal Links
$body.ajaxify();
// Hook into State Changes
$(window).bind('statechange',function(){
// Prepare Variables
var
State = History.getState(),
url = State.url,
relativeUrl = url.replace(rootUrl,'');
// Set Loading
$body.addClass('loading');
// Start Fade Out
// Animating to opacity to 0 still keeps the element's height intact
// Which prevents that annoying pop bang issue when loading in new content
$content.animate({opacity:0},800);
// Ajax Request the Traditional Page
$.ajax({
url: url,
success: function(data, textStatus, jqXHR){
// Prepare
var
$data = $(documentHtml(data)),
$dataBody = $data.find('.document-body:first'),
$dataContent = $dataBody.find(contentSelector).filter(':first'),
$menuChildren, contentHtml, $scripts;
// Fetch the scripts
$scripts = $dataContent.find('.document-script').detach();
// Fetch the content
contentHtml = $dataContent.html()||$data.html();
if ( !contentHtml ) {
document.location.href = url;
return false;
}
// Update the menu
$menuChildren = $menu.find(menuChildrenSelector);
$menuChildren.filter(activeSelector).removeClass(activeClass);
$menuChildren = $menuChildren.has('a[href^="'+relativeUrl+'"],a[href^="/'+relativeUrl+'"],a[href^="'+url+'"]');
if ( $menuChildren.length === 1 ) { $menuChildren.addClass(activeClass); }
// Update the content
$content.stop(true,true);
$content.html(contentHtml).ajaxify().css('opacity',100).show(); /* you could fade in here if you'd like */
// Add the scripts
$scripts.each(function(){
var $script = $(this), scriptText = $script.html(), scriptNode = document.createElement('script');
scriptNode.appendChild(document.createTextNode(scriptText));
contentNode.appendChild(scriptNode);
});
// Complete the change
if ( $body.ScrollTo||false ) { $body.ScrollTo(scrollOptions); } /* http://balupton.com/projects/jquery-scrollto */
$body.removeClass('loading');
// Inform Google Analytics of the change
if ( typeof window.pageTracker !== 'undefined' ) {
window.pageTracker._trackPageview(relativeUrl);
}
},
error: function(jqXHR, textStatus, errorThrown){
document.location.href = url;
return false;
}
}); // end ajax
}); // end onStateChange
}); // end onDomLoad
})(window); // end closure
So getting rid of all joomla additional js, making sure content is not adding its own, and deleting all my added functions does not make a hashed link load in an html4 browser.
I am lost HELP!!
I also have tried removing the google analytics code I had from the bottom aswell- no joy...:(
I've faced same issue.
-> http://foo.com/#bar/ displays / not bar/ if it's pasted in new tab/window. -> http://foo.com/#bar/ transforms to http://foo.com/bar/ in HTML5 browsers, but again, home page is displayed, no bar/
@davidosullivan, @evoactivity, did you manage to fix that?
Hey there nookiepl,
I have had to give development of my project a rest for a couple of weeks but will be back on it next week, will let you know if I make any progress. Would obviously really appreciate it if anyone else could do likewise...
Anyone know how to get any help from balupton?
D
Anyone know how to get any help from balupton?
I am listening! And I do care about every single issue - the problem is that I am working a day job to pay the bills - so the only time I get is a an hour or two each night to work my entire array of open-source stuff - which can be pretty stressing. So doing the best I can. Wish I could get a sponsor or something so I can do this fulltime, but until that happens patience is key.
Another project is currently priority, but once that finishes - sometime this week, then those night hours will be on History.js - which I promise I will sort out every single darn issue in this place! Muwahahha.
So yeah - patience, pull requests or sponsor ;-) Cheers.
Hey Balupton,
Apologies for upsetting you, no offence intended- glad to know you are there and this great work is still actively in development! Looking forward to hearing your advice.
D
Not upset or offended :)
@davidosullivan, @evoactivity, and @nookiepl, there seems to be a bug in History.js that prevents statechange from firing on first load. It's very easy to get around though by just manually triggering the statechange event somewhere in your init method.
Yes, I did something like that, but that means doubled first request. But I can live with that until official fix. :)
Hi, I am hoping to use History as follows: 1) User paginates over AJAX-enabled pages (AJAX calls update the page's content, while the URL does not change). 2) I would like to use History.pushState programmatically so store the page numbers, which the user visited. 3) When the user clicks the Back button, I would like to get an event so that I can do HREF=content_on_that_page.
Is there a way to tell the difference between statechange caused by pushState from the one caused by the user manually hitting the Back button? If not, could you please recommend an approach that would work?
Thank you very much for any help. --Alex
Hi again:
I think I figured out how to do this for our application. Basically, I intercept the click event for an AJAX action. Then I stop the event and do History.pushState with the URL of the action. Hence, the push does not trigger the statechange event. The the statechange event handler in the application layout extracts the URL and does the AJAX request on it to get the content.
I hope that this is useful to anybody reading.
Thank you, --Alex
@neilgupta Do you have an example piece of code to fire the state change event on initial page load? I had tried to do this:
History.Adapter.bind(window,'statechange',onNavigate);
onNavigate();
But in my onNavigate() function the History.getState() returns an empty object.
This is my code (but I disabled it, because we cannot intercept all clicks; so we still do not have a solution):
<script type="text/javascript">
(function(window,undefined){
var History = window.History, State = History.getState();
if ( !History.enabled ) {
return false;
}
History.Adapter.bind(window,'statechange',function(){
var State = History.getState();
console.log("IN APPLICATION_LAYOUT: STATE_CHANGE; AJAX REQUESTING URL=$"+State.url+"$");
new Ajax.Request(State.url, { method: 'get' });
});
})(window);
</script>
--Alex
@dobesv Your code is executing onNavigate whenever the statechange event is fired, but that event is never actually triggered. All you need to do is trigger the 'statechange' event with History.Adapter.trigger(window, 'statechange')
@neilgupta thanks I'll give that a try!
@neilgupta It works! Thank you!
I'm not sure if I understand it all correctly, but I think it is behaving correctly:
If you open your html4-browser and visit the URL www.example.com/#./page-name, it opens www.example.com:
_suid
(where a state or title is encoded for a html4-browser), the initial state must be empty (and the title cannot not change)Assume we had an _suid
in the url. What would the correct behaviour be: Parsing it on a new page load and loading an initial state? That would work when copy & pasting a html4-browser-url but not an url of an html5-browser (where is no _suid
and is no way to share a page's state by url
).
So it looks right to me to behave exactly like for html5-browsers: When copy and pasting an url, the initial state is always empty. If you want to be it otherwise on html4-browsers or html5-browsers, you have to do it yourself (that is not what the plugin is for!).
But in that case it is not enough to parse everything after the hash, because you will make sure that you always encode your full state in the url
for html5-browsers too! Or you break html5-browsers.
My logic is like this:
History.pushState( {var: 1}, "", "?var=99" )
What is your state after you do this call? The state is {var: 1}
. What should your url
be like? It should end with ?var=99
because that defines the pushState
html5-api. Now when copy and pasting the url, it cannot share the {var: 1}
in any way. So the initial state for a new page load of ?var=1
cannot be {var: 1}
, because it simply does not make any sense to parse the url for states, because, well, the api does not make an assumptions about that they correlate.
That is how it should work and works for html5-browsers.
And now comes the html4-fallback with hashes in the url. It is only a fallback, so it must do the same like html5-browsers (despite a different url with encoded states in it), and it does. The initial state is empty when you open a new page, whatever url you use. The plugin does not care about the url on the initial load (with one exception: opening a html4-hash-link in a html5-browser: It only translates the url, but no state)
This fixed the problem for me:
$.fn.ajaxify = function() {
var $this = $(this);
$this.find('a:internal:not(.no-ajax)').click(function(event) {
var $this = $(this),
url = $this.attr('href'),
title = $this.attr('title') || null,
hashPos = url.indexOf('#'), hash;
// Continue as normal for cmd clicks etc
if (event.which == 2 || event.metaKey) {return true;}
if (hashPos > -1) {
hash = url.substr(hashPos);
url = url.substring(0, hashPos);
}
History.pushState(null, title, url);
if (hash) window.location.hash = hash;
event.preventDefault();
return false;
});
return $this;
};
Thank your @ryananguiano, that was exactly what I needed. Only needed to change '.no-ajax' to '.no-ajaxy' for my usecase.
Shouldn't this load the correct content, for example if you clicked this link (html4 browsers) www.example.com/#./page-name shouldn't it load /page-name not the homepage of example.com?
Or is this not something that is supported?