Nuxt/Vue reactive body attributes
Asked Answered
D

2

6

I want to add the class menu-opened on the body tag when I click on the menu burger div.

My div

<div v-on:click="openMenu"></div>

The openMenu method

methods: {
     openMenu() {
       console.log('open menu launch')
       this.$store.dispatch('menu/setMenu', true)
     }
    }

My store

state = {
    isMenuOpen: false
}

actions = {
    setMenu({ commit }, value) {
      commit('SET_MENU_OPEN_STATUS', value)
    }
}

mutations= {
    SET_MENU_OPEN_STATUS(state, newState){
        state.isMenuOpen = newState
    }
}

On my template, i got this code to add the class on the body based on the state of the isMenuOpen value :

export default {
    data() {
        return {
           menuState: this.$store.state.isMenuOpen
        }
    },
    head () {
      return {
        bodyAttrs: {
          class: this.menuState ? 'menu-opened' : ''
        }
      }
    }
}

My store is working well, the value change when I click on my div, but it's not adding the class, like if the head function is not reactive...

Thanks for your help

Duodecimal answered 30/8, 2018 at 14:34 Comment(2)
Can you show the code / template that includes <body>?Jase
This is the nuxt HTML model here : fr.nuxtjs.org/guide/views/#documentStramonium
A
6

In Nuxt

This is because the head method is only called once on initial page load. If you would like to update the values you can, but you will need to call the head() function again.

You could do this with a watcher or you could call it from your store.

A quick and kind of dirty way would be with a combination of computed properties and a watcher..

export default {
    data() {
        return {
           menuState: this.$store.state.isMenuOpen
        }
    },
    head () {
      return {
        bodyAttrs: {
          class: this.isMenuOpen ? 'menu-opened' : ''
        }
      }
    },
   computed: {
     isMenuOpen () {
       return this.$store.state.isMenuOpen
     }
   },
   watch: {
     isMenuOpen () {
       this.head()
     }
   }
}

In Vue

You need to watch on events like mounted or beforeDestroy described there

https://v2.vuejs.org/v2/guide/instance.html

and use pure js to modify dom like

const bodyElement = document.querySelector('body')
bodyElement.classList.add('a');
bodyElement.classList.remove('b');
bodyElement.classList.toggle('c');

https://developer.mozilla.org/pl/docs/Web/API/Element/classList

Adactylous answered 30/8, 2018 at 16:9 Comment(4)
If i place this on a layout, i get this.head is not a function as an error and rendering failsShake
Calling this.head() has no effect.Daredeviltry
This is great answer, but works only in nuxt. In question there is nuxt/vue, so if it is disliked then only because of users of pure vue.Cauterize
It's an old question , but i managed to do this in Nuxt like your answer, i'm updating the values in the store and use the store values in default.vue to update the bodyAttrs... i just have one issue , there is a latency when updating the classes , i can see that the values are updating in DevTools but the classes are not updating at the same time ... i use the same values from the store on three places to update the classes ... i created a gif for better understanding... anyone has a clue what am i doing wrong ... gifyu.com/image/FjPDPubis
H
0

You could do something like this:

My div

<div v-on:click="openMenu" ref="myDiv"></div>

The openMenu method

methods: {
     openMenu() {
       console.log('open menu launch')
       this.$refs.myDiv.closest('body').classList.add("menu-opened")
     }
    }

And then you need to implement similar functionality for the close-menu button, only using:

this.$refs.myDiv.closest('body').classList.remove("menu-opened")
Hahn answered 15/10, 2021 at 8:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.