19

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);
    }
KingKongFrog
  • 13,946
  • 21
  • 75
  • 124
  • 1
    Possible duplicate of [How to detect DIV's dimension changed?](http://stackoverflow.com/questions/6492683/how-to-detect-divs-dimension-changed) – Roy J May 05 '17 at 21:33
  • 5
    @RoyJ read my post. This is asking how to do it using Vue.js – KingKongFrog May 05 '17 at 22:23
  • 1
    There's nothing special in Vue for it. You will have to work one of the solutions there into your Vue code. – Roy J May 05 '17 at 23:54
  • 1
    I'm surprised there is nothing in Vue for this. Managing resizing can be one of the hackiest parts of [`vanillajs`](http://vanilla-js.com/) – Jesse Reza Khorasanee Nov 28 '19 at 03:00

3 Answers3

22

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)
}
NiKO
  • 2,610
  • 1
  • 19
  • 19
  • 2
    This code doesn't work. It throws an error in beforeDestroy(). I don't know why the previous edit by Grillparzer (which fixed it) was rejected. We can NOT have this method chained, because the .observe() method doesn't return an "ResizeObserver" instance, so "this.ro.unobserve()" will NOT work. – Przemysław Niemiec May 21 '22 at 12:22
  • I can't see the need for the `beforeDestroy` `unobserve`. As far as I can tell the `ResizeObserver` will be garbage collected anyway – binaryfunt Mar 01 '23 at 18:13
5

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>
sam-m
  • 146
  • 1
  • 6
1

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)

Robin Macharg
  • 1,468
  • 14
  • 22