縦ぽん!!

2カラムレイアウトのwebページでスクロールに追従するサイドバーをよく見かけることがある。しかし、ブラウザ画面の高さと固定される領域の高さを考慮してないために、固定された下部が見切れて表示されないというものも少なくない。

拙作のWordPressテーマ「Garyu」ではこのような問題への対処に加え、固定したい領域内にGoogle adsenseの自動広告が含まれた場合でも問題なく表示するよう配慮したスクリプトを組み込んである。

そのまま他のサイトで転用できるような作りではないため、参考になるかどうかわからないが、動作概念とGoogle adsenseの自動広告への配慮が伝われば幸いである。

なお、このスクリプトはjQueryで画面から見切れない固定型ナビゲーションメニューを実装する方法で紹介されているスクリプトをベースに、調整させていただいたものだ。

動作概要

まず、スクロールに追従するサイドバーの動きだが、Yahoo!JAPANのニュースページを見ていただくと理解が早いと思う。右サイドに表示されているアクセスランキングなどの領域はページがスクロールされても見切れることなく表示されていることが分かる。これと同じことをjQueryで行おうというものだ。

固定表示させる領域の高さと位置について

スクロールに追従させるサイドバーはその領域の高さとブラウザ画面の高さを考慮しないと、一部が見切れて表示されなくなってしまう。サイドバー領域の高さがブラウザ画面の高さを超える場合はサイドバー領域の下部を基準に表示する位置を調整するとよい。

下の概念図を見ると画面の表示領域と固定表示させる領域の関係が分かると思う。赤枠が画面の表示領域で、青の部分が固定表示させる領域だ。ページのスクロール量に合わせて固定表示させる領域を調整している。

スクロールに追従するサイドバーの概念図

なお、概念図については今までにない快適動作!サイドバーを⬆⬇スクロールに合わせてぴたっとくっつけるスクリプト -Sticky Sidebar | コリスで紹介されていた図が非常にわかりやすかったため、参考にさせていただいた。

Google adsenseの自動広告について

Google adsenseの自動広告はページの構造を判断して適切と思える位置にバナーなどを掲載するようになっている。スクロールに追従するサイドバーがなければ特に気にする必要はないのだが、追従する領域に影響を与える位置に広告が配置されるとそれが表示されなくなることが考えられる。

そのため、広告の表示の有無によってスクリプトの動作を調整しなければならない。私が調べた限りではGoogle adsenseの自動広告領域には google-auto-placed というクラス名が与えられている。このクラス名を持つ領域の有無を判定すれば動作の調整が可能なはずだ。

WordPressテーマ「Garyu」の構造にあてはめる

まずは下の図を見ていただきたい。

「Garyu」の構造

