How can I use window size in Vue? (How do I detect the soft keyboard?)
vue.jsvue.js Problem Overview
In my mobile web app with Vue, I want to hide my footer when the soft keyboard pops. So I have a little function to test the ratio of window height to window width...
showFooter(){
return h / w > 1.2 || h > 560;
}
...and I declare window.innerHeight/window.innerWidth in my data.
data: { h: window.innerHeight, w: window.innerWidth }
Trouble is, when window.innerHeight changes, my h property doesn't get the new value. How can I watch window.innerHeight ?
vue.js Solutions
Solution 1 - vue.js
I am sure there are better ways to do this, but this one will work for you until I come up with something:
Basically you will need just one data prop and one watcher to do this.
new Vue({
el: '#app',
data() {
return {
windowHeight: window.innerHeight,
txt: ''
}
},
watch: {
windowHeight(newHeight, oldHeight) {
this.txt = `it changed to ${newHeight} from ${oldHeight}`;
}
},
mounted() {
this.$nextTick(() => {
window.addEventListener('resize', this.onResize);
})
},
beforeDestroy() {
window.removeEventListener('resize', this.onResize);
},
methods: {
onResize() {
this.windowHeight = window.innerHeight
}
}
});
And this would output the changes
<div id="app">
<br> Window height: {{ windowHeight }} <br/>
{{ txt }}
</div>
Solution 2 - vue.js
VUE 3
In Vue 3, you can create a function that can return a reactive width and breakpoint name values. You can easily reuse the function across multiple components.
import { computed, onMounted, onUnmounted, ref } from "vue"
export default function () {
let windowWidth = ref(window.innerWidth)
const onWidthChange = () => windowWidth.value = window.innerWidth
onMounted(() => window.addEventListener('resize', onWidthChange))
onUnmounted(() => window.removeEventListener('resize', onWidthChange))
const type = computed(() => {
if (windowWidth.value < 550) return 'xs'
if (windowWidth.value >= 550 && windowWidth.value < 1200) return 'md'
if (windowWidth.value >= 1200) return 'lg'
return null; // This is an unreachable line, simply to keep eslint happy.
})
const width = computed(() => windowWidth.value)
return { width, type }
}
You can use this in the setup method of your vue 3 component as follows.
const { width, type } = useBreakpoints()
Quick tip: Even though document event listeners are the most optimized things in the planet, this is best used only once in an app for performance reasons. Make a tiny plugin and add the value to the Vue instance, just like Vuetify does. Or to be simpler, commit them to vuex and read from there.
Solution 3 - vue.js
The above answer didn't work for me. Instead, I used:
mounted() {
window.addEventListener('resize', () => {
this.windowHeight = window.innerHeight
})
}
Solution 4 - vue.js
For those already using Vuetify, you can just watch this.$vuetify.breakpoint.width
or this.$vuetify.breakpoint.height
for changes in the viewport's dimensions.
Solution 5 - vue.js
This is probably really too late but if you want a much more simpler approach you can do npm installation https://www.npmjs.com/package/vue-window-size
and import windowWidth from 'vue-window-size';
Or this with composition api
setup() {
const windowSize = ref(window.innerWidth)
onMounted(() => {
window.addEventListener('resize', () => {windowSize.value = window.innerWidth} )
})
onUnmounted(() => {
window.removeEventListener('resize', () => {windowSize.value = window.innerWidth})
})
return {
windowSize
}
}
Solution 6 - vue.js
My answer might be late but none of the above worked for me so here is what I found on this topic ! :)
https://codepen.io/sethdavis512/pen/EvNKWw
HTML :
<div id="app">
<section class="section has-text-centered">
<h1 class="title is-1">
Your Window
</h1>
<h3 class="title is-3">
Width: {{ window.width }} px<br/>
Height: {{ window.height }} px
</h3>
<p class="has-text-white">
↑<br/>
← resize window →<br/>
↓
</p>
</section>
</div>
CSS:
$top-color: yellow;
$bottom-color: tomato;
html, body, #app, section.section {
height: 100%;
}
body {
background: -webkit-linear-gradient($top-color, $bottom-color);
background: -o-linear-gradient($top-color, $bottom-color);
background: -moz-linear-gradient($top-color, $bottom-color);
background: linear-gradient($top-color, $bottom-color);
}
section.section {
display: flex;
flex-flow: column;
justify-content: center;
align-items: center;
}
.title {
color: white;
}
JS:
new Vue({
el: '#app',
data: {
window: {
width: 0,
height: 0
}
},
created() {
window.addEventListener('resize', this.handleResize);
this.handleResize();
},
destroyed() {
window.removeEventListener('resize', this.handleResize);
},
methods: {
handleResize() {
this.window.width = window.innerWidth;
this.window.height = window.innerHeight;
}
}
});