質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.47%
HTML5

HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

1回答

802閲覧

javascriptで1画面ずつスクロールする動きを実装したい

suzu1234

総合スコア41

HTML5

HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

1クリップ

投稿2022/09/23 12:05

前提

以下のサイトを参考に1画面ずつスクロールする動きを実装しています。記載されているコードについてある程度理解することができたのですが、2点ほど調べてもわからない箇所があったため、お知恵をを拝借したく質問させていただきました。

参考ページ:https://mukuchi.work/wheelpage/
デモページ:https://mukuchi.work/demos/wheelPage/

該当のソースコード

<div id="wrap"> <div class="page1">1</div> <div class="page2">2</div> <div class="page3">3</div> <div class="page4">4</div> <div class="page5">5</div> </div>
body { margin: 0; padding: 0; overflow: hidden; } #wrap div { width: 100%; height: 100vh; line-height: 100vh; text-align: center; font-size: 30px; } .page1 { background-color: aqua; } .page2 { background-color: antiquewhite; } .page3 { background-color: tomato; } .page4 { background-color: palegreen; } .page5 { background-color: orange; }
(function () { 'use strict'; // 変数 var wrap = document.getElementById('wrap'), elements = document.querySelectorAll('#wrap div'), // 1画面分スクロールさせる要素 elRect = [], // 要素の位置情報を取得するための配列 elTop = [], // 要素の位置を入れるための配列 count = 0, // wheelFlag = false; // 各要素の位置を取得 function getElTop() { for (var i = 0; i < elements.length; i++) { // 要素の数ループ elRect.push(elements[i].getBoundingClientRect()); // 要素の位置情報を配列へ } for (var i = 0; i < elRect.length; i++) { // 要素の数ループ elTop.push(elRect[i].top + window.scrollY); // 要素の位置を配列へ } } getElTop(); // 画面リサイズのときの処理 window.addEventListener('resize', function () { elRect = []; // 位置情報の配列を一旦空に elTop = []; // 位置の配列を一旦空に getElTop(); // 位置を取得 window.scrollTo(0, elTop[count]); // 現在表示中の画面位置へ }); // マウスホイールのときの処理 wrap.addEventListener('wheel', function (e) { e.preventDefault(); // デフォルトのスクロール動作を削除 if (!wheelFlag) { // wheelFlagがfalseのときに発動 wheelFlag = true; // wheelFlagをtrueにして無駄に発動しないように if (e.deltaY > 0) { // ホイールが下方向だったら if (count >= elements.length - 1) { // 要素の数以上に増えないようにcountが要素の数を超えたら count = elements.length - 1; // countを要素の数とする } else { count++; // それまではcountをプラス } } else if (e.deltaY < 0) { // ホイールが上方向だったら if (count <= 0) { // 0より小さくならないようにcountが0以下なら count = 0; // countを0とする } else { count--; // それまではcountをマイナスしていく } } setTimeout(function () { //0.8秒後にwheelFlagをfalseにして次のページめくれるように wheelFlag = false; },800); setTimeout(function () { window.scrollTo({ // count番目の要素へスクロール top: elTop[count], behavior: 'smooth', }); },20); // スクロールまで時間差をつけて慣性スクロール対策 } }); }());

わからないこと

【1点目】
JSの18行目にあります以下のコードなのですが、window.scrollYを足しているのはなぜでしょう?試しにwindow.scrollYを消してみたところ正常に作動していたので、どういった意味があるのか教えていただきたいです。

「elTop.push(elRect[i].top + window.scrollY);」

【2点目】
10行目で「wheelFlag」にfalseを代入していると思うのですが、33行目の条件式が「!wheelFlag」となっているにもかかわず、説明には”falseのときに発動”となっています。10行目にfalseが代入されており!が付いているのでtrueでの発動ではないのでしょうか?
また、33行目の条件式が発火する理屈についても教えていただきたいです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

suzu1234

2022/09/23 12:52

cssのみで実装できるのですね、、有益な情報をありがとうございます。ブクマさせていただきます。 ですが、後学のためにjsのコードを理解したいと思っております
hatena19

2022/09/23 13:10

私自身は必要ないと思ってますので、それについては他の回答者に譲りますが、 現在では、スクロール連動エフェクトにスクロールイベントを使うこと自体が時代遅れになってます。代わりに、Intersection Observerを使うことが推奨されてます。 JSでのスクロール連動エフェクトにはIntersection Observerが便利 - ICS MEDIA https://ics.media/entry/190902/ こちらを学習する方がより後学のためになると思います。
guest

回答1

0

ベストアンサー

1つ目

getBoundingClientRectで取得できるのは、「ユーザーが見えている画面の左上端から要素までの距離」です。
なので、HTML全体がスクロールされていて「画面の上端」と「HTML全体の上端」がずれている場合にスクロール位置を調整するためにwindow.scrollYを足しています。

2つ目

wheelFlagがfalseの時に!wheelFlagがtrueになるので「wheelFlagがfalseのときに発動」と書いているのだと思います。

蛇足

参考にしているコードですが古い所や無駄な所があり、あまり参考になるコードではなさそうです。

  • 変数の定義にvarを使っていますが、varはエラーが出なくてバグを見逃しやすいので、現在ではletconstが推奨されます。
  • elRect変数をトップレベルで宣言していますが使っているのはgetElTop関数のみなのでgetElTop関数内で宣言したほうがいいですし、それで「配列を一旦空に」の処理も必要なくなります。
  • そもそもgetElTop関数内で無駄にfor文を二回回していますが、これを一つのループにまとめれば、elRectも必要なくなります。
  • querySelectorAllの返り値はfor-ofが使えるのでこちらのほうが便利でミスも少ないです。

などを踏まえて修正。
呼び出す側でelTop = getElTop();とすることで、elTop変数が変更されるも場所をわかりやすくしまました。

js

1const wrap = document.getElementById('wrap'), 2 elements = document.querySelectorAll('#wrap div'); 3let elTop = getElTop(), 4 count = 0, 5 wheelFlag = false; 6function getElTop() { 7 const elTop = []; 8 for (const element of elements) { 9 const rect = element.getBoundingClientRect(); 10 elTop.push(rect.top + window.scrollY); 11 } 12 return elTop; 13} 14window.addEventListener('resize', function () { 15 elTop = getElTop(); 16 window.scrollTo(0, elTop[count]); 17}); 18// 以下は同じ

また既に言われている通りresizeイベントよりIntersectionObserverを使うのが望ましいです。


参考用にMDNのリンクを置いておきます。

https://developer.mozilla.org/ja/docs/Web/API/Element/getBoundingClientRect
https://developer.mozilla.org/ja/docs/Web/API/Window/scrollY
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/let
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/const
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/for...of

投稿2022/09/25 00:45

ka2obushi

総合スコア173

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

suzu1234

2022/09/26 16:23

ご回答ありがとうございます!新たにコードも書いていただいて凄く参考になりました。 いただいた回答をもとに実装してみたいと思います!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.47%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問