目次
画面の後ろにすばやく理解してください
もちろん、登録されているイベントリスナー(OnClick)では、受信クリックイベントオブジェクトのターゲットをチェックして、ページのリンク要素に関連しているかどうかを確認する必要があります。これはさまざまな方法で行うことができるため、ヘルパー関数isinpagelink()として抽象化します。この機能のメカニズムを後で見ていきます。
コールバックで実行され、ジャンプ機能に渡し、スクロールしたい要素のハッシュを過去に渡します:
私たちが書いたコードで(CSSサポートが利用できない場合、それをフォールバックスキームと考えることができます)、ブラウザの履歴に対するスクリプトの動作を考慮しませんでした。コンテキストとユースケースに応じて、これは興味深いものである場合とそうでない場合がありますが、スクリプトがデフォルトのスクロールエクスペリエンスを強化する必要があると考える場合、CSSのように一貫した動作を期待する必要があります。
FAQ(FAQ)
ライブラリを使用せずにネイティブJavaScriptを使用してスムーズなスクロールを実現するにはどうすればよいですか?
特定の要素にスムーズにスクロールするには、
を使用して、速度を含むスクロールアニメーションをより適切に制御するために、カスタムスムーズなスクロール機能を作成できます。
および
を使用してアニメーションフレームをキャンセルしてアニメーションを停止できます。
キーボードナビゲーションを使用してスムーズなスクロールを実現する方法は?
ホームページ ウェブフロントエンド jsチュートリアル バニラJavaScriptにスムーズなスクロールを実装する方法

バニラJavaScriptにスムーズなスクロールを実装する方法

Feb 18, 2025 am 10:49 AM

How to Implement Smooth Scrolling in Vanilla JavaScript

コアポイント

  • Jump.jsライブラリを使用して、ネイティブJavaScriptのスムーズなスクロールを実装し、外部依存関係なしにスクロールアニメーションを簡素化します。
  • Jump.jsの元のコードを変更してES6からES5に変換して、さまざまなブラウザーとのより広い互換性を確保します。
  • requestAnimationFrameメソッドを使用して、スムーズなアニメーションの更新を実行し、パフォーマンスを最適化し、スムーズなユーザーエクスペリエンスを提供します。
  • カスタムJavaScriptを実装して、デフォルトのページ内リンク動作をインターセプトし、突然のジャンプをスムーズなスクロールアニメーションに置き換えます。
  • この機能を認識するブラウザでのネイティブスムーススクロールをサポートするための統合されたCSS
  • プロパティと、ブラウザがサポートしていない場合はJavaScriptフォールバックメカニズムが提供されます。 scroll-behavior スクロール後にターゲット要素にフォーカスを設定し、キーボードナビゲーションの潜在的な問題に対処し、すべてのユーザーのユーザビリティを向上させることにより、アクセシビリティを確保します。
この記事は、エイドリアン・サンドゥ、クリス・ペリー、ジェレミー・ヘレイン、マロリー・ヴァン・アチェルバーグによって査読されました。 SitePointで最高のコンテンツを取得するためにSetePointのすべてのピアレビューアに感謝します!

Smooth Scrollingは、デフォルトの内部ナビゲーションエクスペリエンスを徐々に強化するユーザーインターフェイスモードであり、アクティベーションリンクの位置からリンクURLまで、スクロールボックス内の位置(ビューポートまたはスクロール可能な要素)をアニメーション化します。クリップに示されています。 これは新しいものではなく、長年にわたって既知のパターンでした。たとえば、2003年に遡るこのSitePointの記事をご覧ください!ちなみに、この投稿は、クライアント側のJavaScriptプログラミング、特にDOMが長年にわたってどのように変化および進化し、より簡単なネイティブJavaScriptソリューションの開発を可能にしているため、歴史的に価値があります。

jQueryエコシステムでは、このパターンには多くの実装があり、jqueryまたはプラグインで直接実装できますが、この記事では、純粋なJavaScriptソリューションに興味があります。具体的には、jump.jsライブラリを探索して利用します。

ライブラリとその機能と機能の概要を紹介した後、ニーズに合わせて元のコードにいくつかの変更を加えます。その過程で、関数や閉鎖などのコアJavaScript言語スキルをレビューします。次に、HTMLページを作成して、スムーズなスクロール動作をテストし、カスタムスクリプトとして実装します。その後、CSSでのネイティブスムーススクロールのサポートが追加され(利用可能な場合)、最終的にブラウザナビゲーション履歴についていくつかの観察を行います。

これは、作成する最終的なデモです:

CodepenでSitePoint(@SitePoint)用の滑らかなスクロールペンを表示します。

完全なソースコードはGitHubで見つけることができます。

Jump.js

Jump.jsはネイティブES6 JavaScriptで記述されており、外部依存関係はありません。これはわずか42スロックしかない小規模ですが、提供される最小化パッケージのサイズは約2.67 kbで、翻訳する必要があります。 GitHubプロジェクトページにデモが提供されています。

名前が示すように、それはジャンプのみを提供します:スクロールバーの位置のアニメーションの変更は、現在の値からターゲット位置への変更、DOM要素、CSSセレクター、または正またはネガティブの形の距離を提供することによって指定されます数値。これは、スムーズなスクロールモードの実装では、自分自身をハイジャックするリンクを実行する必要があることを意味します。詳細については、以下のセクションを参照してください。

