Detect Back Button in Navigation Guards of Vue-Router

24,366

Solution 1

This is the only way that I've found:

We can listen for popstate, save it in a variable, and then check that variable

// This listener will execute before router.beforeEach only if registered
// before vue-router is registered with Vue.use(VueRouter)

window.popStateDetected = false
window.addEventListener('popstate', () => {
  window.popStateDetected = true
})


router.beforeEach((to, from, next) => {
  const IsItABackButton = window.popStateDetected
  window.popStateDetected = false
  if (IsItABackButton && from.meta.someLogica) {
    next(false) 
    return ''
  }
  next()
})

Solution 2

As stated by @Yuci, all the router hook callbacks are performed before popstate is updated (and therefore not helpful for this use case)

What you can do:

methods: {
    navigate(location) {
        this.internalNavigation = true;
        this.$router.push(location, function () {
            this.internalNavigation = false;
        }.bind(this));
    }
}
  1. Wrap 'router.push' with you own 'navigate' function
  2. Before calling router.push, set 'internalNavigation' flag to true
  3. Use vue router 'oncomplete' callback to set internalNavigation flag back to false

Now you can check the flag from within beforeEach callback and handle it accordingly.

router.beforeEach((to, from, next) => {
  if ( this.internalNavigation ) {
      //Do your stufff
  }
  next()
})

Solution 3

Slight improvement to @yair-levy answer.

Wrapping push to own navigate method is not convenient because you usually want to call push() from various places. Instead, router original methods can be patched in one place without changes in remaining code.

Following code is my Nuxt plugin to prevent navigation triggered by back/forward buttons (used in Electron app to avoid back caused by mouse additional "back" button, which makes mess in Electron app) Same principle can be used for vanilla Vue and to track common back button together with your custom handling.

export default ({ app }, inject) => {
  // this is Nuxt stuff, in vanilla Vue use just your router intances 
  const { router } = app

  let programmatic = false
  ;(['push', 'replace', 'go', 'back', 'forward']).forEach(methodName => {
    const method = router[methodName]
    router[methodName] = (...args) => {
      programmatic = true
      method.apply(router, args)
    }
  })

  router.beforeEach((to, from, next) => {
    // name is null for initial load or page reload
    if (from.name === null || programmatic) {
      // triggered bu router.push/go/... call
      // route as usual
      next()
    } else {
      // triggered by user back/forward 
      // do not route
      next(false)
    }
    programmatic = false // clear flag
  })
}
Share:
24,366
Asqan
Author by

Asqan

www.muhammetturk.com

Updated on July 23, 2022

Comments

  • Asqan
    Asqan almost 2 years

    How the route is changed, matters for my case. So, I want to catch when the route is changed by a back button of browser or gsm.

    This is what I have:

    router.beforeEach((to, from, next) => {
      if ( /* IsItABackButton && */ from.meta.someLogica) {
        next(false) 
        return ''
      }
      next()
    })
    

    Is there some built-in solutions that I can use instead of IsItABackButton comment? Vue-router itself hasn't I guess but any workaround could also work here. Or would there be another way preferred to recognize it?

  • Asqan
    Asqan over 5 years
    I was expecting a better solution from Navigation-Guards for this problem but probaby this is indeed currently the only thing what we can do about this problem.
  • Yuci
    Yuci over 4 years
    @Nuno Balbona Pérez, it looks like the popstate event handler runs AFTER the router.beforeEach callback. I don't think this would work just as is.
  • PirateApp
    PirateApp over 3 years
    upvoted! where are you putting this code? inside router.js? i am using nuxt here
  • mix3d
    mix3d over 3 years
    Can you explain how this works to detect that a back button was detected? How to use this to solve the OP's written problem? How does the scrollBehavior event fire in relation to beforeEach ?
  • ego
    ego over 3 years
    To take care of calling order problem It's possible to timeout or nextick-wrap beforeEach logic. Not elegant, but this solution works quite well. Bigger problem is that this solution also reacts to forward-btn click - but to be fair question doesn't say anything about it lol
  • Roland Pihlakas
    Roland Pihlakas about 3 years
    You can also add to.meta.fromHistory = (savedPosition !== null); if you need to let the rest of the app know it’s from back/forward.