[jQuery]ページ内のコンテンツに応じてヘッダーの配色変更

ヘッダーを固定かつ透過にしておいて、背景が白ければ黒文字、背景が黒ければ白文字に変える、みたいなやつ。

組む

こんな感じで組んであるとして。

<header>...</header>
<main>
    <section class="black">...</section>
    <section class="white">...</section>
    <section class="black">...</section>
    <section class="white">...</section>
    <section class="black">...</section>
    <section class="white">...</section>
    <section class="black">...</section>
    <section class="white">...</section>
    <section class="black">...</section>
    <section class="white">...</section>
</main>

.blackは背景が黒、.whiteは背景が白だとする。

.blackが表示されてる(=headerと被ってる)時はheaderをそれ用の配色に、白だったらそれ用にするって感じ。headerの配色はclassの付与で変化させて、付与の判定はjs(jQuery)で行う。

cssは割愛。なんかこう、わかるでしょ、黒とか暗い画像を幅いっぱいにしてるやつと白っぽいやつが交互になってるアレで、headerはそれに合わせて色が変わったりするやつ。あんなかんじのやつ。

$(function () {
  $(window).on('load scroll resize', function () {
    var countCheck = $('.black').length;
    arrayStart = new Array();
    arrayEnd = new Array();
    $('.black').each(function(){
      targetStart = $(this).offset().top;
      targetEnd = targetStart + $(this).innerHeight();
      arrayStart.push(targetStart);
      arrayEnd.push(targetEnd);
    })
    var positionCheck = new Array();
    var positionNow = $(window).scrollTop();
    for(var i = 0; i < countCheck; i++){
      var sliceEnd = i+ 1;
      var checkStart = arrayStart.slice(i , sliceEnd);
      var checkEnd = arrayEnd.slice(i , sliceEnd);
      if(checkStart <= positionNow && positionNow < checkEnd){
        positionCheck.push(true);
      }else{
        positionCheck.push(false);
      }
    }
    if(positionCheck.includes(true)){
      if($('header').hasClass('for_black')){
        $('header').removeClass('for_black');
      }
    }else{
      if(!$('header').hasClass('for_black')){
        $('header').addClass('for_black');
      }
    }
  })
})

.blackがheaderと被ってるときはheaderに.for_blackが付与される。

こうすりゃいいんだろ的な組み方したら動かなかったり安定しなかったりで、マジで時間掛かった。

考え方

headerにclassを付与する発火エリアとして、トリガーになる要素の位置と高さを取得。付与のタイミングは複数あるかもしれないから発火エリアはclassで指定して、.each()でページ内すべての箇所を取得、配列にストックする(arrayStart、arrayEnd)。

発火エリアの両端をそれぞれ配列に格納したので、それぞれの同じ位置の内容を抽出すればいいよね、ってことになる。ブラウザの現在の表示位置を取得して、配列をforで分解して付与判定を行う。発火エリアは複数あること前提なので、それぞれtrue/falseで判定して、それも配列にまとめる(positionCheck)。

forの回数は発火エリアの数を予め拾っておいたのでそれが基準になる(countCheck)。配列から特定位置の数値を抽出するのに難儀した。jQueryは.slice()で切り取り開始・終了位置を放り込む以外に抽出方法がないっぽい。

発火判定の配列にtrueが含まれるか否かでheaderにclassを付与するかどうかを割り振る(.includes())。

今回の肝はここで、.each()で配列化せずに直接付与できたら話は早かったんだけど、発火エリアが複数あってそのどれかが該当すれば、って条件だとこうするしかないと思った。

コード的にはうざったいけど常時判定が機能してるんで、class付与の部分は「既に付いてたら付けない」みたいなifを噛ませてある。classの付与周りは重複しないから噛ませなくてもいいけど、単純に検証を見て常にclassがビカビカしてるのが嫌。

ということで、配列ありきの組み方で構築した。「load scroll resize」でまとめたのが気に食わないんだけど、変数の移動が安定しなかったからこうするしかなかった。

まとめ的なもの

初めからあった話じゃなくて後付の取り組みだったんで結構しんどかった。3時間位掛けたと思う。

上にも書いてあるけど「.on()を分けて書きたい部分があったんだけど変数の使い回しが安定しなかったからまとめた」「配列の特定位置の値を抽出する」あとは「配列の初期化(宣言)が安定しなかった」とかで手間があった。

ぼんやりしたイメージを具体化できたんだけど、見返したらそうなるよねって感じなんだけど、実際のところ、たどり着くのは大変ですね。

追記:20220308

もっと簡単な条件が組めた。めっちゃシンプルだからリンク先のを使うのがいいと思う。だけどこれはこれで使いみちがあるかもしれないので残しておく。

とはいえ、大した期間を挟まずにバージョンアップができちゃうっていうのはどうなのか。そのときは全力で考えて組んだのに、思考を整理するだけでたった1ヶ月程度でこんなに変わるもんなのかっていう。成長といえば聞こえはいいけど、なんかこう、恥ずかしい。

コメント

タイトルとURLをコピーしました