DEV Community

Cover image for Listen for and debounce Window Resize Event in Vue.JS
SandraRodgers
SandraRodgers

Posted on

Listen for and debounce Window Resize Event in Vue.JS

Learn how to listen for a resize event in Vue and how to debounce the handler function to improve performance.

At some point you will probably need to get information about the changing size of the browser window. You might want it for calculating the position of other elements on the page, such as keeping an element a certain distance from the bottom of the page no matter how tall the window is.

One way to do this in Vue.js is to add an event listener to track a 'resize' of the window. I'll walk you through how you can do this in Vue. Let's get started!

Set up initial height/width and listen for change to that height/width:

Since we want to track the change in height and width of the window, we will create a data property for height and width. This now gives us a place to hold on to that information about height and width.

data() {
    return {
      height: 0,
      width: 0
    };
  },
Enter fullscreen mode Exit fullscreen mode

We need to do three things to track the change in height/width. We need to:

  • Add an event listener during the created lifecycle hook to listen for 'resize'.
  • Remove the event listener during the destroyed lifecycle hook to stop listening for 'resize'.
  • Set the initial height/width when the component mounts. We will set it during the mounted lifecycle hook using the window.innerHeight and window.innerWidth properities.

Here is how we add the event listener and remove the event listener:

created()  {
    window.addEventListener("resize", this.resizeHandler);
},

destroyed()  {
    window.removeEventListener("resize", this.resizeHandler);
},
Enter fullscreen mode Exit fullscreen mode

Notice the method resizeHandler. We will have to add logic for that method in the component's methods object, but we'll get to that in a bit.

Here is how we set the initial height and width of the window when the component mounts. This ensures that when the component mounts, we have set the height and width properties to the actual height and width of the browser initially. After that, the event listeners will track the change from that starting height and width.

mounted()  {
    this.height =  window.innerHeight;
    this.width =  window.innerWidth;
},
Enter fullscreen mode Exit fullscreen mode

Resize handler method

In the mounted hook, we set the initial height and width. We also want to do that constantly as the window is resized, so we will create a method with that same logic. And the method will run as the broswer listens for that event.

Here is the method:

methods:  {
    resizeHandler(e)  {
        this.height =  window.innerHeight;
        this.width =  window.innerWidth;
    },
},
Enter fullscreen mode Exit fullscreen mode

Remember, this is the method that is called as the event listener fires:

window.addEventListener("resize",  this.resizeHandler);
Enter fullscreen mode Exit fullscreen mode

See it working

We can watch the data for height and width change as the window is resized. Put this in your Vue template:

<template>
    <div  id="app">
        <div>Height: {{ height }}</div>
        <div>Width: {{ width }}</div>
    </div>
</template>
Enter fullscreen mode Exit fullscreen mode

Then change the window size and watch the numbers react to that change. You can see a demo Codepen here.

BONUS: debounce to improve performance

An event listener that fires constantly like this one can take a toll on performance. It's really smart to add a function that will debounce the event listener. Debounce means that instead of the handleResize function running each time the event fires, we add a timeout to cause the function to fire only after a certain time. We limit the amount of time the handler function will fire, so the browser doesn't get bogged down trying to keep up with the constant firing of the resize event. Since a resize event happens so quickly, and the event would fire so often, we can limit it and it won't be noticeable to the user, as long as the timeout we put on it isn't for too long a duration.

We can add debouncing by using a kind of middleman on the height/width property. We will compute the height and width using a getter and a setter. So we will need to change our data properties to reflect that they are holding the debounced height and width, and then we will use the computed property to put a timeout on the height/width that is returned.

data()  {
    return  {
        debouncedHeight:  0,
        debouncedWidth:  0,
        heightTimeout:  null,
        widthTimeout:  null,
    };
},

computed:  {
    height:  {
        get()  
            return  this.debouncedHeight;
        },
        set(val)  {
            if  (this.timeout)  clearTimeout(this.timeout);
                this.heightTimeout =  setTimeout(()  =>  {
                this.debouncedHeight = val;
            },  500);
        },
    },

width:  {
    get()  {
        return  this.debouncedWidth;
    },
        set(val)  {
            if  (this.timeout)  clearTimeout(this.timeout);
                this.widthTimeout =  setTimeout(()  =>  {
                this.debouncedWidth = val;
            },  500);
        },
    },
},
Enter fullscreen mode Exit fullscreen mode

Everything else remains the same, but now our height and width use the set method to slow down how rapidly they can actually be set, instead of setting them every time that resize event fires. You can see a working Codepen here. I recommend slowing the timeout to really see how it works.

I hope this was helpful to you!

Discussion (0)