「Garyu」の場合「サイドバー(スクロール追従)」のウィジェットエリア(#ScrollSidebarBlock)を固定表示させる領域としているが、Google adsenseの自動広告がこの領域の下に配置されることが想定されるため、赤枠の領域全体を固定表示させる領域として考える必要がある。この領域外、例えば「サイドバー」のウィジェットエリア(#SidebarBlock)と「サイドバー(スクロール追従)」の間にGoogle adsenseの自動広告が配置された場合は、固定させたい領域から押し出されるようになるため、特に考慮する必要はない。

スクロールに追従させるための基準となるのがコンテンツエリア(#ContentsBlock)で、この領域の位置と高さと固定表示させる領域の高さ、さらにブラウザ画面の高さから表示位置を決めるようにすると適切な位置に赤枠の領域を固定表示させることができる。

スクリプトについて

/* スクロールに追従するサイドバー(Google AdSense自動広告対応) */
$(window).on('load scroll resize', function() {
  setSideBar();
});

function setSideBar() {
  if (!($('#ScrollSidebarBlock').length)) return;

  var setWrap = $('#ContentsBlock');
  var wrapTop = setWrap.offset().top;
  var wrapHeight = setWrap.outerHeight();

  var sideBar2 = $('#ScrollSidebarBlock');
  var sideBar1Height = 0;
  var sideBar2Height = sideBar2.outerHeight(true);
  var sideBar2OffsetTop = 0;
  var adHeight = 0;
  var adCorrectionHeight =0; 

  if ($('#SidebarBlock').length) {
    sideBar1Height = $('#SidebarBlock').outerHeight(true);
    sideBar2OffsetTop = $('#SidebarBlock').offset().top + sideBar1Height;
  } else {
    sideBar2OffsetTop = wrapTop;
  }
  
  var googleAd = $($('#SideBar').children().last());
  if (googleAd.attr('class') == 'google-auto-placed') {
    adHeight = googleAd.outerHeight(true);
    adCorrectionHeight = parseInt(googleAd.children().css('margin-top'));
    adMarginTop = parseInt(googleAd.css('margin-top'));
    adMarginBottom = parseInt(googleAd.css('margin-bottom'));
  }
  else {
    googleAd = null;
  }

  sideBar2.css('width', sideBar2.outerWidth(true));
  if (sideBar1Height + sideBar2Height + adHeight < wrapHeight) {
    if (sideBar2OffsetTop < $(window).scrollTop()) {
      if (sideBar2Height + adHeight < $(window).height()) {
        if ($(window).scrollTop() < (wrapTop + wrapHeight - (sideBar2Height + adHeight))) {
          sideBar2.css({position: 'fixed', top: '0'});
          if (googleAd !== null) googleAd.css({position: 'fixed', top: sideBar2Height - adCorrectionHeight, width: 'initial'});
        } else {
          sideBar2.css({position: 'absolute', top: wrapHeight - (sideBar2Height + adHeight)});
          if (googleAd !== null) googleAd.css({position: 'absolute', top: wrapHeight - (adHeight + adCorrectionHeight)});
        }
      } else {
        if ($(window).scrollTop() + $(window).height() < wrapTop + wrapHeight) {
          if ($(window).scrollTop() < sideBar2OffsetTop) {
            sideBar2.css({position: 'fixed', top: '0'});
            if (googleAd !== null) googleAd.css({position: 'fixed', top: sideBar2Height, width: 'initial'});
          } else {
            if (sideBar2OffsetTop + sideBar2Height + adHeight < $(window).scrollTop() + $(window).height()) {
              sideBar2.css({position: 'absolute', top: $(window).scrollTop() - (sideBar2Height + adHeight - $(window).height()) - wrapTop});
              if (googleAd !== null) googleAd.css({position: 'absolute', top: $(window).scrollTop() - (adHeight + adCorrectionHeight - $(window).height()) - wrapTop});
            } else {
              sideBar2.css({position: 'absolute', top: sideBar2OffsetTop - wrapTop});
              if (googleAd !== null) googleAd.css({position: 'absolute', top: sideBar2OffsetTop - wrapTop + sideBar2Height - adCorrectionHeight});
            }
          }
        } else {
          sideBar2.css({position: 'absolute', top: wrapHeight - (sideBar2Height + adHeight)});
          if (googleAd !== null) googleAd.css({position: 'absolute', top: wrapHeight - (adHeight + adCorrectionHeight)});
        }
      }
    } else {
      sideBar2.css({position: 'static', top: 'auto'});
      if (googleAd !== null) googleAd.css({position: 'static', top: 'auto'});
    }
  }
}  

固定表示のためのスクリプトは関数 setSideBar() で実装し、画面読み込み時、スクロール時、画面リサイズ時に呼び出している。アコーディオン処理などによって表示位置が変わった場合でも、固定表示させる領域の位置を再調整可能なようにするためだ。

スクリプト内部はページのスクロール量に応じて、固定表示させる領域に対して動的にスタイルシートの定義を行っている。position定義を static, absolute, fixedいずれかにあてはめ、必要に応じてtopの位置を調整する、という手法だ。

Google adsenseの自動広告の領域については余白や position:fixed にした場合に width:initial を定義したりといった細かい調整を行っているが、自動広告の仕様が変わればそれにあわせて再調整が必要となるだろう。

なお、スクリプト自体の詳細な説明は省略させていただく。難解な記述ではないと思うので、条件式とそれぞれの変数の値から処理を判断していただきたい。

動作デモ

動作デモは特別に用意するまでもなく、当サイトに設置してあるサイドバーの表示を見ていただければお分かりいただけると思う。サイドバーの一部は画面がスクロールした場合でもその領域の一部が常に表示されるようになっているはずだ。さらに、Google adsenseの自動広告が表示されている場合も、それらが隠れることなく追従するようになっているはずだ。

関連する記事