I'm using history.pushState to set a URL, while at the same time using a jquery .load to actually load the page content. I'm new to the history API, but I understand that this is the normal way to get back-button/etc functionality in dynamic sites.
The problem is that the spoofed address I'm setting with pushState
is being used by .load
in some way when it fetches pages, which means that .load
is trying to fetch non-existant pages, and so fails. DocumentRoot
is at /var/www/bar
on the server. In this example, the user is attempting to navigate between two points in a single html file on the server, which is located at dir1/dir2/p1.html
under DocumentRoot
on the server.
dir1/dir2/p1.html #Container1
, and the address bar should show foo.com/a/b/c
dir1/dir2/p1.html #Container2
, and the address bar should show foo.com/a/b/d
So how exactly do I do this? This is what I've tried with relative URLs, and it fails:
$("#mainContent").load("dir1/dir2/p1.html #container1"); // good: XHR fetch foo.com/bar/dir1/dir2/p1.html
history.pushState(null, null, "a/b/c"); // good: shows 'foo.com/a/b/c' as the URL
...
$("#mainContent").load("dir1/dir2/p1.html #container2"); // bad: XHR fetch foo.com/bar/a/b/dir1/dir2/p1.html
history.pushState(null, null, "a/b/d"); // bad: shows 'foo.com/a/b/a/b/d' as the URL
And this is what happens when I use absolute URLs:
$("#mainContent").load("/dir1/dir2/p1.html #container1"); // good: XHR fetch foo.com/bar/dir1/dir2/p1.html
history.pushState(null, null, "a/b/c"); // good: shows 'foo.com/a/b/c' as the URL
...
$("#mainContent").load("/dir1/dir2/p1.html #container2"); // bad: XHR fetch foo.com/a/b/dir1/dir2/p1.html
history.pushState(null, null, "a/b/d"); // good: shows 'foo.com/a/b/d' as the URL
Note that the absolute paths are even worse, in that the XHR fetches don't even use DocumentRoot
(bar
) any more. Thanks.
EDIT 1
relative case tested on Chrome/Windows and Firefox/Linux with the same results; absolute case tested only on Firefox/Linux.
EDIT 2
I've been experimenting and found two ways to fix this, but I don't really understand what's going on, so I won't post this as an answer:
history
to one level deep, so a/b/c
becomes a-b-c
. All the stuff I've googled on the history API shows only one-level-deep URLs, so maybe this is commonbase
tag to the site's <head>
(<base "href=http://example.com/" target="_blank">
Changing the history state will change the document's URL and thus its baseURI
if there is no other things that did already change it, like a <base>
element.
So all future network requests will indeed be made from this new base URI.
To workaround that, you can manually set back the baseURI after you changed you history state,
console.log( document.baseURI ); // /js
history.pushState(null, null, '/foo');
console.log( document.baseURI ); // /foo
// manually set document's baseURI via <base>
const base = document.createElement('base');
base.href = 'bar';
document.head.append(base);
console.log( document.baseURI ); // /bar
As a jsfiddle for users facing StackSnippet's null origined iframes restrictions
Or you could even just append this <base> element only once in your document from the first page load.