現在、ビューポートの垂直スクロールのみがサポートされていることに注意してください。

期間(このパラメーターが必要)、アニメーションの最後にトリガーされるコールバックなど、いくつかのオプションを使用してジャンプを構成できます。デモの後半で彼らの実際のアプリケーションを見ることができます。詳細については、ドキュメントを参照してください。

Jump.jsは、Internet Explorerバージョン10以降など、問題なく「モダンな」ブラウザで実行されます。繰り返しますが、サポートされているブラウザの完全なリストについては、ドキュメントを参照してください。適切なRequestAnimationFrame PolyFillを使用すると、古いブラウザーで実行することもできます。

画面の後ろにすばやく理解してください

内部的には、Jump.jsソースコードは、ウィンドウオブジェクトのRequestAnimationFrameメソッドを使用して、スクロールアニメーションの各フレームで更新されたViewPortの垂直位置の位置をアレンジします。この更新は、easing inasing関数をWindow.scrolltoメソッドに使用して計算された次の位置値を渡すことによって達成されます。詳細については、ソースコードを参照してください。

いくつかのカスタマイズ

デモを掘り下げる前に、元のコードにいくつかの小さな変更を加えて、Jump.jsの使用方法を示しますが、それが内部での動作方法を変更しません。

ソースコードはES6で記述されており、モジュールを翻訳およびバンドルするためのJavaScriptビルドツールで使用する必要があります。これは一部のプロジェクトでは少し多すぎるかもしれないので、リファクタリングを適用して、どこでも使用するためにコードをES5に変換します。

最初に、ES6の構文と機能を削除しましょう。スクリプトは、ES6クラスを定義します:

<code>import easeInOutQuad from './easing'

export default class Jump {
  jump(target, options = {}) {
    this.start = window.pageYOffset

    this.options = {
      duration: options.duration,
      offset: options.offset || 0,
      callback: options.callback,
      easing: options.easing || easeInOutQuad
    }

    this.distance = typeof target === 'string'
      ? this.options.offset + document.querySelector(target).getBoundingClientRect().top
      : target

    this.duration = typeof this.options.duration === 'function'
      ? this.options.duration(this.distance)
      : this.options.duration

    requestAnimationFrame(time => this._loop(time))
  }

  _loop(time) {
    if(!this.timeStart) {
      this.timeStart = time
    }

    this.timeElapsed = time - this.timeStart
    this.next = this.options.easing(this.timeElapsed, this.start, this.distance, this.duration)

    window.scrollTo(0, this.next)

    this.timeElapsed       ? requestAnimationFrame(time => this._loop(time))
      : this._end()
  }

  _end() {
    window.scrollTo(0, this.start + this.distance)

    typeof this.options.callback === 'function' && this.options.callback()
    this.timeStart = false
  }
}
</code>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
コンストラクターと多数のプロトタイプメソッドを使用してES5「クラス」に変換できますが、このクラスの複数のインスタンスは決して必要ないので、通常のオブジェクトで実装されたシングルトンは問題ありません。

