I am disabling document body scroll when my mobile nav is open which is working as expected. However it was not removing the overflow hidden when user would click a link to another route/page. I created a simple method to remove the overflow hidden when a link is clicked in the nav when the menu is open, which does work with one small caveat. When the user is on say page "home" and the mobile nav is OPEN, when they click the link "home" inside of the mobile nav it is closing the menu, and I understand I have done that with the method I created. Is there a way to prevent that event from firing when clicking the link of the page you are on?
<header :class="{ 'header-active': activeHamburger }">
<nav class="nav-row nav-row--primary" aria-label="Main Navigation">
<ul class="row--flex justify--space-between">
<li>
<router-link to="/" #click="removeOverflowHidden();
">
home
</router-link>
</li>
<li>
<router-link to="About" #click="removeOverflowHidden();
">
about
</router-link>
</li>
<li>
<router-link to="Work" #click="removeOverflowHidden();
">
work
</router-link>
</li>
<li>
<router-link to="Contact" #click="removeOverflowHidden();
">
contact
</router-link>
</li>
</ul>
</nav>
</header>
data() {
return {
activeHamburger: false
};
},
watch: {
activeHamburger: function() {
if (this.activeHamburger) {
document.documentElement.style.overflow = "hidden";
return;
}
document.documentElement.style.overflow = "auto";
}
},
methods:{
removeOverflowHidden() {
this.activeHamburger = false;
}
}
You can pass the route value to the method and check that it's not same as the current route before executing.
<template>
<header :class="{ 'header-active': activeHamburger }">
<nav class="nav-row nav-row--primary" aria-label="Main Navigation">
<ul class="row--flex justify--space-between">
<li>
<router-link to="/" #click="removeOverflowHidden('home');
">
home
</router-link>
</li>
<li>
<router-link to="About" #click="removeOverflowHidden('about');
">
about
</router-link>
</li>
<li>
<router-link to="Work" #click="removeOverflowHidden('work');
">
work
</router-link>
</li>
<li>
<router-link to="Contact" #click="removeOverflowHidden('contact');
">
contact
</router-link>
</li>
</ul>
</nav>
</header>
</template>
<script>
export default {
data() {
return {
activeHamburger: false
};
},
watch: {
activeHamburger: function() {
if (this.activeHamburger) {
document.documentElement.style.overflow = "hidden";
return;
}
document.documentElement.style.overflow = "auto";
}
},
methods:{
removeOverflowHidden(value) {
if (this.$route.path !== value) {
this.activeHamburger = false;
}
}
}
}
</script>
I haven't seen your routes but you can also use this.$route.name if you prefer and adjust accordingly the values you pass to the method.
Related
I've a header components who's rendered in the layout default.vue and when the menu is open, it takes the entire page and hide the content. When i click on the link i want the menu to wait if the page where the user is redirect finished to load. But i dont see any "hooks" that tell if the page is loaded or not.
<template>
<header class="the-header" :class="{ 'the-header--open': isNavOpen }">
<div class="the-header__container container">
<a href="#" class="the-header__brand" #click="onToggleTheme" #mouseenter="$nuxt.$emit('cursor-enter', $event)" #mouseleave="$nuxt.$emit('cursor-leave', $event)">
<img src="~/assets/img/logo.png" alt="Logo" />
</a>
<div class="the-header__menu">
<div #click="onToggleNav" #mouseenter="$nuxt.$emit('cursor-enter', $event)" #mouseleave="$nuxt.$emit('cursor-leave', $event)" class="the-header__burger">
<span></span>
<span></span>
<span></span>
</div>
</div>
</div>
<nav class="the-header__nav">
<nuxt-link
to="/"
#click.native="onRedirection"
class="the-header__link container"
>
<span
class="the-header__link-text hovered--no-clip"
#mouseenter="$nuxt.$emit('cursor-enter', $event)"
#mouseleave="$nuxt.$emit('cursor-leave', $event)"
>
Accueil
</span>
</nuxt-link>
<nuxt-link
to="/about"
#click.native="onRedirection"
class="the-header__link container"
>
<span
class="the-header__link-text hovered--no-clip"
#mouseenter="$nuxt.$emit('cursor-enter', $event)"
#mouseleave="$nuxt.$emit('cursor-leave', $event)"
>
A propos
</span>
</nuxt-link>
</nav>
</header>
</template>
<script>
export default {
data() {
return {
isNavOpen: false,
};
},
mounted() {
this.$nextTick(() => {
this.$nuxt.$loading.start()
setTimeout(() => this.$nuxt.$loading.finish(), 500)
})
},
methods: {
onToggleNav() {
this.isNavOpen = !this.isNavOpen;
},
onRedirection() {
setTimeout(() => {
this.isNavOpen = false;
}, 500);
},
onToggleTheme() {
let currentTheme = document.documentElement.getAttribute("data-theme");
let targetTheme = "dark";
if (currentTheme === "dark") {
targetTheme = "light";
}
document.documentElement.setAttribute("data-theme", targetTheme);
},
},
};
</script>
So I put a setTimeout on the loading function with a minimum of 500ms, but if the loading take more time then the effet doesnt work :/
sry for bad english (i'm french)
WORKAROUND FOUND, SEE EDIT4
my router is acting a bit funny.
It works perfectly for the first link you click, however It doesn't apply the active class after the first time, if I refresh the page it will apply. you can see in the gif here:
Here is my navigation bar code:
<template>
<div class="nav">
<div class="nav__logo-box">
<img src="/logo-no-text-morning.png" :class="$mq" alt />
</div>
<div class="nav__nav-list">
<div class="nav__link">
<router-link to="/" exact-active-class="active-page">
<i class="fas fa-horse-head fa-3x"></i>
<p>Home</p>
</router-link>
</div>
<div class="nav__link">
<router-link to="/about" exact-active-class="active-page">
<i class="fas fa-info-circle fa-3x"></i>
<p>About</p>
</router-link>
</div>
<div class="nav__link">
<router-link to="/gallery" exact-active-class="active-page">
<i class="fas fa-images fa-3x"></i>
<p>Gallery</p>
</router-link>
</div>
<div class="nav__link">
<router-link to="/contact" exact-active-class="active-page">
<i class="fas fa-envelope-open-text fa-3x"></i>
<p>Contact</p>
</router-link>
</div>
</div>
</div>
</template>
Here is my router index.ts
import Vue from "vue"; import VueRouter, { RouteConfig } from "vue-router"; import Home from "../views/Home.vue"; Vue.use(VueRouter); const routes: Array<RouteConfig> = [ {
path: "/",
name: "Home",
component: Home }, {
path: "/about",
name: "About",
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import("#/views/About.vue") }, {
path: "/contact",
name: "Contact",
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import("#/views/Contact.vue") }, {
path: "/gallery",
name: "Gallery",
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import("#/views/Gallery.vue") } ]; const router = new VueRouter({ routes }); export default router;
EDIT:
here's the a look at the web browser debugger:
It for some reason is adding an router-link-active to the home page (probably because its path is just "/").
And then it will add the exact-active just once, but then clicking on another page it will not change. it will stay on the page you was on before, only way to make it change is to refresh the page.
EDIT2:
So now I have decided to store the current page using the following code:
methods: {
getCurrentPage: function() {
this.currentPage = this.$router.currentRoute.name;
}
},
However you can see the behaivour here, it again doesn't update properly and lags behind, it's maybe one or two pages behind where I actaully am.
EDIT3:
I have tried to use a computed method to watch instead but it does not update, it just gets the first page its on and doesnt update again.
data: function() {
return {
currentPage: ""
};
},
computed: {
getCurrentPage: function() {
return this.$router.currentRoute.name;
}
},
mounted() {
console.log(this.$router.currentRoute.name);
this.currentPage === this.getCurrentPage;
}
EDIT4: (WORKAROUND FOUND)
So with the help of the comments, I've got a workaround that will work for now. it's this:
computed: {
getCurrentPage: function() {
return this.$route.name;
}
}
On the links I then bind the active page class:
<div class="nav__nav-list">
<div class="nav__link">
<router-link to="/" :class="{ currentpage: getCurrentPage === 'Home' }">
<i class="fas fa-horse-head fa-3x"></i>
<p ref="homeLink">Home</p>
</router-link>
</div>
<div class="nav__link">
<router-link to="/about" :class="{ currentpage: getCurrentPage === 'About' }">
<i class="fas fa-info-circle fa-3x"></i>
<p ref="aboutLink">About</p>
</router-link>
</div>
<div class="nav__link">
<router-link to="/gallery" :class="{ currentpage: getCurrentPage === 'Gallery' }">
<i class="fas fa-images fa-3x"></i>
<p ref="galleryLink">Gallery</p>
</router-link>
</div>
<div class="nav__link">
<router-link to="/contact" :class="{ currentpage: getCurrentPage === 'Contact' }">
<i class="fas fa-envelope-open-text fa-3x"></i>
<p ref="contactLink">Contact</p>
</router-link>
</div>
</div>
In my template, I have
<ul class="nav nav-pills nav-fill">
<li class="nav-item">
<router-link
:to="{
name: 'ConversationDetailHighlights',
params: {
id: conversation.id
}
}"
class="nav-link"
active-class="active"
v-bind:class="{active: currentlyActive === 'ConversationDetailHighlights'}"
>Highlights</router-link>
</li>
<li class="nav-item">
<router-link
:to="{
name: 'ConversationDetailFullTranscript',
params: {
id: conversation.id
}
}"
class="nav-link"
active-class="active"
v-bind:class="{active: currentlyActive === 'ConversationDetailFullTranscript'}"
>Full Transcript</router-link>
</li>
</ul>
And in my script:
data() {
return {currentlyActive = 'ConversationDetailHighlights'}
}
watch: {
$route(to) {
this.currentlyActive = to.name;
}
},
mounted() {
this.currentlyActive = this.$route.name;
},
However, when I click the Transcript, both li's are active. How do I get the highlights one to become inactive?
You are doing this far too complicated. vue-router already contains all you need to set a css-class on the active <router-link> components.
To quote from Getting Started
Notice that a automatically gets the .router-link-active class when its target route is matched. You can learn more about it in its API reference.
So all you need to do is style the .active css-class that you chose with the active-class prop.
Note that multiple routes may match at any one time. E.g. / will always match. In that case you should put the exact attribute:
<router-link to="/" exact active-class="active" />
I've been working on a sub menu system for vue.js that gets populated by the current route's children. I recently posted a question about it and it was answered here.
Now I'm trying to improve upon that but I'm having trouble finding out how to get a component's path or namespace (not sure what word to use). Currently I see what I want in the Vue Dev tools I just don't now how to get those properties.
I've tried {{$route.path}} but that only gives me the full path.
Another thing I've tried which kind of helps is storing the current path the first time I load the menu. Which preserves the path I want to be appending to. The only issue is when i navigate directly to the page it loads the menu with the pages url which then breaks the functionality.
Here is the code:
<template>
<nav id="sidebar">
<div class="sidebar-header">
<h3>Bootstrap Sidebar</h3>
</div>
<h2>Route: {{ }}</h2>
<ul class="list-unstyled components" v-for="(route, index) in $router.options.routes.filter(x=>x.path==path)">
<li v-for="child in route.children">
<a class="nav-item" :key="index">
<router-link :to="{path: path+'/'+child.path}" exact-active-class="active">
<icon :icon="route.icon" class="mr-2" /><span>{{ child.path }}</span>
</router-link>
</a>
</li>
</ul>
</nav>
</template>
<script>
export default {
data() {
return {
path: this.$route.path
}
},
methods: {
},
}
</script>
I really want something closer to this though, where instead of using $route.path to return the full path like /traveler/Create I want something to just return /traveler or whatever the path for it's router-view is:
<template>
<nav id="sidebar">
<div class="sidebar-header">
<h3>Bootstrap Sidebar</h3>
</div>
<ul class="list-unstyled components" v-for="(route, index) in $router.options.routes.filter(x=>x.path==$route.path)">
<li v-for="child in route.children">
<a class="nav-item" :key="index">
<router-link :to="{path: $route.path+'/'+child.path, params: { idk: 1 }}" exact-active-class="active">
<icon :icon="route.icon" class="mr-2" /><span>{{ child.path }}</span>
</router-link>
</a>
</li>
</ul>
</nav>
</template>
<script>
import { travelerroutes } from '../../router/travelerroutes'
export default {
data() {
console.log(travelerroutes);
return {
travelerroutes,
collapsed: true
}
},
methods: {
toggleCollapsed: function (event) {
this.collapsed = !this.collapsed
}
}
}
</script>
To get the path of the current component I had to just use the $Route.matched property. In my case because I didn't want to include the childrens' paths I used the first match like this $Route.matched[0].path
you can learn more about it here
I also used it to update my other question/answer
Essentially you can use it in a template like this:
<template>
<nav id="sidebar">
<div class="sidebar-header">
<h3>Bootstrap Sidebar</h3>
</div>
<ul class="list-unstyled components" v-for="(route, index) in $router.options.routes.filter(x=>x.path==$route.matched[0].path)">
<li v-for="child in route.children">
<a class="nav-item" :key="index">
<router-link :to="route.path+'/'+child.path" exact-active-class="active">
<icon :icon="route.icon" class="mr-2" /><span>{{ child.name }}</span>
</router-link>
</a>
</li>
</ul>
</nav>
</template>
<script>
export default {
data() {
return {
}
}
}
</script>
I'm doing this simple hamburger menu in pure HTML and CSS (from CodeMyUI), and I would like it to auto close when a link is clicked. Using vue with router-links the page isn't reloaded.
<template>
<nav>
<input type="checkbox" id="menu" />
<label for="menu"></label>
<div class="menu-content">
<router-link to="/" class="mainlink">{{ $t("message.navFront") }}</router-link>
<router-link to="/about" class="mainlink">{{ $t("message.navAbout") }}</router-link>
<router-link to="/portfolio" class="mainlink">{{ $t("message.navPort") }}</router-link>
</div>
</nav>
</template>
Have you tried using v-model to bind the value?
<input type="checkbox" id="menu" v-model="checkboxValue />
<router-link to="/" class="mainlink" #click.native="uncheck">{{ $t("message.navFront") }}</router-link>
Then you should at the checkboxValue to the data() and the uncheck method to methods.
data() {
return {
checkboxValue: false,
}; },
methods:{
uncheck(){
this.checkboxValue = false;
}}
With this you should be able to deselect the checkbox once you click on the links.
Edit: So apparently the routes-link element doesn't support #click, but it might support #click.native (or v-on:click.native)
you need to uncheck <input type="checkbox" id="menu" /> when a click happens on a link within the menu.
with a nice vue event explained like here: https://v2.vuejs.org/v2/guide/events.html#Method-Event-Handlers
maybe somehow like this:
template
<router-link to="/" class="mainlink" v-on:click="uncheck"> {{ $t("message.navFront") }} </router-link>
js
methods: {
uncheck: function (event) {
document.getElementById("menu").checked = false;
}
}