Skip to content

Instantly share code, notes, and snippets.

@bathos
Created October 11, 2017 03:17
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bathos/d06723c3dbbe57cfb0be4902950dbd05 to your computer and use it in GitHub Desktop.
Save bathos/d06723c3dbbe57cfb0be4902950dbd05 to your computer and use it in GitHub Desktop.
urlchange.js
// This module installs a custom global DOM event, 'urlchange', which is
// dispatched when the URL _semantically_ changes for any reason.
//
// It also alters URL behavior more generally. The URL is kept consistently in a
// canonical form, such that differences in the order of query parameters (other
// than same-key parameters, whose order is potentially meaningful) are
// eliminated. While the event is not fired on load, URL canonicalization may be
// performed.
//
// Like HashChangeEvent, URLChangeEvent has two properties, 'newURL' and
// 'oldURL', except they are instances of URL rather than strings.
class URLChangeEvent extends CustomEvent {
constructor(newURL, oldURL) {
super('urlchange');
this.newURL = newURL;
this.oldURL = oldURL;
}
}
const { pushState, replaceState } = History.prototype;
Object.defineProperties(History.prototype, {
pushState: {
value() {
pushState.apply(this, arguments);
respond();
}
},
replaceState: {
value() {
replaceState.apply(this, arguments);
respond();
}
}
});
const respond = () => {
const newURL = new URL(location);
newURL.searchParams.sort();
if (String(newURL) !== location.href) {
history.replaceState(undefined, undefined, newURL);
return;
}
if (String(newURL) !== String(oldURL)) {
const event = new URLChangeEvent(newURL, oldURL);
oldURL = newURL;
console.debug(`Emitting URLChange global DOM event: ${ newURL }`);
window.dispatchEvent(event);
}
};
let oldURL = new URL(location);
oldURL.searchParams.sort();
if (String(oldURL) !== location.href) {
replaceState.call(history, undefined, undefined, oldURL);
}
window.addEventListener('hashchange', respond);
window.addEventListener('popstate', respond);
@bathos
Copy link
Author

bathos commented Oct 11, 2017

class PathChangeEvent extends CustomEvent {
  constructor(newPath, oldPath) {
    super('pathchange');

    this.newPath = newPath;
    this.oldPath = oldPath;
  }
}

window.addEventListener('urlchange', ({ newURL, oldURL }) => {
  if (newURL.pathname !== oldURL.pathname) {
    console.debug(`Emitting PathChange global DOM event: ${ newURL.pathname }`);
    window.dispatchEvent(new PathChangeEvent(newURL.pathname, oldURL.pathname));
  }
});


class SearchChangeEvent extends CustomEvent {
  constructor(newSearchParams, oldSearchParams) {
    super('searchchange');

    this.newSearchParams = newSearchParams;
    this.oldSearchParams = oldSearchParams;
  }
}

window.addEventListener('urlchange', ({ newURL, oldURL }) => {
  if (newURL.search !== oldURL.search) {
    console.debug(`Emitting SearchChange global DOM event: ${ newURL.search }`);

    window.dispatchEvent(
      new SearchChangeEvent(newURL.searchParams, oldURL.searchParams)
    );
  }
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment