Skip to content

Detect when users click an exact active router-link (current page === router-link's to) #1155

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
ghost opened this issue Feb 10, 2017 · 21 comments

Comments

@ghost
Copy link

ghost commented Feb 10, 2017

It would be great if we will be able to at least detect when our users click the same router-link again.

Let me explain:

Let's say I have an app with fixed navbar on top and a user have navigated to a long page (let it be /news) and at one moment after long scrolling (let the page have infinite scroll) the user decides to click the same link /news again (from the navbar), obviously to go up (or to refresh the page, who knows), but the problem is that nothing will happen (it feels like the link is dead), and as far as I know there is no way to capture multiple clicks on the same route-link without using some hacky workarounds.

The problem can be solved for example by adding a timestamp to query string so every time user clicks the same link the navigation will happen again, but it feels so wrong, because the only thing basically what should happen is the scroll to an anchor or scroll to the top.

So, it would be great to have an option to detect when users click the same router-link multiple times without hacky workarounds, or at least an option to scroll to the top on secondary click.

If there is an elegant option to do it now, please tell me, because I didn't find such feature in the documentation.

@posva
Copy link
Member

posva commented Feb 10, 2017

Hey, you can actually add an event listener like @click.native: http://jsfiddle.net/posva/5ra5asny/
This will be triggered every time 🙂

@posva posva closed this as completed Feb 10, 2017
@egoist
Copy link

egoist commented Mar 6, 2017

@posva hmm seems he's asking how to only trigger a function when you click on the same route, in your example when I click home on foo page the event will also be fired

@LinusBorg
Copy link
Member

If there is an elegant option to do it now, please tell me, because I didn't find such feature in the documentation.

beforeRoueUpdate() should work nicely, no? if to and from have the same .path, that's a "refresh" click.

@posva
Copy link
Member

posva commented Mar 6, 2017

@egoist Yes, you're right. In that case the user can check that the route is the same in the click event. Maybe we can fire a new event when the user clicks on the router-link while it's the current route. But currently, you can achieve that on userland already
edit: the beforeRouteUpdate wasn't a thing back there 😆 . But it shouldn't fire if the route didn't change eg: a rout with no params

@ghost
Copy link
Author

ghost commented Mar 6, 2017

@posva I've tried to check if the route is the same in the click event, but it seems that the event is fired after navigation, because when I tried to compare current route with the route from link they were same all the time.

P.S: For now, I've solved my issue from the first post with a wrapper component for <router-link/> which also fires an event on each navigation so I can do my scroll :)

@egoist
Copy link

egoist commented Mar 6, 2017

@LinusBorg

beforeRoueUpdate() should work nicely, no? if to and from have the same .path, that's a "refresh" click.

seems no, as per doc: // called when the route that renders this component has changed

@LinusBorg
Copy link
Member

Oh, nevermind ^^

@egoist
Copy link

egoist commented Mar 6, 2017

So maybe reopen this?

@fnlctrl
Copy link
Member

fnlctrl commented Mar 6, 2017

Why does it have to be the second time? Why not simply scroll to the top on every click?

@posva
Copy link
Member

posva commented Mar 6, 2017

Yes, the fact that the click triggers after the navigation mean that you have to manually handle everything or use a query like ?t=${Date.now()} like http://jsfiddle.net/posva/2c2dfmqb/
So, I think we should can emit some kind of custom event like activate when a router-link is clicked while the current route is already the one we have

@posva posva reopened this Mar 6, 2017
@posva posva changed the title [Feature] Detect when users click the same router-link many times. Detect when users click an exact active router-link (current page === router-link's to) Mar 6, 2017
@posva
Copy link
Member

posva commented Mar 6, 2017

@fnlctrl Imaging you're reading a news feed, and click on one of the news to get more details, then you want to go back, but instead of doing back, you click on the news button because it's there, we may want to scroll to where the user was last time he was on the news feeds. I'll personally hit Back, but some people forget about this really often. Also, in a PWA on an iPhone, you may not have a back button

@fnlctrl
Copy link
Member

fnlctrl commented Mar 6, 2017

I think we're complicating the problem here...
What's wrong with clicking the link always resulting in scrolling to the top? If the user clicks the link from another route, it's fine since he would expect it as new contents are loaded. If you want the click handler to be called on the second (or more) click, why not for the first click?

<router-link to='/foo' @click.native="toTop"/>
methods: {
  toTop: {window.scrollTo(0,0)}
}

@posva That's not what OP originally asked for. From my perspective, rendering a dedicated a back button would be most appropriate for this use case.

@posva
Copy link
Member

posva commented Mar 6, 2017

@posva That's not what OP originally asked for. From my perspective, rendering a dedicated a back button would be most appropriate for this use case

I didn't express myself correctly. I also think that way 😆

@ghost
Copy link
Author

ghost commented Mar 6, 2017

@fnlctrl Yes, you're right, and your example shows that I was trying to acheive, but, for now, you will have to repeat that @click.native="toTop" almost on every <router-link/>, and if multiple components also have links, you will have to repeat the method too, or to extract it to a dedicated file and to include it in methods list every time.

As I wrote in another comment, I've solved this with a wrapper component for <router-link/> which also adds a @click.native with the scroll method, but having just an event would be better, as the click event is fired after navigation so we can not detect if route was changed or not, if for some reason we may need this.

P.S: Also, the sensation of 'dead' links is not user firendly, because when user clicks a link (even the second time), he/she expects at least something to happen (scroll, in our case).

@fnlctrl
Copy link
Member

fnlctrl commented Mar 6, 2017

Well, I think duplicating @click.native="toTop" is still better than a complicated mechanism with vue-router.
To reduce duplicated code there are some easy ways, e.g.

  1. Bind the click event to the container
<div class="nav-bar" @click="toTop">
  <router-link ....../>
  <router-link ....../>
  <router-link ....../>
  ...
</div>
  1. Add toTop to Vue's prototype
Vue.prototype.$scrollToTop = () => window.scrollTo(0,0)

And it can be used everywhere including templates.

<router-link @click.native="$scrollToTop">

@egoist
Copy link

egoist commented Mar 6, 2017

What if I don't really want to scrolling to the top when switching to a new route 😂 I want to handle that in the mounted function of that route, the content of new route might be loaded asynchronously and if we scroll to top before the content loaded and then scroll to top again in that route, it will just look weird. Especially scroll with animation.

@fnlctrl
Copy link
Member

fnlctrl commented Mar 6, 2017

What if I don't really want to scrolling to the top when switching to a new route

...I guess you can just don't bind a click event?
or something like

<div class="nav-bar" @click="toTop">
  <router-link @click.native.stop ....../> // now clicking this won't scroll to the top
  <router-link ....../>
  <router-link ....../>
  ...
</div>

I want to handle that in the mounted function of that route, the content of new route might be loaded asynchronously and if we scroll to top before the content loaded, it will just look weird. Especially scroll with animation.

Then it's not suitable to be handled by vue-router anyway, as it's supposed to be handled by userland.

@ghost
Copy link
Author

ghost commented Mar 6, 2017

@fnlctrl In that case, to my mind, a wrapper component seems to look better, and we can use it like <scroll-link/> every time we need the scroll:

<template lang="pug">
    router-link(
        :to="to",
        :tag="tag",
        :exact="exact",
        :append="append",
        :replace="replace",
        :activeClass="activeClass",
        :event="event",
        @click.native="scroll"
    ): slot
</template>

<script>
    export default {
        props: [ /* .. */ ],
        methods: { 
            scroll() {
                window.scrollTo(0,0);
            }
        }
    }
</script>

@fnlctrl
Copy link
Member

fnlctrl commented Mar 6, 2017

Yeah, it looks nice indeed. Closing as the issue is now resolved in userland ;)

@fnlctrl fnlctrl closed this as completed Mar 6, 2017
@mig-hub
Copy link

mig-hub commented Jan 10, 2019

I am having the same problem while trying to implement a menu which comes back to home when clicking on the active link. And like it was mentioned I cannot compare the link's to property with the current route's path because the route changes before.

When searching the documentation, I was expecting a callback like router.beforeEach which happens on any router link before router.beforeEach. Such a callback most likely exist and is not exposed because there is a click event which decides if the route needs to change or not.

@posva
Copy link
Member

posva commented Jan 10, 2019

See #974

@vuejs vuejs locked as resolved and limited conversation to collaborators Jan 10, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants