Skip to content

Commit cc2a2b3

Browse files
committed
Repeated clicks on the same hash link work
Also divorced some internal logic for the $scroll plugin. Related to Issues #31, #32
1 parent a6e9e4c commit cc2a2b3

File tree

3 files changed

+46
-18
lines changed

3 files changed

+46
-18
lines changed

components/element/Link.vue

+13-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ to type it absolutely everywhere.
1111
-->
1212

1313
<template>
14-
<NuxtLink :to="path" :class="isActive">
14+
<!-- XXX @click.native will stop working in Vue v3+ (Vue Router v4+), but @click should start working -->
15+
<NuxtLink :to="path" :class="isActive" @click.native="navigate">
1516
<slot />
1617
</NuxtLink>
1718
</template>
@@ -40,5 +41,16 @@ export default Vue.extend({
4041
return this.to.startsWith('#');
4142
},
4243
},
44+
methods: {
45+
/**
46+
* Works around a bug in Vue where anchor links don't work repeatedly.
47+
* This feature normally works in browsers, so people exepct it to work.
48+
*/
49+
navigate(): void {
50+
if (this.isHash && this.path === this.$route.hash) {
51+
this.$scroll.toHash(this.path);
52+
}
53+
},
54+
},
4355
});
4456
</script>

layouts/default.vue

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ export default Vue.extend({
1818
},
1919
watch: {
2020
$route(): void {
21-
this.$scroll(this.$route);
21+
this.$scroll.onNavigation(this.$route);
2222
},
2323
},
2424
mounted(): void {
25-
this.$scroll(this.$route);
25+
this.$scroll.onNavigation(this.$route);
2626
// Provide calls to this.$gtag here to update Analytics
2727
},
2828
});

plugins/scroll.client.ts

+31-15
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
11
import { Plugin } from '@nuxt/types';
22
import { Route } from 'vue-router';
33

4-
declare module 'vue/types/vue' {
5-
interface Vue {
6-
$scroll($route: Route): void;
7-
}
8-
}
9-
104
/**
115
* How does this work? A good question. It may be buggy.
126
*
@@ -26,23 +20,45 @@ declare module 'vue/types/vue' {
2620
* in the layout for the pages. If it's broken, first confirm your page's
2721
* layout does call this.$scroll() inside of watch on $route.
2822
*/
29-
const scrollPlugin: Plugin = (_, inject) => {
30-
let historyKeyMax = 0;
3123

32-
inject('scroll', ($route: Route): void => {
24+
const scroll = {
25+
/**
26+
* To be called on navigation
27+
*/
28+
onNavigation($route: Route): void {
3329
const historyKey = +history.state.key;
34-
if (historyKeyMax < historyKey) {
35-
historyKeyMax = historyKey;
30+
if (this._historyKeyMax < historyKey) {
31+
this._historyKeyMax = historyKey;
3632
if (!$route.hash) {
3733
globalThis.scrollTo(0, 0);
3834
} else {
3935
// Take over the browser's usual job of scrolling, because some won't
40-
const target = globalThis.document.getElementById($route.hash.slice(1));
41-
const boundingClientRect = target?.getBoundingClientRect();
42-
globalThis.scrollBy(0, boundingClientRect?.y ?? 0);
36+
this.toHash($route.hash);
4337
}
4438
}
45-
});
39+
},
40+
/**
41+
* Moves to a given hash on a page if it exists. Can be given without or without a # sign
42+
*/
43+
toHash(hash: string): void {
44+
if (hash.startsWith('#')) {
45+
hash = hash.slice(1);
46+
}
47+
const target = globalThis.document.getElementById(hash);
48+
const boundingClientRect = target?.getBoundingClientRect();
49+
globalThis.scrollBy(0, boundingClientRect?.y ?? 0);
50+
},
51+
_historyKeyMax: 0,
4652
};
4753

54+
const scrollPlugin: Plugin = (_, inject) => {
55+
inject('scroll', scroll);
56+
};
57+
58+
declare module 'vue/types/vue' {
59+
interface Vue {
60+
$scroll: typeof scroll;
61+
}
62+
}
63+
4864
export default scrollPlugin;

0 commit comments

Comments
 (0)