5つのCSSスクロール天井実装方法の比較(パフォーマンス向上版)

5つのCSSスクロール天井実装方法の比較(パフォーマンス向上版)

改訂版のプレビュー

この記事は 3 日前に書かれたものです。先輩の同僚から改訂の提案をいくつかいただきましたが、その提案はまさに適切だと思います。そこで、アップグレードされた修正バージョンがあります。コードはGitHubに更新されました。

変更点は次のとおりです。

  1. getBoundingClientRect() コレクションの意味を直感的に説明するためのグラフィック説明を追加しました
  2. 頻繁なリフローのリスクを回避する方法(スクロール監視の最適化)
  3. スクロール パフォーマンスの問題の監視 (IntersectionObserver を使用した新しいソリューション)

変更・更新された内容は、ポイント4と5にあります。この記事をお読みいただいた方は、変更・更新された内容を直接お読みいただけます。または、もう一度見ることもできます。

序文

2社目に入社して最初に受けた依頼は、これまで外注していたローリングシーリングの改修でした。なぜローリングシーリングでバグが発生するのか疑問に思いました。後でコードを確認したところ、offsetTop プロパティが直接使用されており、互換性処理が行われていないことがわかりました。

オフセットトップ

現在の要素から配置された親 ( element.offsetParent ) の上端までの距離 (オフセット値) を取得するために使用されます。

親 offsetParent の配置の定義は、現在の要素に最も近い、位置 != static の親要素です。

おそらくこのコードを書いた人は、「親を配置する」という追加条件に気づかなかったのでしょう。

プロジェクトの後半では、実現する必要のあるローリング天井効果に常に遭遇しました。ここでは、私が知っている 4 つのローリング天井実現方法について詳しく説明します。

上記の4つの方法をすべてご存知ですか?関連するコードは GitHub にアップロードされています。ご興味があれば、コードをクローンしてローカルで実行することができます。ぜひ星を付けて応援してください。

4つの実装方法

まず効果図を見てみましょう。

1. position:stickyを使用して

1. スティッキーポジショニングとは何ですか?

スティッキー配置は、相対配置と固定配置の組み合わせに相当します。要素とその親要素間の距離がスティッキー配置の要件に達すると、要素の相対配置効果は固定配置効果になります。

MDNポータル

2. 使い方は?

使用条件:

  1. 親要素にはoverflow:hidden属性またはoverflow:auto属性を設定できません
  2. 上、下、左、右のいずれかの値を指定する必要があります。指定しない場合は、相対的な位置のみになります。
  3. 親要素の高さは固定要素の高さより低くすることはできません
  4. スティッキー要素は親要素内でのみ有効です。

この効果は、スクロールする必要がある要素に次のスタイルを追加することで実現できます。

.スティッキー{
    位置: -webkit-sticky;
    位置: 固定;
    上: 0;
}

3. この属性は便利ですか?

まず、このプロパティの互換性を「Can I use:」で確認してみましょう。

この API はまだ実験的な属性であるため、この属性の互換性はあまり良くないことがわかります。ただし、IOS システムにおけるこの API の互換性は比較的良好です。

そのため、本番環境でこの API を使用する場合は、通常、以下の方法と組み合わせて使用​​します。

2. JQueryのoffset().top実装を使用する

JQuery は、DOM を操作し、DOM の計算された属性を読み取るための API をカプセル化していることはご存じのとおりです。offset().top API と scrollTop() の組み合わせに基づいて、スクロール天井効果も実現できます。

...
window.addEventListener('スクロール'、self.handleScrollOne);
...
handleScrollOne: 関数() {
    自分自身 = this とします。
    scrollTop を $('html').scrollTop() とします。
    offsetTop = $('.title_box').offset().top とします。
    self.titleFixed = scrollTop > offsetTop;
}
...

これは確かに可能ですが、JQuery は徐々に廃止されつつあるため、コードでは JQuery の API を使用しないようにしています。 offset().top のソース コードに基づいて、ネイティブ offsetTop を独自に処理できます。つまり、第三の道があるのです。

scrolloTop() には互換性の問題があります。WeChat ブラウザ、IE、および Firefox の一部のバージョンでは、$('html').scrollTop() の値は 0 になるため、互換性のための 3 番目の解決策があります。

3. ネイティブ offsetTop 実装を使用する

offsetTop は相対配置の親のオフセットであることがわかっています。スクロールする必要がある要素が親要素として配置されているように見える場合、 offsetTop は要素とページの上部の間の距離を取得しません。

