Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to trigger an event when element is resized in Vue.js?

Currently I trigger an event to take the same height of the current element when the component is mounted, however this doesn't always work since it is sent once and if the element is resized it won't get sent again. Also I need to put a timeout since sometimes the chart in the componenet change sthe height after the ajax call.

How do I send this event ANYTIME the height of the current element is changed?

This is what I am currently doing:

    mounted: function() {
        setTimeout(function() {
            this.$emit('resize', this.$el.offsetHeight);
        },1000);
    }
like image 404
KingKongFrog Avatar asked May 05 '17 21:05

KingKongFrog


3 Answers

There is more Vue style solution based on ResizeObserver, but not supported in all browsers - yet

DOC: https://caniuse.com/#feat=resizeobserver

Examplum

data () {
  return {
    ro: null,
  }
},

methods: {
  onResize () {
    this.$emit('resize', this.$refs.myElement.offsetHeight)
  },
},

mounted () {
  this.ro = new ResizeObserver(this.onResize)
  this.ro.observe(this.$refs.myElement)
},

beforeDestroy () {
  this.ro.unobserve(this.$refs.myElement)
}
like image 152
NiKO Avatar answered Nov 09 '22 17:11

NiKO


Until ResizeObserver becomes standard, you can use MutationObserver to keep track of the element's size. Codepen link

new Vue({
  el: "#app",
  data() {
    return {
      width: null,
      height: null,
      observer: null
    };
  },

  mounted() {
    //get initial dimensions. Mutation observer will observe mutations only
    const box = this.$refs.box,
      boxSize = box.getBoundingClientRect();

    this.width = Math.trunc(boxSize.width) + "px";
    this.height = Math.trunc(boxSize.height) + "px";
    // initialize the observer on mount
    this.initObserver();
  },

  //disconnect the observer before destroy
  beforeDestroy() {
    if (this.observer) this.observer.disconnect();
  },

  methods: {
    initObserver() {
      const box = this.$refs.box,
        vm = this,
        config = {
          attributes: true
        };
      // create the observer
      const observer = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
          // check if the mutation is attributes and update the width and height data if it is.
          if (mutation.type === "attributes") {
            let {
              width,
              height
            } = box.style;

            vm.width = width;
            vm.height = height;
          }
        });
      });

      // observe element's specified mutations
      observer.observe(box, config);
      // add the observer to data so we can disconnect it later
      this.observer = observer;
    }
  }
});
.box {
  text-align: center;
  font-family: Arial;
  box-sizing: border-box;
  width: 150px;
  height: 150px;
  border: 2px solid red;
  padding: 10px;
  margin: 0 auto;
  resize: both;
  overflow: auto;
}

.size {
  color: #2a9966;
  font-weight: 600;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <!-- more info on refs: https://vuejs.org/v2/api/#ref -->
  <div class=" box" ref="box">
    <h4>Resize Me</h4>
    <p>
      <span>width: </span><span class="size">{{ width }}</span>
    </p>
    <p>
      <span>height: </span><span class="size">{{ height }}</span>
    </p>
  </div>
</div>
like image 5
sam-m Avatar answered Nov 09 '22 16:11

sam-m


I've had some issues getting ResizeObserver to work in a Vue2 TypeScript project. Two approaches worked with (AFAICT) identical results:

  • Using the resize-observer-polyfill (which essentially wraps MutationObserver). This required an import ResizeObserver from 'resize-observer-polyfill' in my component.

  • Installing the @types/resize-observer-browser npm package, based on this answer, and adding an entry to my tsconfig.json:

    {
      "compilerOptions": {
        ...
        "types": [
          ...
          "resize-observer-browser"
        ]
      }
    }
    

    This second approach didn't require any additional import and is the solution I'm favouring.

Both approaches offer the same ResizeObserver API, e.g.

this.resizeObserver = new ResizeObserver((entries, observer) => {
    log("Resize called", entries, observer)
})
this.resizeObserver.observe(this.$refs.canvas as Element)

(note: only tested in Chrome 88, Feb 2021)

like image 1
Robin Macharg Avatar answered Nov 09 '22 16:11

Robin Macharg