Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to compute styles on <body> or <html> using vue.js?

Tags:

vue.js

vuejs2

I am using vuejs style bindings to render changes dynamically as the styles are computed.

This works great for everything within the scope of my Vue instance but how can I compute styles for body or html tags? This used to be possible when you could bind the vue instance to but vue no longer lets you do it.

I want to dynamically update the background color of using my computed variables in vue.

edit: added code snippet to demonstrate

var app = new Vue({
  el: '#app',
  data: {
    color: '#666666'
  },
  computed: {
    backgroundColor: function() {
      return {
        'background-color': this.color
      }
    }
  },
  methods: {
    toggleBackground: function() {
      if(this.color=='#666666'){
        this.color = '#BDBDBD'
      } else {
        this.color = '#666666'
      }
    }
  }
})
<script src="https://vuejs.org/js/vue.min.js"></script>
<html>
  <body>
    <div id="app" :style="backgroundColor">
      <div>
        lots of content...
      </div>
      <button @click="toggleBackground"> Click to toggle </button>
    </div>
  </body>
</html>
like image 685
Ed Shee Avatar asked Dec 03 '22 20:12

Ed Shee


2 Answers

If you really need to style body itself, you'll need to do it with plain JavaScript in a watcher. A simple example is below.

You should (not something I've tried, but I'm hypothesizing) be able to defeat overscrolling effects by making body and your outer container non-scrolling. Put a scrollable container inside that. When it overscrolls, it will show your outer container, right?

The reasons for not binding to body are here (for React, but applies to Vue).

What’s the problem with ? Everybody updates it! Some people have non-[Vue] code that attaches modals to it. Google Font Loader will happily put elements into body for a fraction of second, and your app will break horribly and inexplicably if it tries to update something on the top level during that time. Do you really know what all your third party scripts are doing? What about ads or that social network SDK?

new Vue({
  el: '#app',
  data: {
    isRed: false
  },
  watch: {
    isRed() {
      document.querySelector('body').style.backgroundColor = this.isRed ? 'red' : null;
    }
  }
});
#app {
  background-color: white;
  margin: 3rem;
}
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="app">
  <input type="checkbox" v-model="isRed">
</div>
like image 160
Roy J Avatar answered Mar 16 '23 21:03

Roy J


I think I found better solution than using jQuery/querySelector

You can add tag style right in your Vue template. And add v-if on this, smth like that:

<style v-if="true">
  body {
    background: green;
  }
</style>

Thus you can use computed/methods in this v-if and DOM always will update when you need.

Hope this will help someone ;)

UPD: Using tag "style" in templates is not best idea, but you can create v-style component, then everything will be fine: Use style tags inside vuejs template and update from data model

My snippet:

Vue.component('v-style', {
  render: function (createElement) {
    return createElement('style', this.$slots.default)
  }
});

new Vue({
  el: '#app',
  data: {
    isRed: false,
    color: 'yellow',
  },
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <input type="checkbox" v-model="isRed">
  <v-style v-if="isRed">
    body {
      background: red; /*one more benefit - you can write "background: {{color}};" (computed)*/
    }
  </v-style>
</div>
like image 41
crutch12 Avatar answered Mar 16 '23 22:03

crutch12