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
};
},
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
andwindow.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);
},
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;
},
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;
},
},
Remember, this is the method that is called as the event listener fires:
window.addEventListener("resize", this.resizeHandler);
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>
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.heightTimeout) clearTimeout(this.heightTimeout);
this.heightTimeout = setTimeout(() => {
this.debouncedHeight = val;
}, 500);
},
},
width: {
get() {
return this.debouncedWidth;
},
set(val) {
if (this.widthTimeout) clearTimeout(this.widthTimeout);
this.widthTimeout = setTimeout(() => {
this.debouncedWidth = val;
}, 500);
},
},
},
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!
Top comments (4)
Thanks for this! It was super helpful. Btw, I think the debounce code has a bug as it references and clears "this.timeout" instead of the related "this.heightTimeout" and "this.widthTimeout" accordingly, but the example was enough to get me going.
Thanks for letting me know! I never would have noticed. Code is fixed now!
I'm a bit confused how this works with the debounce code, are you able to share a snippet of the whole solution with debounce? Thanks!
There's a codepen here that I made for this article: codepen.io/sandrarodgers/pen/RwZBGXZ