sara
sara

Reputation: 885

Vue JS - How to get window size whenever it changes

I'm new to vuejs but I was trying to get the window size whenever I resize it so that i can compare it to some value for a function that I need to apply depending on the screen size. I also tried using the watch property but not sure how to handle it so that's probably why it didn't work

  methods: {

    elem() {
     this.size = window.innerWidth;
     return this.size;
   },
  mounted() {
    if (this.elem < 767){ //some code }
   }

Upvotes: 85

Views: 149685

Answers (7)

cartbeforehorse
cartbeforehorse

Reputation: 3487

The only solution that worked from me was Vuetify (and yes, I've tried most solutions that were suggested on this page). For some reason, adding an event listener (which seems to have the most up-votes on this thread) didn't work for my project. I'm not 100% sure why, but some comments seem to suggest that it's a problem with non-singleton component apps (whatever that means)?

I also found a few extensions that are out there in the ecosystem (including something called vue-resize-directive >>, but they only seem compatible with Vue 2!! :-(

Anyway, the solution that did work was this:

https://vuetifyjs.com/en/api/v-resize-directive/

It's a bit of a faff to install Vuetify in an existing project >>, but once installed its use as a directive means that it adds minimal code in your component.

    <div v-resize="myResizeFunction">
        <!-- blah -->
    </div>

Where myResizeFunction() is the event-handler callback function in your JavaScript.

Unfortunately, unlike the afore-mentioned vue-resize-directive, the Vuetify version of the directive doesn't seem to have any modifiers (such as the ability to define a debounce delay, for example). But it works well enough for my needs.

Hope that helps someone.

Upvotes: 0

If you are using composition api in vue 3, you may use following code

onMounted(() => {
  window.addEventListener("resize", handleWindowSizeChange);
  handleWindowSizeChange();
});
onUnmounted(() => {
  window.removeEventListener("resize", handleWindowSizeChange);
});
const handleWindowSizeChange = () => {
// your code
};

Upvotes: 2

Rajaruban Rajindram
Rajaruban Rajindram

Reputation: 996

For Vue3, you may use the code below:

mounted() {
  window.addEventListener("resize", this.myEventHandler);
},
unmounted() {
  window.removeEventListener("resize", this.myEventHandler);
},
methods: {
  myEventHandler(e) {
    // your code for handling resize...
  }
}

destroyed and beforeDestroyed is deprecated in Vue3, hence you might want to use the beforeUnmount and unmounted

Upvotes: 20

agm1984
agm1984

Reputation: 17132

I looked at the code of that library vue-window-size, and besides the additional logic, it's just adding an event listener on window resize, and it looks like it can be instructed to debounce. Source

The critical problem for me is that my Vue SPA app does not emit a window resize event when a vue-router route changes that makes the <html> element go from 1000px to 4000px, so it's causing me all kinds of problems watching a canvas element controlled by p5.js to redraw a wallpaper using p5.resizeCanvas().

I have a different solution now that involves actively polling the page's offset height.

The first thing to be aware of is JavaScript memory management, so to avoid memory leaks, I put setInterval in the created lifecycle method and clearInterval in the beforeDestroy lifecycle method:

created() {
    this.refreshScrollableArea = setInterval(() => {
        const { offsetWidth, offsetHeight } = document.getElementById('app');
        this.offsetWidth = offsetWidth;
        this.offsetHeight = offsetHeight;
    }, 100);
},

beforeDestroy() {
    return clearInterval(this.refreshScrollableArea);
},

As hinted in the above code, I also placed some initial state:

data() {
    const { offsetWidth, offsetHeight } = document.querySelector('#app');

    return {
        offsetWidth,
        offsetHeight,
        refreshScrollableArea: undefined,
    };
},

Note: if you are using getElementById with something like this.id (ie: an element that is a child in this component), document.getElementById(this.id) will be undefined because DOM elements load outer-to-inner, so if you see an error stemming from the data instantiation, set the width/height to 0 initially.

Then, I put a watcher on offsetHeight to listen for height changes and perform business logic:

    watch: {
        offsetHeight() {
            console.log('offsetHeight changed', this.offsetHeight);

            this.state = IS_RESET;
            this.setState(this.sketch);
            return this.draw(this.sketch);
        },
    },

Conclusion: I tested with performance.now() and:

document.querySelector('#app').offsetHeight 
document.getElementById('app').offsetHeight
document.querySelector('#app').getClientBoundingRect().height

all execute in about the exact same amount of time: 0.2ms, so the above code is costing about 0.2ms every 100ms. I currently find that reasonable in my app including after I adjust for slow clients that operate an order of magnitude slower than my localmachine.

Here is the test logic for your own R&D:

const t0 = performance.now();
const { offsetWidth, offsetHeight } = document.getElementById('app');
const t1 = performance.now();

console.log('execution time:', (t1 - t0), 'ms');

Bonus: if you get any performance issue due to long-running execution time on your setInterval function, try wrapping it in a double-requestAnimationFrame:

created() {
    this.refreshScrollableArea = setInterval(() => {
        return requestAnimationFrame(() => requestAnimationFrame(() => {
            const { offsetWidth, offsetHeight } = document.getElementById(this.id);
            this.offsetWidth = offsetWidth;
            this.offsetHeight = offsetHeight;
        }));
    }, 100);
},

requestAnimationFrame itself a person should research. I will leave it out of the scope of this answer.

In closing, another idea I researched later, but am not using is to use a recursive setTimeout function with a dynamic timeout on it (ie: a timeout that decays after the page loads); however, if you consider the recursive setTimeout technique, be conscious of callstack/function-queue length and tail call optimization. Stack size could run away on you.

Upvotes: 7

Calvintwr
Calvintwr

Reputation: 8798

Simplest approach

https://www.npmjs.com/package/vue-window-size

Preview

import Vue from 'vue';
import VueWindowSize from 'vue-window-size';

Vue.use(VueWindowSize);

You would then access it normally from your components like this:

<template>
  <div>
    <p>window width: {{ windowWidth }}</p>
    <p>window height: {{ windowHeight }}</p>
  </div>
</template>

Upvotes: 10

Goran
Goran

Reputation: 3410

Put this code inside your Vue component:

created() {
  window.addEventListener("resize", this.myEventHandler);
},
destroyed() {
  window.removeEventListener("resize", this.myEventHandler);
},
methods: {
  myEventHandler(e) {
    // your code for handling resize...
  }
}

This will register your Vue method on component creation, trigger myEventHandler when the browser window is resized, and free up memory once your component is destroyed.

Upvotes: 180

Gayan Sandamal
Gayan Sandamal

Reputation: 337

You can use this anywhere anytime

methods: {
    //define below method first.
    winWidth: function () {
        setInterval(() => {
            var w = window.innerWidth;
            if (w < 768) {
                this.clientsTestimonialsPages = 1
            } else if (w < 960) {
                this.clientsTestimonialsPages = 2
            } else if (w < 1200) {
                this.clientsTestimonialsPages = 3
            } else {
                this.clientsTestimonialsPages = 4
            }
        }, 100);
    }
},
mounted() {
    //callback once mounted
    this.winWidth()
}

Upvotes: -1

Related Questions