はじめに
offsetWidth
やoffsetHeight
などの幅や高さを取得する関数たちは、display: none
になっている場合、0を返します。
Vue.jsで要素の表示非表示にv-show
を用いている場合、v-show
はその要素にdisplay: none
を付与して制御を行うので、非表示中のoffsetWidth
は0として返ってくることになります。
<template> <div> <p v-show="false">ここのoffsetWidthは0pxです!</p> </div> </template>
ハマりやすいポイント
offsetWidth
などは、自分の要素だけでなく、親要素にdisplay: none
が付与されていても0を返します。
注意したいのは、親コンポーネントでv-show
を用いている(コンポーネントの表示制御を行っているなど)場合です。
Parent.vue
<template> <div> <div v-show="flag"> <Child /> </div> </div> </template> <script> import Child from "./Child"; export default { name: "Parent", components: { Child, }, data() { return { flag: false, }; }, mounted() { setTimeout(() => { this.flag = true; }, 1000); // 描画されて1秒後にtrueになる }, }; </script>
Child.vue
<template> <p ref="target">hello, world!</p> </template> <script> export default { name: "Child", mounted() { console.log(`pタグのoffsetWidthは ${this.$refs.target.offsetWidth}px です!`); // pタグのoffsetWidthは 0px です! }, }; </script>
この場合、v-show
では表示非表示に関わらず描画が行われ、その後display: none
で制御を行います。そのため子コンポーネントのmounted
のタイミングではv-show
がfalse
なので、結果は0pxになってしまいます。
2つの解決方法
offsetWidth
を取得できるようにする方法を2種類紹介します。
フラグを受け取りv-show
がtrue
になった直後に取得する
親で用いているフラグを受け取ってwatch
することで、true
になった瞬間にoffsetWidth
を取得します。
mounted
のタイミングでは取得できませんが、watch
と組み合わせれば任意のタイミングで利用できるようになります。
Parent.vue
<template> <div> <div v-show="flag"> <Child :flag="flag" /> <!-- flagを子コンポーネントに渡す --> </div> </div> </template> <script> // 省略 </script>
Child.vue
<template> <p ref="target">hello, world!</p> </template> <script> export default { name: "Child", props: { flag: Boolean, }, watch: { flag(newFlag) { if (newFlag) { console.log(`pタグのoffsetWidthは ${this.$refs.target.offsetWidth}px です!`); // 取得できた! } }, }, }; </script>
v-if
に変えて遅延描画にする
切り替えコストが上がってしまいますが、v-if
の遅延描画を活用することで、mounted
フックで取得できるようになります。
Parent.vue
<template> <div> <div v-if="flag"> <!-- v-ifに変更する --> <Child /> </div> </div> </template> <script> // 省略 </script>
Child.vue
<template> <p ref="target">hello, world!</p> </template> <script> export default { name: "Child", mounted() { console.log(`pタグのoffsetWidthは ${this.$refs.target.offsetWidth}px です!`); // 取得できた! }, }; </script>
まとめ
offsetWidth
が取得できない場合の解説と解決方法の紹介でした。親コンポーネントを探しに行かないといけない点は盲点だったので、同じ問題にハマらないように気をつけたいですね!