Skip to content

Commit 5df7525

Browse files
author
scottjehl
committed
This change introduces history.replaceState on top of our already-functional hash-driven navigation to clean-up hash-based external URLs as hash change events occur. I've found this behaves very well on properly-supporting browsers like Chrome and Firefox, while falling back to hash-based urls on non-supporting platforms, allowing us to preserve deep links to Ajax-driven pages (at least in JS-enabled environments).
This approach seems to be particularly useful on iOS, where pushState is supported, but often does nothing due to security restrictions that require pushState calls to occur only as the result of a user interaction. Note that replaceState is only used for externally-available URLs, so dialogs and generated pages like nested listviews still require hash-based links (because the pages are actually located within the page referenced in the URL that precedes the hash.
1 parent 2b51355 commit 5df7525

File tree

1 file changed

+45
-12
lines changed

1 file changed

+45
-12
lines changed

js/jquery.mobile.navigation.js

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -168,9 +168,17 @@
168168
//get path from current hash, or from a file path
169169
get: function( newPath ) {
170170
if( newPath === undefined ) {
171-
newPath = location.hash;
171+
if( $.support.pushState && !location.hash ){
172+
newPath = location.href;
173+
}
174+
else{
175+
newPath = location.hash;
176+
177+
}
172178
}
173-
return path.stripHash( newPath ).replace( /[^\/]*\.[^\/*]+$/, '' );
179+
return path.stripHash( newPath );
180+
// Todo Todo Todo: this return used to apply the following: .replace( /[^\/]*\.[^\/*]+$/, '' ).
181+
// It doesn't work with pushstate, but WHY was it there???
174182
},
175183

176184
//return the substring of a filepath before the sub-page key, for making a server request
@@ -300,7 +308,9 @@
300308

301309
//disable hashchange event listener internally to ignore one change
302310
//toggled internally when location.hash is updated to match the url of a successful page load
303-
ignoreNextHashChange: false
311+
ignoreNextHashChange: false,
312+
313+
ignoreNextPopState: false
304314
},
305315

306316
//define first selector to receive focus when a page is shown
@@ -862,7 +872,8 @@
862872
// for the dialog content to be used in the hash. Instead, we want
863873
// to append the dialogHashKey to the url of the current page.
864874
if ( isDialog && active ) {
865-
url = active.url + dialogHashKey;
875+
//if pushState is enabled, we only need to set the hash to the dialogkey + something random
876+
url = ( $.support.pushState ? "" : active.url ) + dialogHashKey + ( Math.random().toString().slice(2,6) );
866877
}
867878

868879
// Set the location hash.
@@ -1113,22 +1124,44 @@
11131124
});
11141125

11151126
//hashchange event handler
1116-
$window.bind( "hashchange", function( e, triggered ) {
1127+
$window.bind( "hashchange popstate", function( e, triggered ) {
11171128
//find first page via hash
1118-
var to = path.stripHash( location.hash ),
1129+
var to = path.get(),
11191130
//transition is false if it's the first page, undefined otherwise (and may be overridden by default)
1120-
transition = $.mobile.urlHistory.stack.length === 0 ? "none" : undefined;
1121-
1131+
transition = $.mobile.urlHistory.stack.length === 0 ? "none" : undefined,
1132+
isDialog = urlHistory.stack.length > 1 && to.indexOf( dialogHashKey ) > -1,
1133+
isGeneratedPage = to.indexOf( $.mobile.subPageUrlKey ) > -1,
1134+
pushStateSupported = $.support.pushState,
1135+
currHref = location.href.split("#")[0];
1136+
1137+
if( e.type === "hashchange" && pushStateSupported && !isDialog ){
1138+
if( isGeneratedPage ){
1139+
var splitter = '&' + $.mobile.subPageUrlKey;
1140+
to = to.split( splitter ).join( "#" + splitter );
1141+
}
1142+
//use replacestate to swap the hash-based url with something real
1143+
history.replaceState( {}, document.title, to );
1144+
1145+
//if it worked, return here.
1146+
if( location.href.split("#")[0] !== currHref ){
1147+
return;
1148+
}
1149+
}
1150+
1151+
1152+
11221153
//if listening is disabled (either globally or temporarily), or it's a dialog hash
1123-
if( !$.mobile.hashListeningEnabled || urlHistory.ignoreNextHashChange ) {
1154+
// seems this ignored hashchange stuff is unnecessary with pushstate in play
1155+
if( e.type === "hashchange" && !pushStateSupported && ( !$.mobile.hashListeningEnabled || urlHistory.ignoreNextHashChange ) ) {
11241156
urlHistory.ignoreNextHashChange = false;
11251157
return;
11261158
}
1159+
1160+
//if it's a generated subpage, like a nested list, only use the
11271161

11281162
// special case for dialogs
1129-
if( urlHistory.stack.length > 1 &&
1130-
to.indexOf( dialogHashKey ) > -1 ) {
1131-
1163+
if( isDialog ) {
1164+
11321165
// If current active page is not a dialog skip the dialog and continue
11331166
// in the same direction
11341167
if(!$.mobile.activePage.is( ".ui-dialog" )) {

0 commit comments

Comments
 (0)