Changing body styles in vue router
Solution 1
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
.
Solution 2
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.
Solution 3
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?
Solution 4
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.
Solution 5
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:
- @Saurabh's answer on another relative question: https://stackoverflow.com/a/42336509/2110294
- @Mteuahasan's comment above regarding Evan You's suggestion
- @GluePear's / OP's answer to this question: https://stackoverflow.com/a/44544595/2110294
- Sass style inclusion headaches: https://github.com/vuejs/vue-loader/issues/110#issuecomment-167376086
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.
Related videos on Youtube
Comments
-
GluePear almost 2 years
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 thered
background), then click theintro
route, the background colour staysred
(I want it to change topink
).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>
-
Mteuahasan almost 7 yearsYou could use the $router object in order to add a class to your body and style it in a global sheet ?
-
John almost 7 yearsall the styles will be available globally. so you solution is the one above
-
GluePear almost 7 yearsEdited my question to include my
index.html
. I can't see how I could add a class tobody
when it's outside of the app. -
Mteuahasan almost 7 yearsI tested your code and this is working fine...You have no errors ? Is the pink background applied but maybe override by red ? (this has no reason to happens tho)
-
Mteuahasan almost 7 yearsEvan You suggestion in order to change body class :
The easy way is just setting document.body.className in a global beforeEach hook.
This solution is two years old but can still be used I guess From : forum-archive.vuejs.org/topic/656/… -
GluePear almost 7 years@Mteuahasan It works until you click back to the first page. E.g. go to home page (red bg), click intro page (it changes to pink), go back to home page (it stays pink, should change back to red).
-
Mteuahasan almost 7 yearsI think the
/intro
style is still there and erase the/
style. Could you confirm ? If so, you should use the body class way (moreover it will be easier to manage, especially if you want to use transitions)
-
-
Syed almost 6 yearsonce added class to body, may be in another view this needs to be removed, should we use
destroyed
lifecycle hook? -
nickpish about 5 yearsThis is a great solution, and seems to append the class name faster than the accepted solution. The only downside is it only loads the class name when actually navigating via routes, i.e. not on initial page load (so if you refresh a single page or directly access a certain page, the class name is not appended.) Not sure if there's a way to modify it to account for this issue aside from combining it w/ the approach detailed in the selected answer?
-
Lomax about 5 yearsAdded the immediate flag to the watcher, but in that case there will be no 'from' route defined, so removal of the class can be skipped.
-
Joonseo Lee over 3 yearsyou save my time!
-
gilly3 over 3 yearsNice (better) answer. By the way, you can use
document.body
to get the body element. It's been available forever (since IE4/1997). -
ObiWanKenobi over 2 yearsUnfortunately, scoped does not apply to DOM elements outside the component, like body, html, etc