クラスを削除することに加えて、他の変更を加える必要があります。
<code>var jump = (function() {

    var o = {

        jump: function(target, options) {
            this.start = window.pageYOffset

            this.options = {
              duration: options.duration,
              offset: options.offset || 0,
              callback: options.callback,
              easing: options.easing || easeInOutQuad
            }

            this.distance = typeof target === 'string'
              ? this.options.offset + document.querySelector(target).getBoundingClientRect().top
              : target

            this.duration = typeof this.options.duration === 'function'
              ? this.options.duration(this.distance)
              : this.options.duration

            requestAnimationFrame(_loop)
        },

        _loop: function(time) {
            if(!this.timeStart) {
              this.timeStart = time
            }

            this.timeElapsed = time - this.timeStart
            this.next = this.options.easing(this.timeElapsed, this.start, this.distance, this.duration)

            window.scrollTo(0, this.next)

            this.timeElapsed               ? requestAnimationFrame(_loop)
              : this._end()
        },

        _end: function() {
            window.scrollTo(0, this.start + this.distance)

            typeof this.options.callback === 'function' && this.options.callback()
            this.timeStart = false
        }

    };

    var _loop = o._loop.bind(o);

    // Robert Penner's easeInOutQuad - http://robertpenner.com/easing/
    function easeInOutQuad(t, b, c, d)  {
        t /= d / 2
        if(t         t--
        return -c / 2 * (t * (t - 2) - 1) + b
    }

    return o;

})();
</code>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
のコールバックは、各フレームのスクロールバーの位置を更新するために使用され、元のコードでは、ES6矢印関数を介して呼び出され、初期化時にジャンプシングルトンにプリバウンドします。次に、同じソースファイルにデフォルトの緩和関数をバンドルします。最後に、名前空間汚染を避けるために、Iife(すぐに関数式を呼び出す)を使用してコードをラップします。

requestAnimationFrame別の再構成ステップを適用できます。ネストされた機能と閉鎖の助けを借りて、オブジェクトの代わりに関数を使用できることに注意してください。

シングルトンは、アニメーションスクロール、ループ、エンドのコールバックがネストされた関数になるように呼び出されるジャンプ関数であり、オブジェクトのプロパティがローカル変数(クロージャー)になります。すべてのコードが機能に安全にラップされているため、Iifeはもう必要ありません。

最終的なリファクタリングステップとして、ループコールバックが呼び出されるたびに繰り返しのタイムスタートリセットチェックを回避するために、最初の時間requestAnimationFrame()が呼び出されます。匿名関数を渡します。ループ関数リセットTimerStART変数:

<code>import easeInOutQuad from './easing'

export default class Jump {
  jump(target, options = {}) {
    this.start = window.pageYOffset

    this.options = {
      duration: options.duration,
      offset: options.offset || 0,
      callback: options.callback,
      easing: options.easing || easeInOutQuad
    }

    this.distance = typeof target === 'string'
      ? this.options.offset + document.querySelector(target).getBoundingClientRect().top
      : target

    this.duration = typeof this.options.duration === 'function'
      ? this.options.duration(this.distance)
      : this.options.duration

    requestAnimationFrame(time => this._loop(time))
  }

  _loop(time) {
    if(!this.timeStart) {
      this.timeStart = time
    }

    this.timeElapsed = time - this.timeStart
    this.next = this.options.easing(this.timeElapsed, this.start, this.distance, this.duration)

    window.scrollTo(0, this.next)

    this.timeElapsed       ? requestAnimationFrame(time => this._loop(time))
      : this._end()
  }

  _end() {
    window.scrollTo(0, this.start + this.distance)

    typeof this.options.callback === 'function' && this.options.callback()
    this.timeStart = false
  }
}
</code>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
再構成プロセス中に、コアスクロールアニメーションコードが変更されていないことに再度注意してください。

テストページ

ニーズに合わせてスクリプトをカスタマイズしたので、テストデモを組み立てる準備ができました。このセクションでは、次のセクションで説明したスクリプトを使用してスムーズなスクロールを強化するページを書きます。

このページには、ドキュメントの以降のセクションのページ内のリンクを指し、TOCへの他のリンクを指す目次(TOC)が含まれています。また、いくつかの外部リンクを他のページに組み合わせます。これは、このページの基本構造です:

<code>var jump = (function() {

    var o = {

        jump: function(target, options) {
            this.start = window.pageYOffset

            this.options = {
              duration: options.duration,
              offset: options.offset || 0,
              callback: options.callback,
              easing: options.easing || easeInOutQuad
            }

            this.distance = typeof target === 'string'
              ? this.options.offset + document.querySelector(target).getBoundingClientRect().top
              : target

            this.duration = typeof this.options.duration === 'function'
              ? this.options.duration(this.distance)
              : this.options.duration

            requestAnimationFrame(_loop)
        },

        _loop: function(time) {
            if(!this.timeStart) {
              this.timeStart = time
            }

            this.timeElapsed = time - this.timeStart
            this.next = this.options.easing(this.timeElapsed, this.start, this.distance, this.duration)

            window.scrollTo(0, this.next)

            this.timeElapsed               ? requestAnimationFrame(_loop)
              : this._end()
        },

        _end: function() {
            window.scrollTo(0, this.start + this.distance)

            typeof this.options.callback === 'function' && this.options.callback()
            this.timeStart = false
        }

    };

    var _loop = o._loop.bind(o);

    // Robert Penner's easeInOutQuad - http://robertpenner.com/easing/
    function easeInOutQuad(t, b, c, d)  {
        t /= d / 2
        if(t         t--
        return -c / 2 * (t * (t - 2) - 1) + b
    }

    return o;

})();
</code>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
頭には、基本的な最も単純なレイアウトを設定するためのいくつかのCSSルールを含め、ボディタグの最後に2つのJavaScriptファイルを含めます。それは私たちが今議論するスクリプトですか?

メインスクリプト

これは、jump.jsライブラリのカスタマイズされたバージョンからのアニメーションジャンプを使用して、テストページスクロールエクスペリエンスを強化するスクリプトです。もちろん、このコードはES5 JavaScriptにも記述されます。

すべきことの簡単な概要を見てみましょう:ページ内のリンクのクリックをハイジャックする必要があります。リンク、そしてJump()関数への呼び出しに置き換えます。

したがって、まず、ページのリンクのクリックを監視する必要があります。これを2つの方法で行うことができます。イベントデリゲートを使用するか、関連する各リンクにハンドラーを添付します。 イベント委員会

最初の方法では、クリックリスナーを要素document.bodyに追加します。このようにして、ページ上の任意の要素のクリックイベントごとに、祖先の枝に沿ってドキュメントに到達するまでdomツリーに泡立ちます。

着信クリックがページ内リンクにある場合、イベントバブルを停止し、関連するデフォルトアクションをブロックします。最後に、ジャンプ関数を呼び出して、ターゲット要素のハッシュセレクターと、必要なアニメーションを構成するパラメーターを提供します。

<code>function jump(target, options) {
    var start = window.pageYOffset;

    var opt = {
      duration: options.duration,
      offset: options.offset || 0,
      callback: options.callback,
      easing: options.easing || easeInOutQuad
    };

    var distance = typeof target === 'string' ? 
        opt.offset + document.querySelector(target).getBoundingClientRect().top : 
        target
    ;

    var duration = typeof opt.duration === 'function'
          ? opt.duration(distance)
          : opt.duration
    ;

    var 
        timeStart = null,
        timeElapsed
    ;

    requestAnimationFrame(loop);

    function loop(time) {
        if (timeStart === null)
            timeStart = time;

        timeElapsed = time - timeStart;

        window.scrollTo(0, opt.easing(timeElapsed, start, distance, duration));

        if (timeElapsed         requestAnimationFrame(loop)
        else
            end();
    }

    function end() {
        window.scrollTo(0, start + distance);

        typeof opt.callback === 'function' && opt.callback();
        timeStart = null;
    }

    // ...

}
</code>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
これはイベントハンドラーです:

シングルハンドラー

2番目のメソッドを使用してリンククリックを監視し、上記のイベントハンドラーのわずかに変更されたバージョンを各ページ内のリンク要素に添付するため、イベントバブルはありません:

<code>import easeInOutQuad from './easing'

export default class Jump {
  jump(target, options = {}) {
    this.start = window.pageYOffset

    this.options = {
      duration: options.duration,
      offset: options.offset || 0,
      callback: options.callback,
      easing: options.easing || easeInOutQuad
    }

    this.distance = typeof target === 'string'
      ? this.options.offset + document.querySelector(target).getBoundingClientRect().top
      : target

    this.duration = typeof this.options.duration === 'function'
      ? this.options.duration(this.distance)
      : this.options.duration

    requestAnimationFrame(time => this._loop(time))
  }

  _loop(time) {
    if(!this.timeStart) {
      this.timeStart = time
    }

    this.timeElapsed = time - this.timeStart
    this.next = this.options.easing(this.timeElapsed, this.start, this.distance, this.duration)

    window.scrollTo(0, this.next)

    this.timeElapsed       ? requestAnimationFrame(time => this._loop(time))
      : this._end()
  }

  _end() {
    window.scrollTo(0, this.start + this.distance)

    typeof this.options.callback === 'function' && this.options.callback()
    this.timeStart = false
  }
}
</code>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

すべての要素をクエリし、.slice()Trickを使用して、返されたDOMノデリストをJavaScriptアレイに変換します(ターゲットブラウザがES6 Array.from()を使用する場合、より良い代替手段方法)。次に、配列メソッドを使用してページ内のリンクをフィルタリングし、上記の同じヘルパー関数を再利用し、最後にリスナーを残りのリンク要素に接続できます。 []

イベントハンドラーは以前とほぼ同じですが、もちろんクリックターゲットを確認する必要はありません。

どのメソッドが使用のコンテキストに依存するのが最適です。たとえば、最初のページの読み込み後に新しいリンク要素を動的に追加できる場合、イベントデリゲートを使用する必要があります。
<code>var jump = (function() {

    var o = {

        jump: function(target, options) {
            this.start = window.pageYOffset

            this.options = {
              duration: options.duration,
              offset: options.offset || 0,
              callback: options.callback,
              easing: options.easing || easeInOutQuad
            }

            this.distance = typeof target === 'string'
              ? this.options.offset + document.querySelector(target).getBoundingClientRect().top
              : target

            this.duration = typeof this.options.duration === 'function'
              ? this.options.duration(this.distance)
              : this.options.duration

            requestAnimationFrame(_loop)
        },

        _loop: function(time) {
            if(!this.timeStart) {
              this.timeStart = time
            }

            this.timeElapsed = time - this.timeStart
            this.next = this.options.easing(this.timeElapsed, this.start, this.distance, this.duration)

            window.scrollTo(0, this.next)

            this.timeElapsed               ? requestAnimationFrame(_loop)
              : this._end()
        },

        _end: function() {
            window.scrollTo(0, this.start + this.distance)

            typeof this.options.callback === 'function' && this.options.callback()
            this.timeStart = false
        }

    };

    var _loop = o._loop.bind(o);

    // Robert Penner's easeInOutQuad - http://robertpenner.com/easing/
    function easeInOutQuad(t, b, c, d)  {
        t /= d / 2
        if(t         t--
        return -c / 2 * (t * (t - 2) - 1) + b
    }

    return o;

})();
</code>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

ここで、Isinpagelink()の実装に目を向けます。これは、以前のイベントハンドラーでこのヘルパー機能を使用して、ページのリンクのテストを抽象化しました。ご覧のとおり、この関数はDOMノードをパラメーターとして取得し、ブール値を返して、ノードがページのリンク要素を表すかどうかを示します。リンクが別のページを指している可能性があるため、通過したノードがAタグであり、ハッシュフラグメントが設定されていることを確認するだけでは十分ではありません。その場合、デフォルトのブラウザアクションを無効にする必要はありません。したがって、プロパティHREFに保存されている値「マイナス」ハッシュフラグメントがページURLに等しいかどうかを確認します。

StripHash()は別のヘルパー関数であり、スクリプトが初期化されたときに変数pageURLの値を設定するためにも使用します。

<code>function jump(target, options) {
    var start = window.pageYOffset;

    var opt = {
      duration: options.duration,
      offset: options.offset || 0,
      callback: options.callback,
      easing: options.easing || easeInOutQuad
    };

    var distance = typeof target === 'string' ? 
        opt.offset + document.querySelector(target).getBoundingClientRect().top : 
        target
    ;

    var duration = typeof opt.duration === 'function'
          ? opt.duration(distance)
          : opt.duration
    ;

    var 
        timeStart = null,
        timeElapsed
    ;

    requestAnimationFrame(loop);

    function loop(time) {
        if (timeStart === null)
            timeStart = time;

        timeElapsed = time - timeStart;

        window.scrollTo(0, opt.easing(timeElapsed, start, distance, duration));

        if (timeElapsed         requestAnimationFrame(loop)
        else
            end();
    }

    function end() {
        window.scrollTo(0, start + distance);

        typeof opt.callback === 'function' && opt.callback();
        timeStart = null;
    }

    // ...

}
</code>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
この文字列ベースのソリューションとハッシュフラグメントの剪定は、ハッシュパーツがURLの一般的な構造にあるため、クエリ文字列を備えたURLでも正常に機能します。

前に言ったように、これはこのテストを実装する1つの可能な方法にすぎません。たとえば、このチュートリアルの冒頭で引用された記事では、異なるソリューションを使用して、リンクHREFのコンポーネントレベルの比較をロケーションオブジェクトと実行します。

<code>requestAnimationFrame(function(time) { timeStart = time; loop(time); });

function loop(time) {
    timeElapsed = time - timeStart;

    window.scrollTo(0, opt.easing(timeElapsed, start, distance, duration));

    if (timeElapsed         requestAnimationFrame(loop)
    else
        end();
}
</code>
ログイン後にコピー
この関数は両方のイベントサブスクリプションメソッドで使用していることに注意してくださいが、2番目の方法では、既に知っている要素のフィルターとして使用していることに注意してください。冗長。これは、演習として読者に任されています。

アクセシビリティの考慮事項

今のところ、私たちのコードは、キーボードユーザーに影響を与える既知のエラー(実際には、Blink/webkit/khtmlに影響する無関係なエラーとIEに影響するエラーのペア)の影響を受けやすいです。 TOCリンクをタブキーを介して閲覧すると、リンクをアクティブにすると、選択したセクションまでスムーズにスクロールしますが、リンクには焦点が残ります。これは、次のタブキーが押されると、ユーザーが選択したセクションの最初のリンクの代わりにTOCに送り返されることを意味します。 この問題を解決するために、メインスクリプトに別の関数を追加します:

コールバックで実行され、ジャンプ機能に渡し、スクロールしたい要素のハッシュを過去に渡します:

<code>import easeInOutQuad from './easing'

export default class Jump {
  jump(target, options = {}) {
    this.start = window.pageYOffset

    this.options = {
      duration: options.duration,
      offset: options.offset || 0,
      callback: options.callback,
      easing: options.easing || easeInOutQuad
    }

    this.distance = typeof target === 'string'
      ? this.options.offset + document.querySelector(target).getBoundingClientRect().top
      : target

    this.duration = typeof this.options.duration === 'function'
      ? this.options.duration(this.distance)
      : this.options.duration

    requestAnimationFrame(time => this._loop(time))
  }

  _loop(time) {
    if(!this.timeStart) {
      this.timeStart = time
    }

    this.timeElapsed = time - this.timeStart
    this.next = this.options.easing(this.timeElapsed, this.start, this.distance, this.duration)

    window.scrollTo(0, this.next)

    this.timeElapsed       ? requestAnimationFrame(time => this._loop(time))
      : this._end()
  }

  _end() {
    window.scrollTo(0, this.start + this.distance)

    typeof this.options.callback === 'function' && this.options.callback()
    this.timeStart = false
  }
}
</code>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

この関数の関数は、ハッシュ値に対応するDOM要素を取得し、既にフォーカスを受信できる要素(アンカーやボタン要素など)であるかどうかをテストすることです。要素がデフォルトでフォーカス(コンテナなど)でフォーカスを受信できない場合、Tabindexプロパティを-1に設定します(プログラムでフォーカスを受信できますが、キーボードを介してではなく)。その後、焦点はその要素に設定されます。つまり、ユーザーの次のタブキーは、フォーカスを次の利用可能なリンクに移動します。 ここでは、メインスクリプトの完全なソースコードを表示できます。すべての変更について前述しました。

CSSを使用したネイティブの滑らかなスクロールをサポートします

CSSオブジェクトモデルビューモジュール仕様は、スムーズなスクロールをネイティブに実装するための新しいプロパティを導入します:

2つの値を取ることができ、scroll-behaviorはデフォルトの瞬時スクロールを表し、

はアニメーションスクロールを表します。この仕様では、その持続時間や時間関数(緩和)などのスクロールアニメーションを構成する方法は提供されません。

autoCSS-Scroll-Behaviorを使用できますか? Caniuse.comからのデータは、主要なブラウザによるCSSスクロール行動機能のサポートを示しています。 smooth

残念ながら、執筆時点では、サポートは非​​常に限られています。 Chromeでは、この機能は開発中であり、Chrome:// Flags画面で有効にすることで部分的に実装できます。 CSSプロパティはまだ実装されていないため、リンククリックのスムーズなスクロールは機能しません。

とにかく、メインスクリプトに小さな変更を加えることで、この機能がユーザーエージェントで利用可能であるかどうかを検出し、残りのコードの実行を避けることができます。ビューポートで滑らかなスクロールを使用するために、css属性をルート要素htmlに適用します(ただし、テストページでは、ボディ要素にも適用できます):

次に、スクリプトの先頭に単純な機能検出テストを追加します:

<code>var jump = (function() {

    var o = {

        jump: function(target, options) {
            this.start = window.pageYOffset

            this.options = {
              duration: options.duration,
              offset: options.offset || 0,
              callback: options.callback,
              easing: options.easing || easeInOutQuad
            }

            this.distance = typeof target === 'string'
              ? this.options.offset + document.querySelector(target).getBoundingClientRect().top
              : target

            this.duration = typeof this.options.duration === 'function'
              ? this.options.duration(this.distance)
              : this.options.duration

            requestAnimationFrame(_loop)
        },

        _loop: function(time) {
            if(!this.timeStart) {
              this.timeStart = time
            }

            this.timeElapsed = time - this.timeStart
            this.next = this.options.easing(this.timeElapsed, this.start, this.distance, this.duration)

            window.scrollTo(0, this.next)

            this.timeElapsed               ? requestAnimationFrame(_loop)
              : this._end()
        },

        _end: function() {
            window.scrollTo(0, this.start + this.distance)

            typeof this.options.callback === 'function' && this.options.callback()
            this.timeStart = false
        }

    };

    var _loop = o._loop.bind(o);

    // Robert Penner's easeInOutQuad - http://robertpenner.com/easing/
    function easeInOutQuad(t, b, c, d)  {
        t /= d / 2
        if(t         t--
        return -c / 2 * (t * (t - 2) - 1) + b
    }

    return o;

})();
</code>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
したがって、ブラウザがネイティブのスクロールをサポートしている場合、スクリプトは何もしず、終了します。そうしないと、以前と同じように実行され続け、ブラウザはサポートされていないCSSプロパティを無視します。

結論
<code>function jump(target, options) {
    var start = window.pageYOffset;

    var opt = {
      duration: options.duration,
      offset: options.offset || 0,
      callback: options.callback,
      easing: options.easing || easeInOutQuad
    };

    var distance = typeof target === 'string' ? 
        opt.offset + document.querySelector(target).getBoundingClientRect().top : 
        target
    ;

    var duration = typeof opt.duration === 'function'
          ? opt.duration(distance)
          : opt.duration
    ;

    var 
        timeStart = null,
        timeElapsed
    ;

    requestAnimationFrame(loop);

    function loop(time) {
        if (timeStart === null)
            timeStart = time;

        timeElapsed = time - timeStart;

        window.scrollTo(0, opt.easing(timeElapsed, start, distance, duration));

        if (timeElapsed         requestAnimationFrame(loop)
        else
            end();
    }

    function end() {
        window.scrollTo(0, start + distance);

        typeof opt.callback === 'function' && opt.callback();
        timeStart = null;
    }

    // ...

}
</code>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

シンプルさとパフォーマンスの実装とは別に、先ほど説明したCSSソリューションのもう1つの利点は、ブラウザのデフォルトスクロールを使用するときに経験される動作と一致することです。各ページジャンプはブラウザの履歴スタックに押し込まれ、対応するボタンでこれらのジャンプを前後に閲覧できます(ただし、少なくともFirefoxにはスムーズなスクロールはありません)。

私たちが書いたコードで(CSSサポートが利用できない場合、それをフォールバックスキームと考えることができます)、ブラウザの履歴に対するスクリプトの動作を考慮しませんでした。コンテキストとユースケースに応じて、これは興味深いものである場合とそうでない場合がありますが、スクリプトがデフォルトのスクロールエクスペリエンスを強化する必要があると考える場合、CSSのように一貫した動作を期待する必要があります。

ネイティブJavaScriptを使用した滑らかなスクロール上の

FAQ(FAQ)

ライブラリを使用せずにネイティブJavaScriptを使用してスムーズなスクロールを実現するにはどうすればよいですか?

ネイティブJavaScriptを使用して、ライブラリを使用せずにスムーズなスクロールを実現するのは非常に簡単です。 window.scrollToメソッドを使用して、behaviorオプションをsmoothに設定できます。このメソッドは、特定の回数でウィンドウ内のドキュメントをスクロールします。簡単な例を次に示します:

<code>import easeInOutQuad from './easing'

export default class Jump {
  jump(target, options = {}) {
    this.start = window.pageYOffset

    this.options = {
      duration: options.duration,
      offset: options.offset || 0,
      callback: options.callback,
      easing: options.easing || easeInOutQuad
    }

    this.distance = typeof target === 'string'
      ? this.options.offset + document.querySelector(target).getBoundingClientRect().top
      : target

    this.duration = typeof this.options.duration === 'function'
      ? this.options.duration(this.distance)
      : this.options.duration

    requestAnimationFrame(time => this._loop(time))
  }

  _loop(time) {
    if(!this.timeStart) {
      this.timeStart = time
    }

    this.timeElapsed = time - this.timeStart
    this.next = this.options.easing(this.timeElapsed, this.start, this.distance, this.duration)

    window.scrollTo(0, this.next)

    this.timeElapsed       ? requestAnimationFrame(time => this._loop(time))
      : this._end()
  }

  _end() {
    window.scrollTo(0, this.start + this.distance)

    typeof this.options.callback === 'function' && this.options.callback()
    this.timeStart = false
  }
}
</code>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
この例では、クラス

の要素をクリックすると、ページは上部にスムーズにスクロールします。 your-element

なぜ私の滑らかなスクロールがSafariで機能しないのですか?

メソッドを使用して

オプションを設定して

オプションを設定しているscrollToサファリではサポートされていません。それを機能させるには、behaviorなどのポリフィルを使用できます。これにより、ネイティブにサポートしていないブラウザのスムーズなスクロールが可能になります。 smooth smoothscroll-polyfill特定の要素にスムーズにスクロールする方法は?

特定の要素にスムーズにスクロールするには、

メソッドを使用して、

オプションをElement.scrollIntoViewに設定できます。例は次のとおりです。behavior smooth この例では、クラス

の要素をクリックすると、ページはクラス
<code>var jump = (function() {

    var o = {

        jump: function(target, options) {
            this.start = window.pageYOffset

            this.options = {
              duration: options.duration,
              offset: options.offset || 0,
              callback: options.callback,
              easing: options.easing || easeInOutQuad
            }

            this.distance = typeof target === 'string'
              ? this.options.offset + document.querySelector(target).getBoundingClientRect().top
              : target

            this.duration = typeof this.options.duration === 'function'
              ? this.options.duration(this.distance)
              : this.options.duration

            requestAnimationFrame(_loop)
        },

        _loop: function(time) {
            if(!this.timeStart) {
              this.timeStart = time
            }

            this.timeElapsed = time - this.timeStart
            this.next = this.options.easing(this.timeElapsed, this.start, this.distance, this.duration)

            window.scrollTo(0, this.next)

            this.timeElapsed               ? requestAnimationFrame(_loop)
              : this._end()
        },

        _end: function() {
            window.scrollTo(0, this.start + this.distance)

            typeof this.options.callback === 'function' && this.options.callback()
            this.timeStart = false
        }

    };

    var _loop = o._loop.bind(o);

    // Robert Penner's easeInOutQuad - http://robertpenner.com/easing/
    function easeInOutQuad(t, b, c, d)  {
        t /= d / 2
        if(t         t--
        return -c / 2 * (t * (t - 2) - 1) + b
    }

    return o;

})();
</code>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
の要素にスムーズにスクロールします。

your-element滑らかなスクロールの速度を制御できますか? target-element

ブラウザによって処理されるため、滑らかなスクロールの速度を直接制御することはできません。ただし、

を使用して、速度を含むスクロールアニメーションをより適切に制御するために、カスタムスムーズなスクロール機能を作成できます。

水平方向の滑らかなスクロールを実現するにはどうすればよいですか? window.requestAnimationFrame

垂直滑らかなスクロールと同様の方法で水平滑らかなスクロールを実現できます。また、

および

メソッドは、スクロールする水平位置を指定するための

オプションも受け入れます。例は次のとおりです。window.scrollTo Element.scrollIntoView leftこれにより、ドキュメントが右に100ピクセルをスムーズにスクロールします。

<code>function jump(target, options) {
    var start = window.pageYOffset;

    var opt = {
      duration: options.duration,
      offset: options.offset || 0,
      callback: options.callback,
      easing: options.easing || easeInOutQuad
    };

    var distance = typeof target === 'string' ? 
        opt.offset + document.querySelector(target).getBoundingClientRect().top : 
        target
    ;

    var duration = typeof opt.duration === 'function'
          ? opt.duration(distance)
          : opt.duration
    ;

    var 
        timeStart = null,
        timeElapsed
    ;

    requestAnimationFrame(loop);

    function loop(time) {
        if (timeStart === null)
            timeStart = time;

        timeElapsed = time - timeStart;

        window.scrollTo(0, opt.easing(timeElapsed, start, distance, duration));

        if (timeElapsed         requestAnimationFrame(loop)
        else
            end();
    }

    function end() {
        window.scrollTo(0, start + distance);

        typeof opt.callback === 'function' && opt.callback();
        timeStart = null;
    }

    // ...

}
</code>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
滑らかなスクロールアニメーションを停止する方法は?

ブラウザによって処理されるため、滑らかなスクロールアニメーションを直接停止することはできません。ただし、カスタムスムーズなスクロール関数を使用している場合は、

を使用してアニメーションフレームをキャンセルしてアニメーションを停止できます。

固定ヘッダーで滑らかなスクロールを実現する方法は? window.cancelAnimationFrame

固定ヘッダーを使用した滑らかなスクロールを実現するには、ヘッダーの高さを考慮に入れるために、スクロール位置を調整する必要があります。これを行うには、ターゲットスクロール位置からヘッダーの高さを差し引くことができます。

アンカーリンクのスムーズなスクロールを実現する方法は?

アンカーリンク用のスムーズなスクロールを実現するには、イベントリスナーをリンクのクリックイベントに追加し、

メソッドを使用してターゲット要素にスムーズにスクロールできます。例は次のとおりです。

これにより、ページ上のすべてのアンカーリンクがターゲット要素にスムーズにスクロールされます。 Element.scrollIntoView

キーボードナビゲーションを使用してスムーズなスクロールを実現する方法は?

キーボードナビゲーションを使用してスムーズなスクロールを実現することは、キーボードイベントの傍受や手動スクロールドキュメントが必要であるため、より複雑です。これを行うには、イベントリスナーを

イベントに追加して、keydownメソッドを使用してドキュメントをスムーズにスクロールします。 window.scrollTo

スムーズなスクロール実装の互換性をテストする方法は?

browserstackなどのオンラインツールを使用して、滑らかなスクロールの互換性をテストできます。これらのツールを使用すると、さまざまなブラウザやさまざまなデバイスでWebサイトをテストして、すべての環境で実装が適切に機能するようにします。

以上がバニラJavaScriptにスムーズなスクロールを実装する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

Python vs. JavaScript:学習曲線と使いやすさ Python vs. JavaScript:学習曲線と使いやすさ Apr 16, 2025 am 12:12 AM

Pythonは、スムーズな学習曲線と簡潔な構文を備えた初心者により適しています。 JavaScriptは、急な学習曲線と柔軟な構文を備えたフロントエンド開発に適しています。 1。Python構文は直感的で、データサイエンスやバックエンド開発に適しています。 2。JavaScriptは柔軟で、フロントエンドおよびサーバー側のプログラミングで広く使用されています。

JavaScriptとWeb:コア機能とユースケース JavaScriptとWeb:コア機能とユースケース Apr 18, 2025 am 12:19 AM

Web開発におけるJavaScriptの主な用途には、クライアントの相互作用、フォーム検証、非同期通信が含まれます。 1)DOM操作による動的なコンテンツの更新とユーザーインタラクション。 2)ユーザーエクスペリエンスを改善するためにデータを提出する前に、クライアントの検証が実行されます。 3)サーバーとのリフレッシュレス通信は、AJAXテクノロジーを通じて達成されます。

JavaScript in Action:実際の例とプロジェクト JavaScript in Action:実際の例とプロジェクト Apr 19, 2025 am 12:13 AM

現実世界でのJavaScriptのアプリケーションには、フロントエンドとバックエンドの開発が含まれます。 1)DOM操作とイベント処理を含むTODOリストアプリケーションを構築して、フロントエンドアプリケーションを表示します。 2)node.jsを介してRestfulapiを構築し、バックエンドアプリケーションをデモンストレーションします。

JavaScriptエンジンの理解:実装の詳細 JavaScriptエンジンの理解:実装の詳細 Apr 17, 2025 am 12:05 AM

JavaScriptエンジンが内部的にどのように機能するかを理解することは、開発者にとってより効率的なコードの作成とパフォーマンスのボトルネックと最適化戦略の理解に役立つためです。 1)エンジンのワークフローには、3つの段階が含まれます。解析、コンパイル、実行。 2)実行プロセス中、エンジンはインラインキャッシュや非表示クラスなどの動的最適化を実行します。 3)ベストプラクティスには、グローバル変数の避け、ループの最適化、constとletsの使用、閉鎖の過度の使用の回避が含まれます。

Python vs. JavaScript:開発環境とツール Python vs. JavaScript:開発環境とツール Apr 26, 2025 am 12:09 AM

開発環境におけるPythonとJavaScriptの両方の選択が重要です。 1)Pythonの開発環境には、Pycharm、Jupyternotebook、Anacondaが含まれます。これらは、データサイエンスと迅速なプロトタイピングに適しています。 2)JavaScriptの開発環境には、フロントエンドおよびバックエンド開発に適したnode.js、vscode、およびwebpackが含まれます。プロジェクトのニーズに応じて適切なツールを選択すると、開発効率とプロジェクトの成功率が向上する可能性があります。

