Nuxt.js: Proper strategy for binding a class to <html>

Created on 15 Sep 2017  路  7Comments  路  Source: nuxt/nuxt.js

I couldn't see a way to do this even through my layout file; I want to add a class to the <html> tag that does overflow: hidden !important when a modal is present so that I can prevent scrolling in the background.

I figure since I have modals in different components I would use vuex to globally communicate:

my store/index.js

import Vuex from 'vuex'

const store = () => new Vuex.Store({
  state: {
    modal: false,
  },
  mutations: {
    modalOn (state) {
      state.modal = true
      document.getElementsByTagName('html')[0].classList.add('is-clipped')
    },
    modalOff (state) {
      state.modal = false
      document.getElementsByTagName('html')[0].classList.remove('is-clipped')
    },
  },
})
export default store

Then in my component I add a watcher that hits these mutations when the modal is on/off:

  watch: {
    'modal' (value) {
      if (value) {
        this.$store.commit('modalOn')
      } else {
        this.$store.commit('modalOff')
      }
    },
  },

Am I doing it wrong? is there a better approach?

This question is available on Nuxt.js community (#c1487)
help-wanted

Most helpful comment

@acidjazz I needed exactly that too (to fix some wonky iOS scrolling) and this did the trick for me

export default {
  head() {
    return {
      bodyAttrs: {
          class: this.modalOpen ? 'm-open' : 'm-close'
      },
      htmlAttrs: {
          class: this.modalOpen ? 'm-open' : 'm-close'
      }
    }
  }
}

All 7 comments

I think you're over complicating it, I'm not sure you really need to use the store for this.

A better approach would probably be a child component that adds the class in its created lifecycle event and removes it on its destroyed lifecycle event. You could probably even house the modal's logic/template within the child component and bind it to a route - that way user could even refresh the page and get back the same modal state (open/closed).

@homerjam well the main point is i want to simplify and globalize this functionality so that all my modals can use this, and i don't really see any other way besides a store.

I don't see a reason to need it w/in routes (yet) but nice suggestion

To add a custom class to your HTML tag, you can use Document.

@alexchopin thanks but I not only need to add a custom class but I need to be able to add/remove this class based on wether or not the modal is on

@acidjazz I needed exactly that too (to fix some wonky iOS scrolling) and this did the trick for me

export default {
  head() {
    return {
      bodyAttrs: {
          class: this.modalOpen ? 'm-open' : 'm-close'
      },
      htmlAttrs: {
          class: this.modalOpen ? 'm-open' : 'm-close'
      }
    }
  }
}

On one hand, that's reactive.
On the other hand, it doesn't have all the affordances :class in template has, such as passing {class1: true} or ['class2'], or camelCase.
You can do this in a jiffy though:

export default {
  head() {
    return {
      htmlAttrs: {
        class: (Array.isArray(this.classes)
          ? this.classes
          : Object.entries(this.classes).reduce((acc, entry) => {
              if (entry[1]) acc.push(entry[0])
              return acc
            }, [])
        )
          .join(' ')
          .replace(/([a-z])([A-Z])/g, '$1-$2')
          .toLowerCase()
      }
    }
  }
}

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

bimohxh picture bimohxh  路  3Comments

danieloprado picture danieloprado  路  3Comments

mattdharmon picture mattdharmon  路  3Comments

lazycrazy picture lazycrazy  路  3Comments

surmon-china picture surmon-china  路  3Comments