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

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

ただいまの
回答率

88.91%

ページ内のリンク先セクションへスクロールする際、セクションの少し手前でスクロールを止めたい

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 190

thayamizu

score 7

下記smooth scrollingのjQueryでhtml上のaタグのリンク先セクション(同一ページ内)にクリックした時にスクロールするようにしたのですが、下記のままだとセクションの見出しと上部に固定しているナビゲーションバーがかぶってしまいました。
下記コードのどこかにoffsetを指定して、リンク先セクションにスクロールする際にナビゲーションバー分(50px)手前でスクロールを止めたかったのですが、うまくいきませんでした。
どのようにすればセクションの手前指定ができるのでしょうか?

$('a[href*="#"]')
    // Remove links that don't actually link to anything
    .not('[href="#"]')
    .not('[href="#0"]')
    .click(function (event) {
      // On-page links
      if (
        location.pathname.replace(/^\//, "") ==
          this.pathname.replace(/^\//, "") &&
        location.hostname == this.hostname
      ) {
        // Figure out element to scroll to
        var target = $(this.hash);
        target = target.length
          ? target
          : $("[name=" + this.hash.slice(1) + "]");
        // Does a scroll target exist?
        if (target.length) {
          // Only prevent default if animation is actually gonna happen
          event.preventDefault();
          $("html, body").animate(
            {
              scrollTop: target.offset().top,
            },
            1000,

            function () {
              // Callback after animation
              // Must change focus!
              var $target = $(target);
              $target.focus();
              if ($target.is(":focus")) {
                // Checking if the target was focused
                return false;
              } else {
                $target.attr("tabindex", "-1"); // Adding tabindex for elements not focusable
                $target.focus(); // Set focus again
              }
            }
          );
        }
      }
    });
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

HTMLが提示されてないので仮のクラス名を使ってます。

全ソース公開を受けてソースコードを差し替えました

jQueryプラグインSTICKY NAVIGATIONを使っているんですね。

プラグインを使用している場合はそのことを明記し、プラグインの公式へのリンクも記述してください。またスクリプトの一部だけではなく、全文を公開してください。おかげでプラグインあるのかー。ちゃんと問題ないかプラグインに影響を与えていないかテストしてて回答が遅れました

$(function () {
    /*FOR STICKY NAVIGATION*/
    var offsetY;
    if (!$("nav").hasClass("sticky")) {
        offsetY = $("nav").addClass("sticky").outerHeight();
        $("nav").removeClass("sticky");
    } else {
        offsetY = $("nav").outerHeight();
    }

    $(".js--section-features").waypoint(
      function (direction) {
          if (direction == "down") {
          $("nav").addClass("sticky");
        } else {
          $("nav").removeClass("sticky");
        }
      },
      {
        offset: offsetY,
      }
    );
    $(".js--scroll-to-plans").click(function () {
      $("html,body").animate(
        {
          scrollTop: $(".js--section-plans").offset().top - offsetY,
        },
        1000
      );
    });

    $(".js--scroll-to-start").click(function () {
      $("html,body").animate(
          {
          scrollTop: $(".js--section-features").offset().top - offsetY,
        },
        1000
      );
    });

    $('a[href*="#"]')
      // Remove links that don't actually link to anything
      .not('[href="#"]')
      .not('[href="#0"]')
      .click(function (event) {
        // On-page links
        if (
          location.pathname.replace(/^\//, "") ==
            this.pathname.replace(/^\//, "") &&
          location.hostname == this.hostname
        ) {
          // Figure out element to scroll to
          var target = $(this.hash); //ここでjQueryオブジェクトに変換済み
          target = target.length
            ? target
            : $("[name=" + this.hash.slice(1) + "]");
          // Does a scroll target exist?
          if (target.length) {
            // Only prevent default if animation is actually gonna happen
            //event.preventDefault();

            $("html,body").animate(
              {
                scrollTop: target.offset().top - offsetY,
              },
              1000,
              function () {
                // Callback after animation
                // Must change focus!
                var oldScroll = $(window).scrollTop(); 
                $(window).one('scroll', function() { //focus()の直前に一度だけ機能するscrollのイベントリスナを設定
                  $(window).scrollTop(oldScroll); //focus()によるスクロールを一度だけ上書きして無効化する。
                }); 

                target.focus(); //targetは既にjQueryオブジェクトになっているから変換は不要
                if (target.is(":focus")) {
                  // Checking if the target was focused
                  return false;
                } else {
                  target.attr("tabindex", "-1"); // Adding tabindex for elements not focusable
                  var oldScroll = $(window).scrollTop(); 
                  $(window).one('scroll', function() { //focus()の直前に一度だけ機能するscrollのイベントリスナを設定
                      $(window).scrollTop(oldScroll); //focus()によるスクロールを一度だけ上書きして無効化する。 
                  }); 
                  target.focus(); // Set focus again
                }
              }
            );
          }
        }
     });

    $(".js--wp-1").waypoint(
      function (direction) {
        $(".js--wp-1").addClass("animated animate__fadeIn");
      },
      {
        offset: "50%",
      }
    );

    $(".js--wp-2").waypoint(
      function (direction) {
        $(".js--wp-2").addClass("animated animate__fadeInUp");
      },
      {
        offset: "50%",
      }
    );

    $(".js--wp-3").waypoint(
      function (direction) {
        $(".js--wp-3").addClass("animated animate__fadeIn");
      },
      {
        offset: "50%",
      }
    );

    $(".js--wp-4").waypoint(
      function (direction) {
        $(".js--wp-4").addClass("animated animate__pulse");
      },
      {
        offset: "50%",
      }
    );

    $(".js--nav-icon").click(function () {
      var nav = $(".js--main-nav");
      var icon = $(".js--nav-icon i");

      nav.slideToggle(200);

      if (icon.hasClass("ion-navicon-round")) {
        icon.addClass("ion-close-round");
        icon.removeClass("ion-navicon-round");
      } else {
        icon.addClass("ion-navicon-round");
        icon.removeClass("ion-close-round");
      }
    });
  });

  // フォーカス系のイベントでのスクロール処理をキャンセルする。
  //
  // firefox以外のブラウザは要素がフォーカスされた場合、フォームのテキストエリアなど入力がスムーズに
  // 出来るように、その要素をスクロールさせる。(正常な動き)その後その要素に対してselectが
  // 発生します。
  // firefoxはバグっててスクロールせずにフォーカスだけがあたる。
  //
  // コード内で要素.focus()する場合は、この解決策でも回避できない場合があります
  // これでもたまにスクロールすることがあり、もう一度クリックすれば直ります。
  // ブラウザ側の実装の問題なのでこれ以上は対処できません。
  //
  // 問題が無ければ、要素.focus()ではなく、要素.select()を検討してみて下さい
  // そうすれば、スクロールの問題は解決するはずです。
  $('section').bind('focusin focus', function(event){ 
    event.preventDefault(); 
  });

firefoxのみ正しく動いて見えたのはfirefoxにバグがあってスクロールしなかったからで
firefox以外のブラウザの動作が正常動作です。
動作確認してみたところ、IEはバグっててダメでした、edge(WEBから落としてきた最新版のedge),chrome,firefoxでは一応問題ありませんでしたが、chromeでまれにスクロールする事がありましたが再度クリックで直ります。
その他についてはソースにコメントを入れてあるので参照してください。

はぁ、プラグインもテストしてて予想外に疲れた・・・

追記:更なる改良でほぼfocus()のスクロールをIE以外で無効化できましたので、再再度ソースを差し替えました

focus()の直前に以下のコードを入れておくと最後に追加しているイベントハンドラと合わせて
ほぼ無効化できました。
focus()の直前に以下のコードを入れ込む

var oldScroll = $(window).scrollTop(); 
$(window).one('scroll', function() { //focus()の直前に一度だけ機能するscrollのイベントリスナを設定
    $(window).scrollTop(oldScroll); //focus()によるスクロールを一度だけ上書きして無効化する。
}); 

このコードと、最後に追加した'focusin focus'イベントリスナの2重のガードで、IE以外は完全に動作が正常化されました。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/08 17:58

    ソースコードの更なる改良でIE以外はほぼ正常に動作するようになりました。
    このコードで動作確認してみてください。

    キャンセル

  • 2020/07/09 10:47

    ありがとうございます!
    確認してみたところ、やりたかったことが実現できました!
    プラグインがあると影響を与えることも知らずにお手数おかけしてすみませんでした。
    firefoxが正常に見えていたのはfirefoxのバグだとは思ってもおりませんでした。丁寧に教えて頂き本当にありがとうございます。勉強になりました!

    キャンセル

  • 2020/07/09 12:18

    ただこれだけやっても100%じゃないんですよ><
    リンクを連打されるとoneのイベントリスナのつけ外しが間に合わなくて
    スクロールすることがあります。
    が、特殊なケースだとおもうので、ほぼこれでいいと思います。

    キャンセル

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

  • ただいまの回答率 88.91%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る