1

I have built a VueJS component and am using it multiple times on the same page. The components work independently of each other perfectly, however there's a method in the component to get it's offsetWidth and apply this (with a prefix) to the component as a class (eg width-800).

When there is only one of the component on a page, it works fine.

When there is more than one, only the last occurrence applies the class. It does calculate the class correctly with the correct width, but just doesn't apply it to the others on the page.

<template>
    <div id="app-medialib" v-bind:class="[this.breakpoint]">
        <p>{{ this.breakpoint }}</p>

this.breakpoint is a data property which holds the class name.

mounted: function(){
  var self = this;

  this.calculateBreakpoint();

  window.onresize = function(event) {
      self.calculateBreakpoint();
  };
}

and the breakpoint method:

calculateBreakpoint: function(){
    var mediaLibraryWidth = null;

    var elems = document.getElementsByClassName('header medialib');
    for( var i=0; i<elems.length; i++ ){
        if( elems[i].offsetParent !== null ){
            console.log('elems[i]', elems[i]);
            mediaLibraryWidth = elems[i].offsetWidth;
            break;
        }
    }

    console.log('calculateBreakpoint', mediaLibraryWidth);

    if( mediaLibraryWidth > 956 ) {
        this.breakpoint = 'md';
    } else if( mediaLibraryWidth < 957 && mediaLibraryWidth > 700 ){
        this.breakpoint = 'sm';
    } else {
        this.breakpoint = 'xs';
    }

    console.log('calculateBreakpoint', this.breakpoint);
},

Any help is much appreciated on this. Been scratching my head for a while now.

Thanks.

2
  • we'll need the code that calls the component and how this.breakpoint is calculated in order to help :) Commented May 4, 2017 at 15:06
  • editing with more code example, forgive the messy calculation, this will be reworked once the class is being applied properly :) Commented May 4, 2017 at 15:13

2 Answers 2

3

When you do window.onresize and bind a function to it, the previous bindings get overridden, so when you have multiple components in your app, their mounted function is called which overrides the window.onresize. That is the reason why only the last components class is being changed.

Try the following in your console.

window.onresize = function (e) {
  console.log('a')
}

window.onresize = function (e) {
  console.log('b')
}

Only 'b' gets logged when you resize.

Checkout : What is the best practice to not to override other bound functions to window.onresize?

In your case using jquery to bind functions on window.onresize will be a better approach.

Try:

$(window).resize(function() {
 console.log('a')
})

$(window).resize(function() {
 console.log('b')
})

It will log both 'a' and 'b' when you resize.

So change your window.onresize to:

$(window).resize(function() {
  self.calculateBreakpoint();
})

Or as per Daniel Beck's suggestion, if you want to avoid using jquery, use:

 window.addEventListener('resize', self.calculateBreakpoint )
Sign up to request clarification or add additional context in comments.

3 Comments

Throwing the entire jQuery library in just to avoid overwriting an event binding is terrible overkill. window.addEventListener("resize", foo()) would do the same thing.
@DanielBeck yes I agree. Updating my answer so that it can also solve the issue without jquery. Thank you.
Cheers. I'm deleting my answer, as it's now effectively a duplicate of yours.
0

Apply the v-bind:class outside of the template like this

Vue.component("foo", {
  template: "<div>{{item}}</div>",
  props: ["item"]
});

var app = new Vue({
  el: "#app",
  data: {
    background: "blue",
    items: ["a", "b", "c", "d"]
  },
  mounted: function() {
    var self = this;
    this.calculateBreakpoint();
    window.onresize = function(event) {
      self.calculateBreakpoint();
    };
  },
  methods: {
    calculateBreakpoint: function() {
      var mediaLibraryWidth = null;
      var elems = document.getElementsByClassName("app");
      for (var i = 0; i < elems.length; i++) {
        if (elems[i].offsetParent !== null) {
          console.log("elems[i]", elems[i]);
          mediaLibraryWidth = elems[i].offsetWidth;
          break;
        }
      }

      console.log("calculateBreakpoint", mediaLibraryWidth);

      if (mediaLibraryWidth > 956) {
        this.background = "red";
      } else if (mediaLibraryWidth < 957 && mediaLibraryWidth > 700) {
        this.background = "blue";
      } else {
        this.background = "green";
      }

      console.log("calculateBreakpoint", this.breakpoint);
    }
  }
});
.red {
  background: red;

}

.blue {
  background: blue;

}

.green {
  background: green;

}

.widget {
  margin: 10px;
  padding: 20px;
  width: 200px;
  font-weight: bold;
  font-size: 16px;
  color: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.10/vue.min.js"></script>
<div id="app" class="app"> 
  <foo v-for="item in items" :class="[background]" class="widget" :item="item"> 
</div> 

https://codepen.io/anon/pen/RVLrmG

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.