How do I add/remove classes to <body>

Since Vue recommends against using body-tag for an Vue instance, how would I add classes to body element?

1 Like

Use HTML5 classList API https://developer.mozilla.org/pl/docs/Web/API/Element/classList and use created/beforeDestroy hooks to add/remove your body class.

1 Like

Isn’t it considered bad practise to manipulate the DOM?

Generall yes, but you can’t mount Vue instance to the body tag anyway, so it’s completely safe to manipulate class this way.

Just curious, what can you do with body that you can’t with the immediate div under it?

Nothing I suppose :slight_smile: I just moved the class to the div just below which also Vue mounts on. Works as expected.

1 scenario would be to add overflow: hidden to the body to prevent scrolling. This is useful when having modals.
Anyone know of a way to do this with by not messing with the DOM, or is that the way to go?

4 Likes

It may be useful for creating css scopes per page. You could even attach it to html element, like Modernizr does, but there’s nothing special to style in <head> section though.

It certainly is fine to add classes to the body through this way. Manipulating the DOM isn’t always bad as long as you understand exactly what you’re doing in the context (Vue) you’re working in.

A lot of people do things this way. Actually, a lot of advanced components will mount, remove itself from its current location and reattach directly as a child of body to do advanced positioning of say drop down menus.

1 Like

Here’s the solution btw:

  1. stick to components lifecycle events:
    https://vuejs.org/v2/guide/instance.html

  2. before component is created addClass to body.

  3. after component is destroyed removeClass from body.

You can easily switch classes with pure JS http://youmightnotneedjquery.com

In my case, I am using a bootstrap admin theme that uses a body class to target styles. So I need a couple different body classes for the login page, and a few other types of pages.

I hope you will find this helpful:
https://www.npmjs.com/package/vue-body-class

Hope this helps:

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');
}
9 Likes

Thanks , It’s work!

I have a similar issue, well not an issue as i can get it to work but not in the correct way.

I have a burger menu inside a nav component, when I click it I want to toggle the body class or even a wrapper class this activates the menu which onClick remove the class, but can’t work it out, i can toggle the burger class but not the body or wrapper.

What I want to achieve

<button class="hamburger hamburger--collapse" type="button" aria-label="Menu" @click="addBodyClass" v-bind:class="{ active: isActive }">
    <span class="hamburger-box">
        <span class="hamburger-inner"></span>
    </span>
</button>

<nav class="nav">
    <div class="nav_inner">
        <ul>
            <li>
                <nuxt-link to="/" @click="removeBodyClass" v-bind:class="{ active: notActive }">Home</nuxt-link>
            </li>
        </ul>
    </div>
</nav>

<script>
export default {
    methods: {
        addBodyClass: function(){
            var myBody = document.getElementsByTagName('body')[0];
            myBody.isActive = !this.isActive;
        }
       removeBodyClass: function(){
            var myBody = document.getElementsByTagName('body')[0];
            myBody.notActive = !this.notActive;
        }
    }

}
</script>

But this doesn’t’ work

So what I’m currently doing is

<button class="hamburger hamburger--collapse" type="button" aria-label="Menu">
    <span class="hamburger-box">
        <span class="hamburger-inner"></span>
    </span>
</button>

Then in a globally accessed js file

import Vue from 'vue'

var myBurger = document.querySelector('.hamburger');
var myBody = document.getElementsByTagName('body')[0];
var myNav = document.querySelector('.nav');

myBurger.onclick = function() {
myBody.classList.toggle('is-active');
}

myNav.onclick = function() {
myBody.classList.remove('is-active');
}

This looks like what I need just not sure how to use it

There is no isActive property on the DOM element. You can achieve what you intend by using the data option in your component to toggle the class on the hamburger menu, but you must add/remove the class on the body element manually.

<button class="hamburger hamburger--collapse" type="button" aria-label="Menu" @click="addBodyClass" v-bind:class="{ active: isActive }">
    <span class="hamburger-box">
        <span class="hamburger-inner"></span>
    </span>
</button>

<nav class="nav">
    <div class="nav_inner">
        <ul>
            <li>
                <nuxt-link to="/" @click="removeBodyClass">Home</nuxt-link>
            </li>
        </ul>
    </div>
</nav>

<script>
let myBody = document.getElementsByTagName('body')[0];

export default {
    data () {
        return {
            isActive: false // This is used to toggle the class on your hamburger button
        }
    },

    methods: {
        addBodyClass: function(){
            this.isActive = true;

            // You must manually add & remove classes to the body element
            myBody.classList.add('is-active');
        },
       removeBodyClass: function(){
            this.isActive = false;

            myBody.classList.remove('is-active');
        }
    }

}
</script>
1 Like

I get error document undefined

let myBody = document.getElementsByTagName('body')[0];

here is what ive got so far

<nav class="nav">
                <div class="nav_inner">
                    <ul>
                        <li>
                            <nuxt-link to="/" @click="removeBodyClass">Home</nuxt-link>
                        </li>
                       </ul>
                </div>
            </nav>
<button class="hamburger hamburger--collapse" type="button" aria-label="Menu" @click="addBodyClass" v-bind:class="{ active: isActive }">
                <span class="hamburger-box">
                    <span class="hamburger-inner"></span>
                </span>
            </button>
<script>
let myBody = document.getElementsByTagName('body')[0];

export default {
    data () {
        return {
            isActive: false // This is used to toggle the class on your hamburger button
        }
    },

    methods: {
        addBodyClass: function(){
            this.isActive = true;

            // You must manually add & remove classes to the body element
            myBody.classList.add('is-active');
        },
       removeBodyClass: function(){
            this.isActive = false;
            myBody.classList.remove('is-active');
        }
    }

}
</script>

Ah, because you’re using nuxt. You can just move the document.getElementsByTagName('body')[0]; to your methods or set a reference once the component has mounted.

<script>
let myBody = null;

export default {
    data () {
        return {
            isActive: false // This is used to toggle the class on your hamburger button
        }
    },

    methods: {
        addBodyClass: function(){
            this.isActive = true;

            // You must manually add & remove classes to the body element
            myBody.classList.add('is-active');
        },
       removeBodyClass: function(){
            this.isActive = false;
            myBody.classList.remove('is-active');
        }
    },

    mounted () {
      myBody = document.getElementsByTagName('body')[0];
    }
}
</script>

Looks like it should work and i get no errors, have made a slight change in that I have changed the burger to toggle but when I click the menu items to remove the class the class is not removed.

<template>
    <header class="header row">
        <div class="ctr">
            <nav class="nav">
                <div class="nav_inner">
                    <ul>
                        <li>
                            <nuxt-link to="/" @click="removeBodyClass">Home</nuxt-link>
                        </li>
                    </ul>
                </div>
            </nav>
            <button class="hamburger hamburger--collapse" type="button" aria-label="Menu" @click="addBodyClass">
                <span class="hamburger-box">
                    <span class="hamburger-inner"></span>
                </span>
            </button>
        </div>
    </header>
</template>

<script>
let myBody = null;

export default {
    data () {
        return {
            isActive: false // This is used to toggle the class on your hamburger button
        }
    },
    methods: {
        addBodyClass: function(){
            this.isActive = true;

            // You must manually add & remove classes to the body element
            myBody.classList.toggle('is-active');
        },
       removeBodyClass: function(){
            this.isActive = false;
            myBody.classList.remove('is-active');
        }
    },
    mounted () {
      myBody = document.getElementsByTagName('body')[0];
    }
}
</script>