Reputation: 7715
I'm using Vue router with two pages:
let routes = [
{
path: '/',
component: require('./components/HomeView.vue')
},
{
path: '/intro',
component: require('./components/IntroView.vue')
}
]
This works fine, except that each of my components has different body styling:
HomeView.vue:
<template>
<p>This is the home page!</p>
</template>
<script>
export default {
}
</script>
<style>
body {
background: red;
}
</style>
IntroView.vue:
<template>
<div>
<h1>Introduction</h1>
</div>
</template>
<script>
export default {
}
</script>
<style>
body {
background: pink;
}
</style>
My goal is to have these two pages have different background styles (eventually with a transition between them). But at the moment when I go to the home
route (with the red
background), then click the intro
route, the background colour stays red
(I want it to change to pink
).
Edit: index.html:
<body>
<div id="app">
<router-link to="/" exact>Home</router-link>
<router-link to="/intro">Introduction</router-link>
<router-view></router-view>
</div>
<script src="/dist/build.js"></script>
</body>
Upvotes: 28
Views: 39396
Reputation: 7715
I got it working with the lifecycle hook beforeCreate
and a global stylesheet. In global.css
:
body.home {
background: red;
}
body.intro {
background: pink;
}
In the <script>
section of HomeView.vue
:
export default {
beforeCreate: function() {
document.body.className = 'home';
}
}
And similar in IntroView.vue
.
Upvotes: 64
Reputation: 1508
Top answer is right, but need some optimization.
Because that answer doesn't work when one refreshes that page. Reason is that dom is not loaded done when set the style you want.
So, better solution is this:
beforeCreate() {
this.$nextTick(() => {
document.querySelector('body').style.backgroundColor = '#f2f2f2'
})
},
beforeDestroy() {
document.querySelector('body').style.backgroundColor = ''
},
By wrapping style setting handler in this.$nextTick
, the style will be set when dom is loaded. So you can get correct styles when refresh page
Upvotes: 2
Reputation: 1
You can use scoped attribute in the style element. Then the style will be limited only to that vue file.
HomeView.vue:
<template>
<p>This is the home page!</p>
</template>
<script>
export default {
}
</script>
<style scoped>
body {
background: red;
}
</style>
IntroView.vue:
<template>
<div>
<h1>Introduction</h1>
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
body {
background: pink;
}
</style>
Upvotes: -2
Reputation: 12711
You can also do it directly in the router file using the afterEach
hook:
mainRouter.afterEach((to) => {
if (["dialogs", "snippets"].includes(to.name)) {
document.body.style.backgroundColor = "#F7F7F7";
// or document.body.classList.add(className);
} else {
document.body.style.backgroundColor = "#FFFFFF";
// or document.body.classList.remove(className);
}
});
to
is a route object which contains the route name (if named), path, etc. Documentation for all the props
Upvotes: 0
Reputation: 231
watch: {
$route: {
handler (to, from) {
const body = document.getElementsByTagName('body')[0];
if (from !== undefined) {
body.classList.remove('page--' + from.name.toLowerCase());
}
body.classList.add('page--' + to.name.toLowerCase());
},
immediate: true,
}
},
Another fairly simple solution, add it to your base App.vue file. The to.name can be replaced with to.meta.class or similar for something more specific. This is a nice do it once and it works forever type solution though.
Upvotes: 8
Reputation: 7687
I ran into an issue when I wanted to modify the styles of the html
and body
tags along with the #app
container on specific routes and what I found out is that for various reasons, this can be quite complicated.
After reading through:
In your App.vue (could be considered as the centralised state):
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'my-app',
methods: {
handleStyles () {
// Red style to the body tag for the home page
if (['/'].includes(this.$route.path)) document.body.className = 'bg-red'
// Pink style to the body tag for all other pages
else if (document.body.classList.contains('bg-red')) document.body.className = 'bg-pink'
}
},
// Handle styles when the app is initially loaded
mounted () {
this.handleStyles()
},
// Handle styles when the route changes
watch: {
'$route' () {
this.handleStyles()
}
}
}
</script>
<style>
.bg-red {
background: red;
}
.bg-pink {
background: pink;
}
</style>
So for the route /
you get the red style and for all other routes the pink style is applied.
The handleStyles
logic could have been dealt with by the beforeCreated
hook however in my case, this would only affect the html
and body
styles but the #app
element where the router view is rendered into would only available when the dom has been mounted so I think that it is a slightly more extensible solution.
Upvotes: 3
Reputation: 16513
If the class is view specific, may be this will help
methods: {
toggleBodyClass(addRemoveClass, className) {
const el = document.body;
if (addRemoveClass === 'addClass') {
el.classList.add(className);
} else {
el.classList.remove(className);
}
},
},
mounted() {
this.toggleBodyClass('addClass', 'mb-0');
},
destroyed() {
this.toggleBodyClass('removeClass', 'mb-0');
},
Move the methods
section to a mixin and then the code can be DRY.
Upvotes: 4
Reputation: 160
Alternatively you can use this
It allows to control your page body classes with vue-router. Wrote this when faced the similar issue. It also refers to Add a class to body when component is clicked?
Upvotes: 3