offsetTop に対して次の処理を自分で実行できます。

getOffset: 関数(obj,方向){
    offsetL = 0 とします。
    offsetT = 0 とします。
    while( obj!== window.document.body && obj !== null ){
        オフセットL + = obj.offsetLeft;
        オフセットT += obj.offsetTop;
        obj = obj.offsetParent;
    }
    if(方向 === '左'){
        offsetL を返します。
    }それ以外 {
        offsetT を返します。
    }
}

使用:

...
window.addEventListener('スクロール'、self.handleScrollTwo);
...
handleScrollTwo: 関数() {
    自分自身 = this とします。
    scrollTop を window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop とします。
    offsetTop を self.getOffset(self.$refs.pride_tab_fixed); とします。
    self.titleFixed = scrollTop > offsetTop;
}
...

上記の 2 つの方法に問題があると思いますか?

スクロールトップ効果を実現するには、本当に scrollTop - offsetTop 値を使用する必要がありますか?答えはノーです。

4番目のオプションを見てみましょう。

4. obj.getBoundingClientRect().topを使用して実装する

定義: この API は、ブラウザ ウィンドウに対するページ内の要素の距離を示します。

使用方法: タブの天井では、scrollTop - offsetTop の代わりに obj.getBoundingClientRect().top を使用できます。コードは次のとおりです。

//html
<div class="pride_tab_fixed" ref="pride_tab_fixed">
    <div class="pride_tab" :class="titleFixed == true ? 'isFixed' :''">
        //コード
    </div>
</div> 

// ビュー
エクスポートデフォルト{
    データ(){
      戻る {
        タイトル修正: false
      }
    },
    アクティブ化(){
      this.titleFixed = false;
      window.addEventListener('スクロール'、this.handleScroll);
    },
    メソッド: {
      //スクロール監視、ヘッド固定 handleScroll: function () {
        offsetTop を this.$refs.pride_tab_fixed.getBoundingClientRect().top とします。
        this.titleFixed = offsetTop < 0;
        //コード
      }
    }
  }

offsetTop と getBoundingClientRect() の違い

1. getBoundingClientRect():

ブラウザ ウィンドウを基準としたページ上の要素の左、上、右、下の位置を取得するために使用されます。書類の巻き上げ部分は含まれません。

この関数は、8つのプロパティ(top、right、buttom、left、width、height、x、y)を持つオブジェクトを返します。

2. オフセットトップ:

現在の要素から配置された親 ( element.offsetParent ) の上端までの距離 (オフセット値) を取得するために使用されます。

親 offsetParent の配置の定義は、現在の要素に最も近い、位置 != static の親要素です。

offsetTop メソッドと offsetParent メソッドを組み合わせて、要素から本体の上余白までの距離を取得できます。コードは次のとおりです。

getOffset: 関数(obj,方向){
    offsetL = 0 とします。
    offsetT = 0 とします。
    while( obj!== window.document.body && obj !== null ){
        オフセットL + = obj.offsetLeft;
        オフセットT += obj.offsetTop;
        obj = obj.offsetParent;
    }
    if(方向 === '左'){
        offsetL を返します。
    }それ以外 {
        offsetT を返します。
    }
}

拡張知識ポイント

オフセット幅:

要素が水平方向に占めるスペースの量:

offsetWidth = border-left + padding-left + width + padding-right + border-right

オフセット高さ:

要素が垂直方向に占めるスペースの量:

offsetHeight = border-top + padding-top + height + padding-bottom + border-bottom

注: 垂直スクロール バーがある場合、 offsetWidth には垂直スクロール バーの幅も含まれます。水平スクロール バーがある場合、 offsetHeight には水平スクロール バーの高さも含まれます。

オフセットトップ:

要素の上外側境界線と offsetParent 要素の上内側境界線間のピクセル距離。

オフセット左:

要素の左外側境界から offsetParent 要素の左内側境界までのピクセル距離。

予防

  1. すべてのオフセット プロパティは読み取り専用です。
  2. 要素に display:none が設定されている場合、その offset 属性は 0 になります。
  3. オフセット プロパティにアクセスするたびに再計算 (変数の保存) が必要になります。
  4. 使用時にDOMが初期化されずに属性が読み取られる可能性があり、その場合は0が返されます。この問題については、DOM要素が初期化されるまで待ってから実行する必要があります。

2つの問題に遭遇

1.天井の吸引の瞬間は揺れを伴う

