Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save andersonbosa/804e27a70c2c06abb79dc848f1a572be to your computer and use it in GitHub Desktop.
Save andersonbosa/804e27a70c2c06abb79dc848f1a572be to your computer and use it in GitHub Desktop.
Listen to "location" change on SPAs
/**
* Returns a function, that, as long as it continues to be invoked, will not
* be triggered. The function will be called after it stops being called for
* N milliseconds. If `immediate` is passed, trigger the function on the
* leading edge, instead of the trailing
*
* @param { function () : void } func - will be triggered when it stops to be invoked
* @param { Number } wait - time to wait
* @param { boolean } [immediate] - Either to trigger the function on the
* leading edge (truthy `immediate`) or instead of the trailing (falsy `immediate`)
*/
function debounce (func, wait, immediate) {
let timeout
return function execute () {
const context = this
const args = arguments
const later = () => {
timeout = null
if (!immediate) {
func.apply(context, args)
}
}
const callNow = immediate && !timeout
clearTimeout(timeout)
timeout = setTimeout(later, wait)
if (callNow) {
func.apply(context, args)
}
}
}
/**
* Alias from window
*/
//const Event = window.Event
/**
* Location change handler for SPA environment
* It modifies popstate, pushstate and replacestate
* functions so that all fire a custom locationchange
*/
const spaLocationHandler = {
/**
* Flag to avoid many recasting of
* same function
*/
firstInitialization: true,
/**
* Dispatches two new events [`pushState`, `locationchange`]
* applying old arguments to this method as well
* @param {Function} oldPushState - native code
*/
recastPushState: oldPushState => {
return function pushState () {
const revised = oldPushState.apply(this, arguments)
window.dispatchEvent(new Event('pushState'))
window.dispatchEvent(new Event('locationchange'))
return revised
}
},
/**
* Dispatches two new events [`replaceState`, `locationchange`]
* applying old arguments to this method as well
* @param {Function} oldReplaceState - native code
*/
recastReplaceState: oldReplaceState => {
return function replaceState () {
const revised = oldReplaceState.apply(this, arguments)
window.dispatchEvent(new Event('replaceState'))
window.dispatchEvent(new Event('locationchange'))
return revised
}
},
/**
* Dispatch a new event `locationchange`
*/
popstateEventDispatcher: () => {
window.dispatchEvent(new Event('locationchange'))
},
/**
* Trigged when page "location" changes
*/
onLocationChangeHandler: () => {
console.log('***** onLocationChangeHandler')
},
/**
* Initialize native functions rectification
* Note: This handler does not modify the previous
* functionalities, it just adds a custom dispatcher
*/
init: () => {
const self = spaLocationHandler
if (!self.firstInitialization) {
return
}
/**
* Builds the new `locationchange` event to
* be used on any url change
*/
window.history.pushState = self.recastPushState(window.history.pushState)
window.history.replaceState = self.recastReplaceState(window.history.replaceState)
window.addEventListener('popstate', self.popstateEventDispatcher)
/**
* New event available to atach into window
*/
window.addEventListener('locationchange', debounce(self.onLocationChangeHandler, 2000))
self.firstInitialization = false
}
}
spaLocationHandler.init()
// test
window.addEventListener('locationchange', evt => {
console.log('**** locationchange', evt)
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment