Skip to content

Commit

Permalink
Repeated clicks on the same hash link work
Browse files Browse the repository at this point in the history
Also divorced some internal logic for the $scroll plugin.

Related to Issues #31, #32
  • Loading branch information
BobChao87 committed Sep 29, 2021
1 parent a6e9e4c commit cc2a2b3
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 18 deletions.
14 changes: 13 additions & 1 deletion components/element/Link.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ to type it absolutely everywhere.
-->

<template>
<NuxtLink :to="path" :class="isActive">
<!-- XXX @click.native will stop working in Vue v3+ (Vue Router v4+), but @click should start working -->
<NuxtLink :to="path" :class="isActive" @click.native="navigate">
<slot />
</NuxtLink>
</template>
Expand Down Expand Up @@ -40,5 +41,16 @@ export default Vue.extend({
return this.to.startsWith('#');
},
},
methods: {
/**
* Works around a bug in Vue where anchor links don't work repeatedly.
* This feature normally works in browsers, so people exepct it to work.
*/
navigate(): void {
if (this.isHash && this.path === this.$route.hash) {
this.$scroll.toHash(this.path);
}
},
},
});
</script>
4 changes: 2 additions & 2 deletions layouts/default.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ export default Vue.extend({
},
watch: {
$route(): void {
this.$scroll(this.$route);
this.$scroll.onNavigation(this.$route);
},
},
mounted(): void {
this.$scroll(this.$route);
this.$scroll.onNavigation(this.$route);
// Provide calls to this.$gtag here to update Analytics
},
});
Expand Down
46 changes: 31 additions & 15 deletions plugins/scroll.client.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import { Plugin } from '@nuxt/types';
import { Route } from 'vue-router';

declare module 'vue/types/vue' {
interface Vue {
$scroll($route: Route): void;
}
}

/**
* How does this work? A good question. It may be buggy.
*
Expand All @@ -26,23 +20,45 @@ declare module 'vue/types/vue' {
* in the layout for the pages. If it's broken, first confirm your page's
* layout does call this.$scroll() inside of watch on $route.
*/
const scrollPlugin: Plugin = (_, inject) => {
let historyKeyMax = 0;

inject('scroll', ($route: Route): void => {
const scroll = {
/**
* To be called on navigation
*/
onNavigation($route: Route): void {
const historyKey = +history.state.key;
if (historyKeyMax < historyKey) {
historyKeyMax = historyKey;
if (this._historyKeyMax < historyKey) {
this._historyKeyMax = historyKey;
if (!$route.hash) {
globalThis.scrollTo(0, 0);
} else {
// Take over the browser's usual job of scrolling, because some won't
const target = globalThis.document.getElementById($route.hash.slice(1));
const boundingClientRect = target?.getBoundingClientRect();
globalThis.scrollBy(0, boundingClientRect?.y ?? 0);
this.toHash($route.hash);
}
}
});
},
/**
* Moves to a given hash on a page if it exists. Can be given without or without a # sign
*/
toHash(hash: string): void {
if (hash.startsWith('#')) {
hash = hash.slice(1);
}
const target = globalThis.document.getElementById(hash);
const boundingClientRect = target?.getBoundingClientRect();
globalThis.scrollBy(0, boundingClientRect?.y ?? 0);
},
_historyKeyMax: 0,
};

const scrollPlugin: Plugin = (_, inject) => {
inject('scroll', scroll);
};

declare module 'vue/types/vue' {
interface Vue {
$scroll: typeof scroll;
}
}

export default scrollPlugin;

0 comments on commit cc2a2b3

Please sign in to comment.