ジッターが発生する理由は、天井要素の位置が固定されると、その要素がドキュメント フローから外れ、次の要素がその場所を埋めてしまうためです。このパディング操作がジッターの原因となります。

解決

この天井要素に同じ高さの親要素を追加します。天井効果を実現するには、この親要素の getBoundingClientRect().top 値を監視します。つまり、次のようになります。

<div class="title_box" ref="pride_tab_fixed">
    <div class="title" :class="titleFixed == true ? 'isFixed' :''">
    `obj.getBoundingClientRect().top` を使用して</div>
</div>

このソリューションはジッターバグを解決できます。

2. 天井効果は時間内に反応できない

この問題は私にとって非常に頭の痛い問題であり、これまで気にしたことがありませんでした。ある日、Meituan でテイクアウトを注文したとき、初めてこの問題に注目し始めました。

説明する:

  1. ページが下にスクロールすると、ページのスクロールが停止するまで天井要素は表示されません。
  2. ページを上にスクロールすると、ドキュメント フローの位置が復元されても天井要素は元の状態に戻らず、ページのスクロールが停止した後にのみ元の状態に戻ります。

理由:

iOS では、スクロール イベントをリアルタイムで監視することはできません。関連するイベントは、スクロールが停止したときにのみトリガーされます。

解決:

最初のソリューションの position:sticky を覚えていますか?この属性は IOS6 以上のシステムと互換性が良好なので、IOS デバイスと Android デバイスを区別して 2 つの異なる処理を実行できます。

IOS は position:sticky を使用し、Android はスクロール モニタリングを使用して getBoundingClientRect().top の値を監視します。

IOSのバージョンが低すぎる場合はどうなりますか?ここにアイデアがあります: window.requestAnimationFrame()。

パフォーマンスの最適化(新機能)

これで4つのローリング天井工法の紹介は終わりですが、これで本当に終わりでしょうか?実際のところ、最適化の余地はまだ残っています。
パフォーマンスは 2 つの方向 (実際には 1 つの方向) から最適化されます。

  1. 過度なリフローを避ける
  2. スクロール監視イベントを最適化する

問題箇所特定プロセス

過度のリフローはページのパフォーマンスを低下させることがわかっています。したがって、ユーザーによりスムーズなエクスペリエンスを提供するために、リフローの数をできるだけ減らす必要があります。

友人の中には、「それは知っているけど、これがローリング天井とどう関係があるの?」と言う人もいるかもしれません。

心配しないでください。スクロール トップでは、応答オフセットを取得するために offsetTop または getBoundingClientRect().top が使用されることを覚えていますか?

要素の属性が読み取られるため、ページは自然にリフローされます。

したがって、最適化の方向性としては、要素属性の読み取り回数を減らすことが挙げられます。コードを見ると、画面スクロール イベントがトリガーされると、関連するメソッドが呼び出され、要素のオフセットが読み取られることがわかります。

最適化計画

この問題には 2 つの解決策があります。

  1. パフォーマンスを満たすためにスムーズさを犠牲にし、スロットリングを使用して関連メソッドの呼び出しを制御する
  2. IntersectionObserver をスロットルと組み合わせて使用​​すると、スムーズさも犠牲になります。

最初の選択肢

この解決策は非常に一般的ですが、それがもたらす副作用も明らかです。つまり、天井効果がいくらか遅れることになります。製品が許容できる場合、これは良い方法です。

これにより、一定期間内の読み取りのみを制御できます

ここでのスロットル機能は、lodash.js によってカプセル化された throttle メソッドそのものです。

コードは次のとおりです。

window.addEventListener('スクロール', _.throttle(self.handleScrollThree, 50));

2番目のオプション

2 番目の解決策は比較的受け入れやすいです。IntersectionObserver がサポートされている場合は IntersectionObserver を使用し、そうでない場合は throttle を使用します。

まずはIntersectionObserverについてお話しましょう

IntersectionObserver を使用すると、頻繁に計算を行って判断することなく、要素がデバイスの可視領域に入ったかどうかを監視できます。

この属性を使用すると、要素が可視範囲にない場合に要素の相対位置を読み取る必要がなくなり、パフォーマンスの最適化が実現します。ブラウザがこの属性をサポートしていない場合は、throttle を使用して処理します。

このプロパティの互換性を見てみましょう:

60% 以上をサポートしており、プロジェクトでも使用できます (互換性を十分に考慮する必要があります)。

IntersectionObserver の使用方法の詳細については、MDN または Ruan Yifeng のチュートリアルを参照してください。

