Wordpressで高機能な追従目次を作成する方法を、備忘録を兼ねてシェアします。
スクロールにあわせてサイドバー内で目次が追従するほか、H2~H4タグをアコーディオンで入れ子にして、なおかつスクロールにあわせてアコーディオンが自動開閉します。
なお、以下で紹介するコードは、Creator Clipさんで公開されているものをベースに一部改変したものとなります。こちらのオリジナルのコードにアコーディオンの自動開閉機能を加えたのが本記事の特長となります。
DEMO
サンプルコード
ではまず、サンプルコードから。
JavaScript(jQuery)
以下のコードをJavaScriptファイルとして保存(メモ帳にコピペして拡張子.jsで保存)して、使用テーマ内のJavaScriptフォルダにアップしてください(無ければ、テーマ直下に「js」フォルダを作ってください)。
- $(function() {
- /* -------------------------------------------------------
- 記事の見出しから目次作成
- --------------------------------------------------------*/
- function makeMokuji() {
- var idcount = 1;
- var mokuji = '';
- var currentlevel = 0
- var sectionTopArr = new Array();
- // 見出しを回してリストに格納
- $('article h2, article h3, article h4').each(function(i){ //★★注
- // IDを保存
- this.id = 'chapter-' + idcount;
- idcount ++;
- // 見出しの入れ子
- var level = 0;
- if(this.nodeName.toLowerCase() == 'h2') {
- level = 1;
- } else if(this.nodeName.toLowerCase() == 'h3') {
- level = 2;
- } else if(this.nodeName.toLowerCase() == 'h4') {
- level = 3;
- }
- while(currentlevel < level) {
- mokuji += '<ol class="chapter">';
- currentlevel ++;
- }
- while(currentlevel > level) {
- mokuji += '</ol>';
- currentlevel --;
- }
- // リストを生成
- mokuji += '<li><a href="#' + this.id + '">' + $(this).html() + '</a></li>\n';
- });
- while(currentlevel > 0) {
- mokuji += '</ol>';
- currentlevel --;
- }
- // HTML出力
- strMokuji = '<h4><span class="mokuji-color">目次</span>で流し読み <span class="kao">・*・:≡( ε:)</span> <span class="closeBtn"><i class="fa fa-times-circle-o"></i></span></h4>\
- <div class="mokujiInner">'
- + mokuji +
- '<!-- /.mokujiInner --></div>';
- $('.mokuji').html(strMokuji);
- /* -------------------------------------------------------
- リストクリックでスムーズスクロール
- --------------------------------------------------------*/
- $('.mokuji li').not('.accordion, .accBtn').click(function(){
- var speed = 800;
- var href = $(this).find('a').attr('href');
- var target = $(href == '#' || href == '' ? 'html' : href);
- var position = target.offset().top;
- $('html, body').stop().animate({scrollTop:position}, speed, 'easeInOutCirc');
- return false;
- });
- /* -------------------------------------------------------
- 目次のアコーディオン
- --------------------------------------------------------*/
- $('.mokuji ol').prev().addClass('accordion').append('<span class="accBtn"><i class="fa fa-plus-square-o"></i></span>');
- $('.mokuji li.accordion').wrapInner('<div class="inner clearfix"></div>');
- // 開閉ボタンを押した時
- $('.accBtn').click(function(){
- // 開閉処理
- $(this).parents('li').next().stop().slideToggle(300, 'easeInOutCirc');
- // 閉じるボタンアイコン切替
- $('.closeBtn').removeClass('active').addClass('active');
- // アイコン切替
- if( $(this).find('i').hasClass('fa-plus-square-o') ){
- $(this).find('i').removeClass('fa-plus-square-o').addClass('fa-minus-square-o');
- } else {
- $(this).find('i').removeClass('fa-minus-square-o').addClass('fa-plus-square-o');
- }
- return false;
- });
- // 閉じるボタンの表示切替
- var closeBtnFlag = '';
- $('.mokuji li').each(function() {
- if( $(this).hasClass('accordion') ) {
- closeBtnFlag = false;
- }
- });
- if( closeBtnFlag == true ) {
- $('.closeBtn').hide();
- }
- // 全て閉じるボタンを押した時
- $('.closeBtn').click(function(){
- // 閉じるアイコン切替
- $(this).toggleClass('active');
- // classの有無を確認
- if( $(this).hasClass('active') ){
- $('.mokuji ol ol').stop().slideDown(300, 'easeInOutCirc');
- $('.accBtn').find('i').removeClass('fa-plus-square-o').addClass('fa-minus-square-o');
- } else {
- $('.mokuji ol ol').stop().slideUp(300, 'easeInOutCirc');
- $('.accBtn').find('i').removeClass('fa-minus-square-o').addClass('fa-plus-square-o');
- }
- });
- /* -------------------------------------------------------
- カレント表示切替
- --------------------------------------------------------*/
- var secTopArr = new Array();
- secTopArr.length = 0;
- var current = -1;
- // 現在位置の取得
- $(window).on("load",function() {
- $('article [id^="chapter"]').each(function(i){
- secTopArr[i] = $(this).offset().top;
- });
- });
- //スクロールイベント
- $(window).on('load scroll',function(){
- for (var i = secTopArr.length-1; i>=0; i--) {
- if ($(window).scrollTop() > secTopArr[i] - 300) {
- $('.mokuji li').removeClass('current').eq(i).addClass('current');
- $('.mokuji ol ol li.current').parent('ol').prev().addClass('current');
- $('.mokuji ol ol li.current').parent().prev('li').addClass('current');
- //★★スクロールダウンするとアコーディオンを開く
- $.when(
- $('.mokuji li.current').next('ol').slideDown(300, 'easeInOutCirc')
- ).done(
- $('.current .accBtn').find('i').removeClass('fa-plus-square-o').addClass('fa-minus-square-o'),
- $('.closeBtn').addClass('active')
- )
- // ★★★スクロールダウンで通過したアコーディオンを閉じる
- $.when(
- $('.mokuji li.current').prev('ol').slideUp(300, 'easeInOutCirc')
- ).done(
- $('.accBtn').not('.current .accBtn').find('i').removeClass('fa-minus-square-o').addClass('fa-plus-square-o')
- )
- // ★★★スクロールアップで通過したアコーディオンを閉じる
- $('.mokuji li.current').next().next().next('ol').slideUp(300, 'easeInOutCirc');
- $('.mokuji li.current').next().next('ol').slideUp(300, 'easeInOutCirc');
- break;
- }
- }
- });
- }
- makeMokuji();
- /* -------------------------------------------------------
- 目次固定
- --------------------------------------------------------*/
- function fixedSide() {
- // ウィンドウ幅・人気記事を取得
- var w = window.innerWidth;
- var mainH = $('#main').height(); //★★注
- var sideH = $('#side').height(); //★★注
- var fixedElm = '';
- if(mainH > sideH) { // サイドバーより長ければ
- fixedElm = $('.mokuji');
- // 要素の位置を取得
- var fixedSideTop = fixedElm.offset().top;
- var footerTop = $('footer').offset().top;
- var scrollBottom = $('body').height() - $(window).height() - $('footer').outerHeight();
- $(window).scroll(function(){
- // スクロール位置を取得
- y = $(window).scrollTop();
- // スクロールがサイドバーを上回ったら
- if(y > fixedSideTop){
- fixedElm.addClass('fixed-side');
- } else {
- fixedElm.removeClass('fixed-side');
- }
- });
- }
- }
- fixedSide();
- });
131行目から151行目のあたりが、アコーディオンを自動開閉させるために、わたしが加筆したコードです。
スクロールにあわせてアコーディオンを自動開閉することはそれほど難しくないのですが、アコーディオンの各階層に開閉状態をあらわすアイコンがあり、それらを連動させるのに苦労しました。
もしうまく動かない場合は、★★注部分の要素名やクラス名を確認してください。使用しているWordPressごとに異なる場合があります。
《2021.10.6追記》
WordPress5.5から画像の遅延読み込みがデフォルトになった影響で、目次の現在位置表示がズレる事象が発生してしまうため、119行目あたりに「すべて読みこんだあとで位置関係を取得する」コードを追加しました。
HTML
bodyタグの末尾に以下のコードを記述してください。
JavaScriptファイルまでのパスは、実際のサイト構造に応じて記述してください。
- <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.10.4/jquery-ui.min.js"></script>
- <script src="wp-content/themes/テーマ名/js/XXXX.js"></script>
手短ながら以上となります。CSSは使用するサイトにより微調整が必要ですので、Creator Clipさんで公開されているコードを参考にされてください。