Ownedmedia ウェブ制作に役立つコンテンツを発信中!

Vue.js 2020.09.18

Vue.jsでスクロールに連動したコンテンツナビゲーションを実装する

Tags: ,

1枚もののウェブページで表示されているコンテンツに合わせてメニューのナビゲーションが変わるというような機能要件をVue.jsで実装してみたいと思います。このような機能は以前にもjQueryで実装したまとめ(「jQueryでスクロールと連動したコンテンツナビゲーションを実装する」)にて紹介していましたが、Vue.jsではもっとわかりやすく、また双方向バインディングでテンプレート側の切り分けも簡単にできるのでこちらの方がオススメですね。

 

それでは早速コードを見ていきます。まずはHTMLです、今回はサンプル用としてセクションをいくつか用意し、ヘッダー部分にセクションに対応した現在のコンテンツ名を出力する部分を設けました。その部分はVue.js側で返ってきた値をそのまま表示させるためマスタッシュ記法で記述しておきます。

【HTML(Pug)】※一部抜粋

div#app
  header
    span.
      {{ currentNavi }}
  main
    section(id="section_01")
      span コンテンツ1のエリアです
    section(id="section_02")
      span コンテンツ2のエリアです
    section(id="section_03")
      span コンテンツ3のエリアです
    section(id="section_04")
      span コンテンツ4のエリアです
    section(id="section_05")
      span コンテンツ5のエリアです

 

続いて処理の部分です。今回はVue.jsで新たにインスタンスを作成する方法でまとめていますが、単一コンポーネントを使う場合でも同じになります。まずはdataオプションに格納する値を変数として持たせておきます。ここでは縦方向のスクロール量と対象となるセクションのページトップからのY座標を配列で持たせるようにします。

 

computedのオプションでは、現在のスクロール量からどのコンテンツの位置にいるかを調べて、条件分岐させて対象となるコンテンツ名を返すようにしています。今回はこの値をそのままテンプレートに出力しています。

【JavaScript(Vue)】

var vueModel = new Vue({
  el: '#app',
  data() {
    return {
      scrollY: 0,
      sectionOffsetTop: []
    }
  },
  computed: {
    currentNavi() {
      if(this.scrollY >= this.sectionOffsetTop[0] && this.scrollY < this.sectionOffsetTop[1]) {
        return 'コンテンツ1'
      } else if(this.scrollY >= this.sectionOffsetTop[1] && this.scrollY < this.sectionOffsetTop[2]) {
        return 'コンテンツ2'
      } else if(this.scrollY >= this.sectionOffsetTop[2] && this.scrollY < this.sectionOffsetTop[3]) {
        return 'コンテンツ3'
      } else if(this.scrollY >= this.sectionOffsetTop[3] && this.scrollY < this.sectionOffsetTop[4]) {
        return 'コンテンツ4'
      } else if(this.scrollY >= this.sectionOffsetTop[4]) {
        return 'コンテンツ5'
      } else {
        return 'スクロールすると現在のコンテンツ名が表示されます'
      }
    }
  },
  mounted() {
    window.addEventListener('scroll', () => {
      this.pushScrollY();
    });
    window.addEventListener('resize', () => {
      this.pushScrollY();
    });
    this.pushElementOffsetTop();
  },
  methods: {
    pushScrollY() {
      this.scrollY = window.scrollY;
    },
    pushElementOffsetTop() {
      const targets = [
        'section_01', 'section_02', 'section_03', 'section_04', 'section_05'
      ];
      targets.forEach(target => {
        const element = document.getElementById(target);
        const offsetTop = Math.round(window.scrollY + element.getBoundingClientRect().top);
        this.sectionOffsetTop.push(offsetTop);
      });
    }
  }
});

 

続いてmethodsオプションでは今回の機能に必要となる処理を定義しています。ここで必要になるのは「現在のスクロール量を取得」する処理と「各セクションのページトップからのY座標を取得」する処理の2つになります。Y座標を取得する処理は「getBoundingClientRect().top」で取得できますが、スクロール分を考慮しないといけないのと、小数点が発生した場合の対応も忘れず行なっておきます。最後に、DOMを操作することになるのでmountedオプションで定義した処理を実行するようにしておきます。createdオプションで実行した場合にはDOMが取得できないのでundefinedが返ってきてしまいます。

 

これでスクロールに合わせたコンテンツナビゲーションが作成できました。今回のサンプルはこちらにあげておりますので、ぜひご参考ください。

See the Pen
contents_navi_on_vue
by designsupply (@designsupply)
on CodePen.

 


 

同じ機能でもjQueryではコードが複雑になりがちですが、Vue.jsを使うことでかなり見通しが良くなりました。カスタマイズするときも楽ですね。またHTMLのテンプレート側にも処理に合わせて動的に値を出力したりすることもできますので、より高度な見せ方も可能になりますね。1枚もののページを作成する場合にはぜひ試してみてはいかがでしょうか。

 

この記事を書いた人

オガワ シンヤ

合同会社デザインサプライ代表兼CEO / ディレクター・ウェブデザイナー・フロントエンドエンジニアをやっています。「ウェブとデザインでヒト・モノ・サービスを繋げ新しい価値を生み出す」をコンセプトに日々奮闘中!制作中はチョコレートが欠かせない三十路Webクリエイター。

  • Twitter

コメントフォーム

記事に関するご質問やご意見などありましたら下記のコメントフォームよりお気軽に投稿ください。なおメールアドレスは公開されませんのでご安心ください。

内容に問題なければ、お名前・ハンドルネームとメールアドレスを入力いただき、下記の「コメントを送信」ボタンを押してください。

CAPTCHA


この記事もよく読まれています

Scroll to Top
ご質問・ご相談はありませんか ?