IntersectionObserver とスロットル最適化を使用するコードは次のとおりです。

IntersectionObserverFun: 関数() {
    自分自身 = this とします。
    ele = self.$refs.pride_tab_fixed とします。
    if( !IntersectionObserver ){
        オブザーバー = new IntersectionObserver(function(){
            offsetTop を ele.getBoundingClientRect().top とします。
            self.titleFixed = offsetTop < 0;
        }, {
            閾値: [1]
        });
        オブザーバー.観察(document.getElementsByClassName('title_box')[0]);
    } それ以外 {
        window.addEventListener('スクロール', _.throttle(function(){
            offsetTop を ele.getBoundingClientRect().top とします。
            self.titleFixed = offsetTop < 0;
        }, 50));
    }
}, 

知らせ

IntersectionObserver API は非同期であり、ターゲット要素のスクロールと同期してトリガーされません。

仕様では、IntersectionObserver 実装では requestIdleCallback() を使用する必要があると規定されています。コールバックはすぐに実行されず、window.requestIdleCallback() を呼び出して指定したコールバック関数を非同期的に実行し、最大遅延時間は 100 ミリ秒と規定されます。

要約:

IntersectionObserver と throttle を組み合わせたこのソリューションは、代替ソリューションです。このソリューションの利点は、ページ リフローのリスクを効果的に軽減できることですが、欠点もあり、ページの滑らかさを犠牲にする必要があります。具体的な選択はビジネスニーズによって異なります。

以上がこの記事の全内容です。皆様の勉強のお役に立てれば幸いです。また、123WORDPRESS.COM を応援していただければ幸いです。

<<:  IframeとFRAMEの違いの分析

>>:  MySQLはSQL文を使用してテーブル名を変更します

推薦する

MySQL ダーティ ページ フラッシュとテーブル スペースの縮小の原理の分析

mysql ダーティページWAL メカニズムにより、InnoDB はステートメントを更新するときに、...

HTMLは正規表現を使用してテーブルの例をテストします

以下は、HTML で正規表現を使用してテーブルをチェックするサンプル コードです。具体的なコードの内...

UTF-8 および GB2312 ウェブエンコーディング

最近、多くの学生から Web ページのエンコーディングについて質問を受けています。gb2312 と ...

deepin apt コマンドを使用して最新バージョンの docker をインストールする方法

ステップ1: Ubuntuソースを追加するルートに切り替える suルートソフトウェアソースファイルの...

Vueはドラッグ可能なツリー構造図を実装します

目次Vue 再帰コンポーネントドラッグイベント最近、Vue を使用して、ドラッグ可能なツリー構造図と...

html+css3で実装されたログインインターフェース

成果を達成するまずHTMLを使って基本的なフレームワークを構築します <本文> <...

MySQL SELECT実行順序の簡単な理解

SELECT ステートメントの完全な構文は次のとおりです。 (7)選択 (8) DISTINCT ...

HTML 再利用テクニック

HTML の再利用は、あまり話題に上らない言葉です。今日は、この問題を次のようにまとめたいと思います...

nginx における proxy_pass のさまざまな使用法の詳細な説明

目次プロキシ転送ルール最初のもの: 2番目のタイプ: 3番目のタイプ: 4番目のタイプ: 5番目:プ...

MySQL 上級学習ノート (パート 3): MySQL 論理アーキテクチャの紹介、MySQL ストレージ エンジンの詳細な説明

MySQL 論理アーキテクチャの概要他のデータベースと比較すると、MySQL は、そのアーキテクチャ...

HTML は Double 11 クーポン取得を実装します (クーポン取得ページを開く時間を設定します)

さっそく、コードを直接投稿します。具体的なコードは次のとおりです。 <!DOCTYPE htm...

ウェブページ作成時のHTMLタグの使用に注意してください

HTML はプレゼンテーションからコンテンツへの移行を試みており、コンテンツの意味(HTML) とプ...

WeChatアプレット開発の章:落とし穴の記録

最近、会社初のミニプログラムの開発に参加しました。開発経験は基本的にWebViewをベースとしたハイ...

JavaScript 正規表現の説明

目次1. 正規表現の作成2. 使用モード2.1 シンプルモードの使用2.2 特殊文字の使用3. 応用...

Dockerイメージの作成とプロジェクト全体のワンクリックパッケージングとデプロイ

一般的な Dockerfile 命令の紹介命令説明するから新しいイメージが構築される基となるイメージ...