僕の使用しているテーマ「Twenty Seventeen」は画面サイズに応じて表示が変わるレスポンシブデザインです。このレスポンシブ機能を維持したまま、記事を下へスクロールしたときにサイドバーが下端で追従し、フッター手前で止まるように設定します。
これから、前半でHTMLやCSS、jQueryの解説を行い、後半は「Twenty Seventeen」でのカスタマイズの実例を紹介します。このテーマをご使用されていて実装さえできればいいという方は、次の目次から後半へジャンプしてください。コピペでとっても簡単です!
スポンサーリンク
《 目 次 》
関係するレスポンシブ機能の確認
このテーマのレスポンシブ機能として大きな境目は、画面幅768pxです。具体的には画面幅48em(768px)以上でサイドバーが表示され、768px未満でサイドバーが消え記事下に移動します。
また、サイドバーが表示されると、記事とサイドバーの幅はその親要素の幅に対する比率指定(%)になり、画面幅を変更すると記事とサイドバーそれぞれの幅も変化します。今回のカスタマイズで意識するレスポンシブ機能はこの2つになります。
HTMLの構成とレイアウト
後ほどjQueryで各要素の高さを取得しますが、高さを取得するためには要素が「id属性」である必要があります。
また、サイドバーに親子要素の「#secondary」と「#secondary-child」を設定しています。これは、子要素に「positionプロパティ」を指定した時に基準位置が親要素の左上になるようにするためです(後述)。
<div id="page"> <div id="masthead"> // ヘッダー </div> <div id="content"> <div id="wrap"> <div id="primary"> // メインエリア </div> <div id="secondary"> <div id="secondary-child"> // サイドバー </div> </div> </div> </div> <div id="colophon"> // フッター </div> </div>
サイドバーが表示された時のCSS
@media screen and (min-width: 48em) { #content{ padding: 1.5em 0 0; } #wrap{ position: relative; /*.bottom-side(absolute)の基点に必要*/ margin-left: auto; margin-right: auto; max-width: 1100px; padding-left: 2em; padding-right: 2em; } #primary { float: left; width: 67%; margin-right: 4%; /*position指定時のサイドバー幅の計算に必要*/ } #secondary { float: right; width: 29%; } .fixed-side { position: fixed;/*画面に固定*/ bottom:0; /*画面下に固定 leftとrighrの位置指定はしない*/ } .bottom-side { position: absolute; /*#wrapに絶対配置*/ bottom: 0; /*#wrap領域の下に固定 leftとrighrの位置指定はしない*/ } }
メディアクエリにより、画面幅48em(768px)以上で適用されるようにします。
上記CSSで「fixed–side」と「bottom–side」は現段階で上記HTMLに存在しません。後ほどjQueryでサイドバーの子要素「#secondary-child」にこのクラスを追加/削除することでサイドバー固定と解除の切り替えを行うことができます。
#secondary-childはjQueryにより下記のように変化します。
/*---通常時---*/ <div id="secondary-child"> /*---サイドバー追従時---*/ <div id="secondary-child" class="fixed-side"> //クラスを追加する /*---フッター表示時---*/ <div id="secondary-child" class="bottom-side"> //クラスを追加する
サイドバー追従とpositionプロパティ
positionプロパティの基準位置
サイドバーの子要素(#secondary-child)にpositionプロパティを付与したとき、横の位置指定(left or right)をしなければ基準位置は親要素(#secondary)の左上になります(上図参照)。
position「fixed」と「absolute」
サイドバーの終わりが画面に表示されたら、jQueryにより#secondary-childへ「fixed」を付与し「bottom:0;」で表示画面下に固定します。こうすることで、サイドバーが追従しているように見えるのです。
フッターが画面に表示されたら、今度は#secondary-childから「fixed」を削除し「absolute」の追加と「bottom:0;」を指定します。上位の#wrapに「relative」を指定しておくことで、#wrapの下端に配置され、フッター手前で追従が止まったように見えます。
position指定時は画面幅に対する比率に
サイドバーにpositionプロパティを指定すると、これまでの親要素(#wrap)に対する29%の幅が表示画面幅に対する29%になってしまいます。ですので、positionプロパティを指定した場合は、jQueryにより他の幅を取得しサイドバー幅を計算する必要があります。
/*---jQueryによる幅の取得---*/ //#idのpaddingもborderもmarginも含まない幅 jQuery( "#id" ).width() //#idのpadding,borderを含む幅 jQuery( "#id" ).outerWidth() //#idのpadding,border,marginを含む幅 jQuery( "#id" ).outerWidth( true )
スポンサーリンク
jQueryの解説
下記が今回使用するコードになります。jQeuryの内容について簡単に説明します。
<script> jQuery(window).on( "load scroll resize" , function(){ //画面幅の取得とサイドバー有無の境界幅を入力 var w = window.innerWidth /*スクロールバー込みの画面幅*/ var x = 768; /*サイドバー有無の境界幅*/ //サイドバーがある場合に適用 if (w >= x) { //各要素の高さを取得 var windowH = jQuery(window).height(); /*表示画面の高さ*/ var pageH = jQuery('#page').height(); /*表示ページの高さ*/ var headerH = jQuery('#masthead').outerHeight( true ); /*ヘッダーの高さ*/ var mainH = jQuery('#primary').outerHeight( true ); /*記事の高さ*/ var sideH = jQuery('#secondary-child').outerHeight( true ); /*サイドバーの高さ*/ var footerH = jQuery('#colophon').outerHeight( true ); /*フッターの高さ*/ var paddingH = jQuery('#content').outerHeight( true ) - jQuery('#content').height(); /*余白の高さ*/ //サイドバーの固定と解除条件を計算 var viewSide = headerH + paddingH + sideH ; /*ページトップからサイドバー下端までの高さ*/ var fixedSide = viewSide - windowH ; /*サイドバーを固定するスクロール高さ*/ var scrollBottom = pageH - windowH - footerH ; /*フッターが画面に表示されるスクロール高さ*/ //スクロール値を取得 var scrollTop = jQuery(this).scrollTop(); //記事がサイドバーより長い場合に適用 if( mainH > sideH ) { //サイドバー下端までスクロールしたらサイドバー下端で追従 if( scrollTop > fixedSide ) { jQuery('#secondary-child').addClass('fixed-side'); /*サイドバー固定のクラス付与*/ //サイドバー固定時のサイドバー幅を計算 var secondaryW = jQuery( "#wrap" ).width() - jQuery( "#primary" ).outerWidth( true ); jQuery( "#secondary-child" ).css('width', ''); /*サイドバー幅リセット*/ jQuery( "#secondary-child" ).css( "width" , secondaryW ); /*計算したサイドバー幅を付与*/ }else{ //条件から外れたら固定クラスを削除し幅をリセット jQuery('#secondary-child').removeClass('fixed-side'); /*サイドバー固定のクラス削除*/ jQuery( "#secondary-child" ).css('width', ''); /*サイドバー幅リセット*/ } //フッターまでスクロールしたらサイドバー下端をフッター上端に連結 if( scrollTop > scrollBottom){ jQuery('#secondary-child').removeClass('fixed-side'); jQuery('#secondary-child').addClass('bottom-side'); }else{ //条件から外れたらクラスを削除 jQuery('#secondary-child').removeClass('bottom-side'); } } } else { //サイドバーがない場合に適用 jQuery( "#secondary-child" ).css('width', ''); /*サイドバー幅リセット*/ } }); </script>
1行~50行目 jQuery実行のタイミング
<script> jQuery(window).on( "load scroll resize" , function(){ // 実行する内容 }); </script>
実行するタイミングを「ページの読み込みが完了した時」、「スクロール操作をした時」、「表示画面サイズが変更された時」としています。レスポンシブデザインなので、画面サイズを変更(resize)した時も処理を必要とします。
3行~48行目 サイドバーがある場合に実行
var w = window.innerWidth /*スクロールバー込みの画面幅*/ var x = 768; /*サイドバー有無の境界幅*/ if (w >= x) { // サイドバーがある場合の処理 } else { //サイドバーがない場合の処理 }
表示画面の幅を取得し、サイドバーがある場合に追従する処理を行っています。サイドバーの有無について、メディアクエリではスクロールバーを含んだ幅での判定になるので、判定のずれを解消するためにJavaScriptの「window.innerWidth」で画面幅を取得しています。
8~15行目 各要素の高さを取得
サイドバーを固定/解除するための判定に必要な要素の高さを取得します。要素の高さを取得するには各要素に「id」が付与されていなければなりません。取得している各コードの高さは下図のとおりです。
var windowH = jQuery(window).height(); /*表示画面の高さ*/ var pageH = jQuery('#page').height(); /*表示ページの高さ*/ var headerH = jQuery('#masthead').outerHeight( true ); /*ヘッダーの高さ*/ var mainH = jQuery('#primary').outerHeight( true ); /*記事の高さ*/ var sideH = jQuery('#secondary-child').outerHeight( true ); /*サイドバーの高さ*/ var footerH = jQuery('#colophon').outerHeight( true ); /*フッターの高さ*/ var paddingH = jQuery('#content').outerHeight( true ) - jQuery('#content').height(); /*余白の高さ*/
16行~21行目 サイドバー固定/解除の判定値
上記で取得した各要素の高さを用いて、サイドバー固定/解除の判定スクロール値を計算しています。また、実際のスクロール値も取得しますが、取得するスクロール値は、ページトップから表示画面上辺までの距離となります。
//サイドバーの固定と解除条件を計算 var viewSide = headerH + paddingH + sideH ; /*ページトップからサイドバー下端までの高さ*/ var fixedSide = viewSide - windowH ; /*サイドバーを固定するスクロール高さ*/ var scrollBottom = pageH - windowH - footerH ; /*フッターが画面に表示されるスクロール高さ*/ //スクロール値を取得 var scrollTop = jQuery(this).scrollTop();
22行~44行目 記事とサイドバー高さによる判定
if( mainH > sideH ) { // 記事がサイドバーより長い場合に処理 }
記事の高さがサイドバーの高さより長い場合にサイドバー固定/解除の処理を実行します。サイドバーより記事が短い場合は、当然ながら何も行いません。
24行~35行目 サイドバー固定と幅の計算
//サイドバー下端までスクロールしたらサイドバー下端で追従 if( scrollTop > fixedSide ) { jQuery('#secondary-child').addClass('fixed-side'); /*サイドバー固定のクラス付与*/ //サイドバー固定時のサイドバー幅を計算 var secondaryW = jQuery( "#wrap" ).width() - jQuery( "#primary" ).outerWidth( true ); jQuery( "#secondary-child" ).css('width', ''); /*サイドバー幅リセット*/ jQuery( "#secondary-child" ).css( "width" , secondaryW ); /*計算したサイドバー幅を付与*/ }else{ //条件から外れたら固定クラスを削除し幅をリセット jQuery('#secondary-child').removeClass('fixed-side'); /*サイドバー固定のクラス削除*/ jQuery( "#secondary-child" ).css('width', ''); /*サイドバー幅リセット*/ }
上記画像の左側になります。サイドバーの終わりが画面に表示されたら#secondary-childにclass「fixed-side」を追加し画面下に固定します。また、positionプロパティを指定したので、他の実測値から計算してサイドバーに幅を与えます。
36行~43行目 フッター手前でサイドバーを止める
//フッターまでスクロールしたらサイドバー下端をフッター上端に連結 if( scrollTop > scrollBottom){ jQuery('#secondary-child').removeClass('fixed-side'); jQuery('#secondary-child').addClass('bottom-side'); }else{ //条件から外れたらクラスを削除 jQuery('#secondary-child').removeClass('bottom-side'); }
上記画像の右側になります。フッターが画面に表示されたら、上記で#secondary-childに追加したclass「fixed-side」を削除しclass「bottom-side」を追加します。
スポンサーリンク
Twenty Seventeenでの実装
以下はテーマ「Twentu Seventeen」でのカスタマイズになります。このテーマを使用している場合、下記のとおり進めれば何も考える必要はありません。ただし、1つだけ分岐があるので間違わないようにしてください!
変更を加えるPHP
今回変更するPHPは、「header」、「sidebar」、「single」となりますので、まだ子テーマにコピーしていない人はコピーしましょう。
sidebar.phpへ追記
下図sidebar.phpの指定箇所にコードをコピーして貼り付けます。
<div id="secondary-child"> </div>
single.phpへ追記
下図single.phpの指定箇所を削除し、コードを貼り付けます。
<div id="wrap" class="wrap">
header.phpへ追記
ここが分岐点となります。僕の過去記事を使用して(1.アイキャッチの位置を移動している場合)と、(2.アイキャッチ位置を移動していない場合)で変更内容が少し異なりますので注意してください。
また、僕は下記コードにより記事ページでのみ追従が行われるようにしています。このコードを削除すれば記事一覧ページやアーカイブにも実装できますが、「Twenty Seventeen」の場合、サイドバー上の高さに1つ要素が追加されるため調整を必要とします。
(1.アイキャッチの位置を移動している場合)
下記コードをコピーして、header.phpのheadタグ内にある<?php wp_head(); ?>の下へ貼り付けます。
<?php if ( is_single() ) : ?> <script> jQuery(window).on( "load scroll resize" , function(){ //画面幅の取得とサイドバー有無の境界幅を入力 var w = window.innerWidth /*スクロールバー込みの画面幅*/ var x = 768; /*サイドバー有無の境界幅*/ //サイドバーがある場合に適用 if (w >= x) { //各要素の高さを取得 var windowH = jQuery(window).height(); /*表示画面の高さ*/ var pageH = jQuery('#page').height(); /*表示ページの高さ*/ var headerH = jQuery('#masthead').outerHeight( true ); /*ヘッダーの高さ*/ var mainH = jQuery('#primary').outerHeight( true ); /*記事の高さ*/ var sideH = jQuery('#secondary-child').outerHeight( true ); /*サイドバーの高さ*/ var footerH = jQuery('#colophon').outerHeight( true ); /*フッターの高さ*/ var paddingH = jQuery('#content').outerHeight( true ) - jQuery('#content').height(); /*余白の高さ*/ //サイドバーの固定と解除条件を計算 var viewSide = headerH + paddingH + sideH ; /*ページトップからサイドバー下端までの高さ*/ var fixedSide = viewSide - windowH ; /*サイドバーを固定するスクロール高さ*/ var scrollBottom = pageH - windowH - footerH ; /*フッターが画面に表示されるスクロール高さ*/ //スクロール値を取得 var scrollTop = jQuery(this).scrollTop(); //記事がサイドバーより長い場合に適用 if( mainH > sideH ) { //サイドバー下端までスクロールしたらサイドバー下端で追従 if( scrollTop > fixedSide ) { jQuery('#secondary-child').addClass('fixed-side'); /*サイドバー固定のクラス付与*/ //サイドバー固定時のサイドバー幅を計算 var secondaryW = jQuery( "#wrap" ).width() - jQuery( "#primary" ).outerWidth( true ); jQuery( "#secondary-child" ).css('width', ''); /*サイドバー幅リセット*/ jQuery( "#secondary-child" ).css( "width" , secondaryW ); /*計算したサイドバー幅を付与*/ }else{ //条件から外れたら固定クラスを削除し幅をリセット jQuery('#secondary-child').removeClass('fixed-side'); /*サイドバー固定のクラス削除*/ jQuery( "#secondary-child" ).css('width', ''); /*サイドバー幅リセット*/ } //フッターまでスクロールしたらサイドバー下端をフッター上端に連結 if( scrollTop > scrollBottom){ jQuery('#secondary-child').removeClass('fixed-side'); jQuery('#secondary-child').addClass('bottom-side'); }else{ //条件から外れたらクラスを削除 jQuery('#secondary-child').removeClass('bottom-side'); } } } else { //サイドバーがない場合に適用 jQuery( "#secondary-child" ).css('width', ''); /*サイドバー幅リセット*/ } }); </script> <?php endif; ?>
(2.アイキャッチ位置を移動していない場合)
こちらは2か所変更します。下記コードをコピーして、header.phpのheadタグ内にある<?php wp_head(); ?>の下へ貼り付けます。
<?php if ( is_single() ) : ?> <script> jQuery(window).on( "load scroll resize" , function(){ //画面幅の取得とサイドバー有無の境界幅を入力 var w = window.innerWidth /*スクロールバー込みの画面幅*/ var x = 768; /*サイドバー有無の境界幅*/ //サイドバーがある場合に適用 if (w >= x) { //各要素の高さを取得 var windowH = jQuery(window).height(); /*表示画面の高さ*/ var pageH = jQuery('#page').height(); /*表示ページの高さ*/ var headerH = jQuery('#masthead').outerHeight( true ); /*ヘッダーの高さ*/ var mainH = jQuery('#primary').outerHeight( true ); /*記事の高さ*/ var sideH = jQuery('#secondary-child').outerHeight( true ); /*サイドバーの高さ*/ var footerH = jQuery('#colophon').outerHeight( true ); /*フッターの高さ*/ var paddingH = jQuery('#content').outerHeight( true ) - jQuery('#content').height(); /*余白の高さ*/ var imageH = jQuery('#single-featured-image-header').outerHeight( true ); /*アイキャッチの高さ*/ //サイドバーの固定と解除条件を計算 var viewSide = headerH + imageH + paddingH + sideH ; /*ページトップからサイドバー下端までの高さ*/ var fixedSide = viewSide - windowH ; /*サイドバーを固定するスクロール高さ*/ var scrollBottom = pageH - windowH - footerH ; /*フッターが画面に表示されるスクロール高さ*/ //スクロール値を取得 var scrollTop = jQuery(this).scrollTop(); //記事がサイドバーより長い場合に適用 if( mainH > sideH ) { //サイドバー下端までスクロールしたらサイドバー下端で追従 if( scrollTop > fixedSide ) { jQuery('#secondary-child').addClass('fixed-side'); /*サイドバー固定のクラス付与*/ //サイドバー固定時のサイドバー幅を計算 var secondaryW = jQuery( "#wrap" ).width() - jQuery( "#primary" ).outerWidth( true ); jQuery( "#secondary-child" ).css('width', ''); /*サイドバー幅リセット*/ jQuery( "#secondary-child" ).css( "width" , secondaryW ); /*計算したサイドバー幅を付与*/ }else{ //条件から外れたら固定クラスを削除し幅をリセット jQuery('#secondary-child').removeClass('fixed-side'); /*サイドバー固定のクラス削除*/ jQuery( "#secondary-child" ).css('width', ''); /*サイドバー幅リセット*/ } //フッターまでスクロールしたらサイドバー下端をフッター上端に連結 if( scrollTop > scrollBottom){ jQuery('#secondary-child').removeClass('fixed-side'); jQuery('#secondary-child').addClass('bottom-side'); }else{ //条件から外れたらクラスを削除 jQuery('#secondary-child').removeClass('bottom-side'); } } } else { //サイドバーがない場合に適用 jQuery( "#secondary-child" ).css('width', ''); /*サイドバー幅リセット*/ } }); </script> <?php endif; ?>
アイキャッチを移動していない場合、下記コードの変更も忘れずに!
echo '<div id="single-featured-image-header" class="single-featured-image-header">';
CSSへ追記
最後に下記コードをCSSに追記しますが、6行目の6%は各自で変更する必要があります。
幅をカスタマイズしている方は、メインとサイドバーの比率と合わせて100%となる数字(メインとサイドバーの間隔)を入力してください。(ex.幅をカスタマイズしていない方は、メインが58%サイドバーが36%ですので入力値は6%になります。)
@media screen and (min-width: 48em) { .wrap { position: relative; } .has-sidebar:not(.error404) #primary { margin-right: 6%; /*各自メインとサイドバー間の比率を入力*/ } #secondary-child { width: 100%; } .fixed-side { position: fixed; bottom:0; } .bottom-side { position: absolute; bottom: 0; } }
以上でカスタマイズを終了します。
このカスタマイズによる上下スクロール時、及び表示画面サイズ変更時の動作確認を「Chrome」、「Firefox」、「IE」、「Safari」のブラウザで行っております。
サイトを作るうえで参考にさせてもらっています。
いきなり質問で恐縮ですが、上の原理をカテゴリーページや検索結果のページにも
適用できないかちょっと試してみました。
具体的には
header.php を例えば
→
と変更すれば、動作はします。(ロジックが素晴らしい!)
ただし、サイドバー内のレイアウトが崩れます。
これは何が原因だと思われますか?
よかったら、考えていただけると助かります。
axiaさん初めまして。
最近ほとんどブログをする時間がなくお力になれるかわかりませんが、
サイドバー内のレイアウトはどのように崩れてしまうのでしょうか?
あれ、コードが消えてる…。
お返事ありがとうございます。
なかなか言葉では伝わらないと思い、顛末を記事にしてみました。
https://phazor.info/AXIA/?p=433
ささっと書いたので、かなり粗いですが、アバウトには伝わるんじゃないかと。
axiaさんのおっしゃる通り、header.php内の
で記事ページにのみ追従が行われるようにしています。
さらに、axiaさんのおっしゃる通り、上記コードを
にすることでアーカイブページにも適用することが可能です。
しかし、この場合archive.phpも変更する必要があります。
具体的には僕の記事の『Twenty Seventeenでの実装』で、「single.phpへ追記」をしていますが、これと全く同じことをarchive.phpでもするのです。
現状では、アーカイブページでサイドバーの幅の計算ができないため、サイドバー幅=0になっています。
archive.php内でid=”wrap”を付与することで、サイドバーの幅を取得することができるのです。
ありがとうございます。
id を付与したら、正常に動きました!
ブログをする時間もないとのことですが、お時間ができたら是非
応用編も書いてほしいと思います。
WordPress は多機能すぎて全体が見えにくいので。