対象
- nxutを使ってLINEやmessengerのようなチャットを作っている人
- (非同期で取得したデータを使ってDOMが更新された後のイベントをフックしたい人)
やりたいこと
- チャット画面を開いた時点で、メッセージの一番下までスクロールしたい(以下この画面のことをチャットコンポーネントと呼ぶ)
- メッセージはチャットコンポーネントを表示するタイミングで非同期でサーバから取得する(先に読み込んでおくとかはなし)
とりあえずチャットの一番下までスクロールする方法
このやり方がベストかどうかはわからないが、とりあえず以下のコードでできる(templata部分は省略。とりあえずoverflow: scrollになっているなにかしら)
<script>
method: {
scrollToEnd() {
const chatLog = this.$refs.'スクロールしたいref属性'
if (!chatLog) return
chatLog.scrollTop = chatLog.scrollHeight
}
}
}
</script>
解説
-
this.$refs.'スクロールしたいref属性'
でスクロールさせたいHTML要素を取得して、それのTOPをそれの高さにしてあげることで、一番したまでスクロールした状態にしているだけ
https://jp.vuejs.org/v2/api/#ref
問題点
チャットコンポーネントが表示されたタイミングで一番したまでスクロールして欲しい。
しかしチャットコンポーネントに表示するメッセージはコンポーネントを表示するタイミングで非同期で取得するため、Vueのライフサイクルのmounted
やcreated
ではまだ取得できていない。
(ライフサイクルに関してはここを→Vue インスタンス)
試したこと
updatedを使う
非同期通信が終わったタイミングではbeforeUpadate
やupdated
が呼ばれる
DOMが更新され終わったタイミングでないとchatLog.scrollTop
が取得できないため、使うとしたらupdated
つまりこういうこと↓
<script>
updated() {
this.scrollToEnd()
},
methods: {
scrollToEnd() {
const chatLog = this.$refs.'スクロールしたいref属性'
if (!chatLog) return
chatLog.scrollTop = chatLog.scrollHeight
}
}
}
</script>
しかし、updated
はメッセージ以外のpropsやdataが変わったタイミングでも呼ばれてしまうため、予期せぬタイミングでscrollToEnd()が呼ばれてしまう。 これはよくない
カスタムウォッチャを使う
メッセージの変更のみを拾いたいならカスタムウォッチャを使えばいい。
つまりこういうこと↓
<script>
watch: {
messages: function(newValue) {
this.scrollToEnd()
}
},
methods: {
scrollToEnd() {
const chatLog = this.$refs.'スクロールしたいref属性'
if (!chatLog) return
chatLog.scrollTop = chatLog.scrollHeight
}
}
}
</script>
しかし、カスタムウォッチャでメッセージの変更を拾ったとしても、DOMが変更されるのはその後であり、chatLog.scrollTop
が取得できないためこれもうまくいかない
結論
Vue.nextTick( [callback, context] )
をつかう
Vue公式ドキュメント
nuxtのドキュメントでもひっそり登場している
これはcallbaclで与えた処理をDOMの更新が終わるまで待つというもの。
カスタムウォッチャでnextTick
を呼び、その中でscrollToEnd()
呼ぶことで、メッセージが非同期で更新され、DOMの表示が終わったタイミングでメッセージの一番したまでスクロールすることができる
最終的にこういうこと↓
<script>
updated() {
this.scrollToEnd()
},
methods: {
scrollToEnd() {
this.$nextTick(() => {
const chatLog = this.$refs.'スクロールしたいref属性'
if (!chatLog) return
chatLog.scrollTop = chatLog.scrollHeight
})
}
}
}
</script>
終わりに
propsやdataが更新されたタイミングで何かをしたいというのはよくあるが、DOMが更新されたあとに何かをしたいというのは初めてだったため、結構悩んだ。
しかし、DOMの更新後を拾いたいタイミングはこれからも結構ありそうなのでnextTickは結構便利なんじゃないだろうか
Twitterもやってるので、よければフォローお願いします。
→https://twitter.com/ObataGenta