Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 4cee5fd

Browse files
committed
fix($browser): don’t use history api when only the hash changes
IE10/11 have the following problem: When changing the url hash via `history.pushState()` and then reverting the hash via direct changes to `location.href` (or via a link) does not fire a `hashchange` nor `popstate` event. This commit changes the default behavior as follows: Uses `location.href`/`location.replace` if the new url differs from the previous url only in the hash fragment or the browser does not support history API. Use `history.pushState`/ `history.replaceState` otherwise. Fixes #9143 Closes #9406
1 parent 5d04259 commit 4cee5fd

File tree

3 files changed

+31
-1
lines changed

3 files changed

+31
-1
lines changed

src/ng/browser.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,13 @@ function Browser(window, document, $log, $sniffer) {
153153
// setter
154154
if (url) {
155155
if (lastBrowserUrl == url) return;
156+
var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
156157
lastBrowserUrl = url;
157-
if ($sniffer.history) {
158+
// Don't use history API if only the hash changed
159+
// due to a bug in IE10/IE11 which leads
160+
// to not firing a `hashchange` nor `popstate` event
161+
// in some cases (see #9143).
162+
if (!sameBase && $sniffer.history) {
158163
if (replace) history.replaceState(null, '', url);
159164
else {
160165
history.pushState(null, '', url);

src/ng/location.js

+3
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,9 @@ function $LocationProvider(){
682682

683683
if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
684684
if ($location.$$parseLinkUrl(absHref, relHref)) {
685+
// We do a preventDefault for all urls that are part of the angular application,
686+
// in html5mode and also without, so that we are able to abort navigation without
687+
// getting double entries in the location history.
685688
event.preventDefault();
686689
// update location manually
687690
if ($location.absUrl() != $browser.url()) {

test/ng/browserSpecs.js

+22
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,17 @@ describe('browser', function() {
440440
expect(locationReplace).not.toHaveBeenCalled();
441441
});
442442

443+
it('should set location.href and not use pushState when the url only changed in the hash fragment to please IE10/11', function() {
444+
sniffer.history = true;
445+
browser.url('http://server/#123');
446+
447+
expect(fakeWindow.location.href).toEqual('http://server/#123');
448+
449+
expect(pushState).not.toHaveBeenCalled();
450+
expect(replaceState).not.toHaveBeenCalled();
451+
expect(locationReplace).not.toHaveBeenCalled();
452+
});
453+
443454
it('should use location.replace when history.replaceState not available', function() {
444455
sniffer.history = false;
445456
browser.url('http://new.org', true);
@@ -451,6 +462,17 @@ describe('browser', function() {
451462
expect(fakeWindow.location.href).toEqual('http://server/');
452463
});
453464

465+
it('should use location.replace and not use replaceState when the url only changed in the hash fragment to please IE10/11', function() {
466+
sniffer.history = true;
467+
browser.url('http://server/#123', true);
468+
469+
expect(locationReplace).toHaveBeenCalledWith('http://server/#123');
470+
471+
expect(pushState).not.toHaveBeenCalled();
472+
expect(replaceState).not.toHaveBeenCalled();
473+
expect(fakeWindow.location.href).toEqual('http://server/');
474+
});
475+
454476
it('should return $browser to allow chaining', function() {
455477
expect(browser.url('http://any.com')).toBe(browser);
456478
});

0 commit comments

Comments
 (0)