21
23

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Vue.jsでスクロール時のイベントを便利に定義する

Last updated at Posted at 2019-10-01

追記: この記事でやっていることをプラグイン化しました! → https://github.com/laineus/vue-scroll-hook

やりたいこと

わかりづらいので、具体的にやりたかったことを書きます。

自分が作っていたアプリでは、「ページを下のほうにスクロールしたらトップへ戻るボタンを出したい」といった、「スクロールしたときに何かする」的な処理が結構ありました。

そのたびに、↓こんな感じにcreateddestroyedでイベントを登録・解除するのがめんどいな、と。

<script>
export default {
  created () {
    window.addEventListener('scroll', this.onScroll)
  },
  destroyed () {
    window.removeEventListener('scroll', this.onScroll)
  },
  methods: {
    onScroll () {
      // Do something
    }
  }
}
</script>

そこで、↓こんな感じに書くだけで、スクロールしたときのフックを定義できるようにしました。

<script>
export default {
  onScroll () {
    // Do something
  }
}
</script>

実装

公式ドキュメントをぱっと見した限り、ディレクティブをカスタマイズする手段はあるようですが、上記のようなフックのカスタマイズは見つかりませんでした。

なので、GlobalなMixinを作って実現しました。

main.js
import Vue from 'vue'
// ..
import GlobalMixin from './GlobalMixin'

Vue.mixin(GlobalMixin)

// ..

↓onScrollが定義されていた場合、windowのscrollイベントに登録し、destroyedで削除します。

GlobalMixin.vue
<script>
export default {
  mounted () {
    // Register onScroll event
    if (!this.$options.onScroll) return
    requestAnimationFrame(() => { // ページ遷移の際にVueRouterが実行するy=0へのスクロールを無視するための処理です
      if (this._isDestroyed) return // この数フレームの間にコンポーネントが破棄される可能性を考慮しています
      window.addEventListener('scroll', this.$options.onScroll)
    })
  },
  destroyed () {
    // Unregister onScroll event
    if (!this.$options.onScroll) return
    window.removeEventListener('scroll', this.$options.onScroll)
  }
}
</script>

これだけです。

(余計なお世話かもしれませんが、↑このmixinはむやみに拡張しないでください)

そして、冒頭のように各コンポーネントでonScrollという名前でイベントを定義するだけです。

<script>
export default {
  data () {
    return {
      showButton: false
    }
  },
  onScroll () {
    this.showButton = window.scrollY > 100
  }
}
</script>

余談

公式のカスタムディレクティブの例でまさにスクロール時のイベントを定義するサンプルがあったんですが、ちょっとしっくりこないです…。

<div id="app">
  <h1 class="centered">Scroll me</h1>
  <div
    v-scroll="handleScroll"
    class="box"
  >
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. A atque amet harum aut ab veritatis earum porro praesentium ut corporis. Quasi provident dolorem officia iure fugiat, eius mollitia sequi quisquam.</p>
  </div>
</div>

@scroll的な感じで、.boxがスクローラブルな要素で、そのonscrollに定義される感じがするけど、結局window全体のonscrollに定義してるし、そもそも外からイベントを定義したいケースも思いつかない。

handleScrollの引数に渡したelementを直接弄ってるのも手続き的で気持ちよくないです…。

21
23
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
23

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?