JavaScript通訳者とコンパイラにおけるC/Cの役割 JavaScript通訳者とコンパイラにおけるC/Cの役割 Apr 20, 2025 am 12:01 AM

CとCは、主に通訳者とJITコンパイラを実装するために使用されるJavaScriptエンジンで重要な役割を果たします。 1)cは、JavaScriptソースコードを解析し、抽象的な構文ツリーを生成するために使用されます。 2)Cは、Bytecodeの生成と実行を担当します。 3)Cは、JITコンパイラを実装し、実行時にホットスポットコードを最適化およびコンパイルし、JavaScriptの実行効率を大幅に改善します。

Python vs. JavaScript:ユースケースとアプリケーションと比較されます Python vs. JavaScript:ユースケースとアプリケーションと比較されます Apr 21, 2025 am 12:01 AM

Pythonはデータサイエンスと自動化により適していますが、JavaScriptはフロントエンドとフルスタックの開発により適しています。 1. Pythonは、データ処理とモデリングのためにNumpyやPandasなどのライブラリを使用して、データサイエンスと機械学習でうまく機能します。 2。Pythonは、自動化とスクリプトにおいて簡潔で効率的です。 3. JavaScriptはフロントエンド開発に不可欠であり、動的なWebページと単一ページアプリケーションの構築に使用されます。 4. JavaScriptは、node.jsを通じてバックエンド開発において役割を果たし、フルスタック開発をサポートします。

Webサイトからアプリまで:JavaScriptの多様なアプリケーション Webサイトからアプリまで:JavaScriptの多様なアプリケーション Apr 22, 2025 am 12:02 AM

JavaScriptは、Webサイト、モバイルアプリケーション、デスクトップアプリケーション、サーバー側のプログラミングで広く使用されています。 1)Webサイト開発では、JavaScriptはHTMLおよびCSSと一緒にDOMを運用して、JQueryやReactなどのフレームワークをサポートします。 2)ReactNativeおよびIonicを通じて、JavaScriptはクロスプラットフォームモバイルアプリケーションを開発するために使用されます。 3)電子フレームワークにより、JavaScriptはデスクトップアプリケーションを構築できます。 4)node.jsを使用すると、JavaScriptがサーバー側で実行され、高い並行リクエストをサポートします。

See all articles