ブログ

【ブログ改善】ヘッダの固定と目次・プログレスバーを表示する

当ページのリンクには広告が含まれている場合があります。

ジミログブログ改善

ブログのUIを改善したくて、

  • スマホ閲覧時のヘッダ固定
  • プログレスバーの表示
  • スマホ用目次の表示

を実施した話。

参考にしたのは、ガジェットブロガー&YouTuberとして有名なトーマスガジェマガさんのこちらの記事。

ジミー
ジミー

ジミーです。
X(旧ツイッター)もやってます。


サイト改善の背景

デバイス別の割合
ジミログ訪問者の8割がスマホ

基本的にブログをいじるときはPCからなんだけど、ジミログの訪問者の約8割がスマホ。
スマホ閲覧時に快適になるように改善していかなければ記事も読まれないしリピーターも増えない。

ということで、スマホ用になんとかブログを改善する方法を考えてはいたんだけどついに実行することにした。

今回はその内容と方法を紹介する。
コードの内容はchatGPTとClaude Sonnetに作ってもらったものだから、もし導入する際は必ずバックアップを取った上で完全に自己責任でやってほしい。
適用した結果、多少は見やすくなったと感じてる。

スマホ閲覧時のヘッダの固定

とりあえずトップページに行ってもらいたい

ジミログ
トップページには色んな情報を出してる

Google検索でジミログにたどり着いても目的を達成したらジミログから去るケースがほとんどだと思うけど、こちらからしたらできるだけ長くジミログに居てほしい。

悪あがき的な考えではあるが、訪問者の目を引く記事がトップページにあればなんとか留めることはできる。
その可能性を1%でも上げるために、記事中には常にヘッダーを出すことにした。

ヘッダを常に表示する

作り方はこれから説明するけど、適用した結果がコレ。

ジミログ
ヘッダを常に出す

左がページを開いた直後で、スクロールするとヘッダは表示されなくなっていた。
適用したあとは記事の中のどこでもヘッダーが表示されるようになっていて(画像右側)、読み終わったあとにタップでトップページに行ってもらう可能性が少なからず上がった。

ヘッダを固定する方法

ヘッダを固定する方法を解説する。
chatGPTに教えてもらったから、どんな仕組みか知りたければコードをコピってchtGPTに解析してもらってほしい。

事前準備(固定するヘッダーの要素名を確認)

ウェブサイトは複数の要素から成り立っているんだけど、ヘッダもその要素の一つ。
これから書くコード(プログラム)ではヘッダを固定するんだけど、そのためにはそのヘッダの要素名を指定する必要がある。

ヘッダの要素名はPCブラウザ(ここではChromeを使って説明する)から確認できる。

ジミログ
デベロッパーツールを使う

Chromeブラウザで自分のブログを開いて、ブラウザの右上の方にある「⋮」マークをクリックするとメニューが出てくるので、「その他のツール」→「デベロッパーツール」を選択する。

画面が二分割されて、右側にプログラムのコードがずらっと並んでいるからここから要素名を探す。
右側の画面が「Elements」となっていることを確認して探していく。

ジミログ
全体がハイライトされる行を選択するとこうなる

書かれているコードを上から順にマウスカーソルを乗せていくと、該当の要素がハイライト表示される。
オレのサイトはページ全体がハイライトされる「<body ~~~」という行があるので、▼を押して子要素を展開しスコープを徐々に狭めていきながら要素名を特定していく。
ここはサイトの作りやテーマによって変わるので各自確認してほしい。

ジミログ
固定する要素を確認する

固定したい要素がハイライトされたら、そこのコードの「id=」となっている文字列を確認する。
ジミログではヘッダの要素名は「id="header-full"」だったので、これを用いてヘッダを固定するコードをCSSに記載していく。

CSS、function.phpの修正

step
1
CSSの追加

以下のコードをCSSに追加する。
コードの中にチラホラ出現する

#header-full

となっている箇所は、各自の要素名に合わせて修正してほしい。

/*-----------------------------------
ヘッダを常に表示させたい
------------------------------------*/
@media screen and (max-width: 960px) {
    #header-full {
        position: fixed !important;
        top: 0;
        left: 0;
        width: 100%;
        z-index: 9999;
        background-color: #fff; /* 背景色 */
        box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1); /* 影をつける */
    }
}
/* 横画面ではならないようにする */
@media screen and (max-width: 960px) and (orientation: landscape) {
    #header-full {
        position: relative !important;
    }

    body {
        padding-top: 0 !important;
    }
}
/* トップページではやらない */
@media screen and (max-width: 960px) {
    body.home #header-full {
        position: relative !important;
    }

    body.home {
        padding-top: 0 !important;
    }
}

このコードを「外観」→「テーマファイルエディター」を開いて、子テーマの「Stylesheet(style.css)」に追加してファイルを更新する。
カスタマイザーの追加CSSからでは上手くいかなったので、テーマファイルエディターからの追加をオススメする。

step
2
function.phpの修正

次に、テーマファイルエディターの「functinon.php」にコードを追加する。
以下のコードをコピーして、「外観」→「テーマファイルエディター」にある子テーマの「Theme Functions (functions.php)」に追加する。

// スマホでヘッダーを常に表示する
function add_custom_js() {
    ?>
    <script>
    document.addEventListener("DOMContentLoaded", function() {
        function adjustPadding() {
            var header = document.getElementById("header-full");
            var isTopPage = window.location.pathname === "/" || window.location.pathname === "/index.php"; // トップページ判定
            var isPortrait = window.innerWidth <= 960 && window.innerWidth < window.innerHeight; // 縦画面

            if (header && isPortrait && !isTopPage) { 
                // 記事ページのみヘッダー固定
                var headerHeight = header.offsetHeight;
                document.body.style.paddingTop = headerHeight + "px";
                header.style.position = "fixed";
            } else {
                // トップページまたは横画面のときは固定解除
                document.body.style.paddingTop = "0px";
                if (header) {
                    header.style.position = "relative"; // 元に戻す
                }
            }
        }

        adjustPadding(); // 初回実行
        window.addEventListener("resize", adjustPadding); // 画面サイズ変更時にも再計算
    });
    </script>
    <?php
}
add_action('wp_footer', 'add_custom_js');

これでファイルを更新すれば、記事ページではヘッダが固定表示されるはず。

プログレスバーの表示

記事がいつ終わるのかわかりやすくしたい

ジミログ
いつ終わるのかわからないと離脱しやすい

読者的な目線で見た場合、いつ終わるかわからない記事は読みづらい。
だとしたら自分がいま記事のどの部分を読んでいるのかを視覚的にわかるようにさせておきたい。

画面下部にプログレスバーを表示する方法

ジミログ
プログレスバーの表示

実装した結果がコレ。

プログレスバーの設定は完全にコチラをパクらせていただいた。

step
1
WP Reading Progressの導入

プログレスバーの実装は「WP Reading Progress」プラグインを入れて設定するだけ。
リンク先からダウンロードして、新規プラグインとしてインストールしてほしい。

step
2
WP Reading Progressの設定

インストールしたプラグインの設定をする。
以下を設定して「Save Settings」をクリックする。

項目設定値
Stick the bar to this elementbottom
How to stickチェックなし
Color of the progress barda1725
Progress bar thickness.7vh
Aria label未設定
Make bar start at 0%チェック
On single post pageチェックなし
Show reading progress on「post」と「page」にチェック
And on their archivesチェックなし
No cssチェックなし
Activate ertチェックなし

step
3
アイコンの実装(オプション)

プログレスバーの実装はステップ2まででOK。
バーの先端にアイコンを表示させたい場合はまず以下をStylesheet(style.css)に追加して実装する。

アイコン画像の指定

コード内にある以下のURLを、好きな画像のURLにすればOK。
メディアライブラリにアイコンをアップロードして、画像のURLをコピーすればいい。

background-image: url('https://xxxxxxxxxxxxxxx'); /* アイコン画像のURL */

/*-----------------------------------
プログレスバーの改造
------------------------------------*/

#ruigehond006_wrap {
    max-width: 98%;
}

#ruigehond006_bar {
    position: relative;
    overflow: visible;
    max-width: 98%;
}

#ruigehond006_bar::after {
    content: "";
    position: absolute;
    top: 50%;
    right: -14px;
    transform: translate(50%, -50%); /* 中央揃え */
    width: 50px; /* アイコンの幅 */
    height: 50px; /* アイコンの高さ */
    background-image: url('https://xxxxxxxxxxxxxxx'); /* アイコン画像のURL */
    background-size: contain; /* アイコンを枠に合わせる */
    background-repeat: no-repeat;
    z-index: 10; /* アイコンが他の要素より前に表示されるように */
}

/*-----------------------------------
シークバーのスマホ表示の横幅調整
------------------------------------*/
@media only screen and (max-width: 600px) {
    #ruigehond006_wrap {
        max-width: 96%;
    }

    #ruigehond006_bar::after {
        width: 33px; /* アイコンの幅 */
        height: 33px; /* アイコンの高さ */
    }
}

ファイルを更新すると、プログレスバーの先端にアイコンが表示されるはず。

スマホ閲覧時の目次の表示

スマホだと目次が使いづらい

ジミログ
PCでは常に右側に目次が表示される

PCでジミログを閲覧したとき、記事を読み進めるとそれに追従するように右側に目次が表示される。
それと同じようなことをスマホ閲覧時にもできるようにしたかった。

目次のプラグインのRich Table of Contentsを導入しているんだけど、このプラグインの機能はスマホでは使えない。
使えないというか、目次アイコンは出るんだけどそれをタップすると記事上部の目次に戻されるだけなんだ。

そうではなく、記事を読んでいる状態で目次を出したかったのでClaudeを使って実装した。

スマホ閲覧時に目次を表示する

ジミログ
スマホで目次を出す

実装した結果がコレ。

具体的には右下に目次アイコンを出しておいて、タップすると目次を出すようにした。
今読んでる箇所のハイライトもできるようになっている。

スマホ閲覧時に目次を表示する方法

今までと同じ流れでfunction.phpに以下のコードを追記する。
追記したらファイルを更新でOK。

// フッターに目次機能を追加
function add_floating_toc() {
    if (is_single() || is_page()) { 
    // 投稿ページまたは固定ページで表示
        echo '
        <!-- 目次ボタン(モバイル用) -->
        <div class="floating-toc-button">
            <div class="toc-button-content">
                <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                    <line x1="8" y1="6" x2="21" y2="6"></line>
                    <line x1="8" y1="12" x2="21" y2="12"></line>
                    <line x1="8" y1="18" x2="21" y2="18"></line>
                    <line x1="3" y1="6" x2="3.01" y2="6"></line>
                    <line x1="3" y1="12" x2="3.01" y2="12"></line>
                    <line x1="3" y1="18" x2="3.01" y2="18"></line>
                </svg>
                <span class="toc-button-text">目次</span>
            </div>
        </div>

        <!-- フローティング目次(モバイル用) -->
        <div class="floating-toc">
            <div class="floating-toc-content">
                <h4>目次(タップでジャンプ)</h4>
                <div id="floating-toc-container"></div>
            </div>
        </div>

        <script>
        document.addEventListener("DOMContentLoaded", function() {
            // 目次を自動生成する関数
            function generateTableOfContents() {
                // 記事内のh2, h3タグを取得(必要に応じて変更)
                const contentContainer = document.querySelector(".entry-content, .post-content, article, .article, .post");
                if (!contentContainer) return;
                
                const headings = contentContainer.querySelectorAll("h2, h3");
                
                if (headings.length === 0) return;
                
                // 目次を構築
                const floatingTocContainer = document.getElementById("floating-toc-container");
                if (!floatingTocContainer) return;
                
                const tocList = document.createElement("ul");
                tocList.className = "toc-list";
                
                let currentH2Item = null;
                let currentH2List = null;
                
                // スクロール監視用のヘッディング位置情報配列
                const headingPositions = [];
                
                // 各見出しを処理
                headings.forEach((heading, index) => {
                    // 見出しにIDがなければ付与
                    if (!heading.id) {
                        heading.id = "heading-" + index;
                    }
                    
                    const listItem = document.createElement("li");
                    const link = document.createElement("a");
                    link.href = "#" + heading.id;
                    link.textContent = heading.textContent;
                    link.setAttribute("data-target", heading.id);
                    listItem.appendChild(link);
                    
                    // スクロール監視用に位置情報を保存
                    headingPositions.push({
                        id: heading.id,
                        element: heading,
                        link: link
                    });
                    
                    // h2とh3で階層構造を作る
                    if (heading.tagName === "H2") {
                        // h2の場合は親リストに追加
                        tocList.appendChild(listItem);
                        currentH2Item = listItem;
                        
                        // h3のためのサブリストを作成
                        currentH2List = document.createElement("ul");
                        currentH2Item.appendChild(currentH2List);
                    } else if (heading.tagName === "H3" && currentH2List) {
                        // h3の場合は現在のh2の下のサブリストに追加
                        currentH2List.appendChild(listItem);
                    }
                });
                
                // フローティング目次に追加
                floatingTocContainer.innerHTML = "";
                floatingTocContainer.appendChild(tocList);
                
                // スクロール監視の設定
                function highlightCurrentHeading() {
                    const scrollPosition = window.scrollY + 100; // 少し余裕を持たせる
                    
                    // 最後に見つかったビューポート内の見出し
                    let currentHeadingId = null;
                    
                    // 全見出しをチェック
                    for (let i = 0; i < headingPositions.length; i++) {
                        const headingTop = headingPositions[i].element.getBoundingClientRect().top + window.scrollY;
                        
                        // スクロール位置が見出しを超えていたら、その見出しが現在位置
                        if (scrollPosition >= headingTop) {
                            currentHeadingId = headingPositions[i].id;
                            // 次の見出しまでスクロールしていなければここが現在位置
                        } else {
                            break;
                        }
                    }
                    
                    // すべてのリンクからactiveクラスを削除
                    headingPositions.forEach(item => {
                        item.link.classList.remove("active");
                    });
                    
                    // 現在の見出しに対応するリンクにactiveクラスを追加
                    if (currentHeadingId) {
                        headingPositions.forEach(item => {
                            if (item.id === currentHeadingId) {
                                item.link.classList.add("active");
                            }
                        });
                    }
                }
                
                // 初期表示時の現在位置をハイライト
                highlightCurrentHeading();
                
                // スクロール時に現在位置をハイライト
                window.addEventListener("scroll", highlightCurrentHeading);
            }
            
            // 目次の自動生成を実行
            generateTableOfContents();
            
            // フローティング目次の表示/非表示を制御
            const floatingTocButton = document.querySelector(".floating-toc-button");
            const floatingToc = document.querySelector(".floating-toc");
            
            if (floatingTocButton && floatingToc) {
                // 目次ボタンをクリックしたときの処理
                floatingTocButton.addEventListener("click", function(e) {
                    e.stopPropagation();
                    if (floatingToc.style.display === "block") {
                        floatingToc.style.display = "none";
                    } else {
                        floatingToc.style.display = "block";
                    }
                });
            }
            
            // 画面クリック時の処理
            document.addEventListener("click", function(event) {
                // 目次内のリンクをクリックした場合
                if (event.target.tagName === "A" && floatingToc.contains(event.target)) {
                    floatingToc.style.display = "none";
                }
                
                // 目次とボタン以外をクリックした場合
                if (floatingToc && floatingTocButton && 
                    !floatingToc.contains(event.target) && 
                    !floatingTocButton.contains(event.target)) {
                    floatingToc.style.display = "none";
                }
            });
        });
        </script>
        ';
    }
}
add_action("wp_footer", "add_floating_toc");

// CSSをヘッダーに追加
function add_floating_toc_styles() {
    echo '<style>
    .floating-toc {
        position: fixed;
        right: 20px;
        bottom: 20px;
        background-color: rgba(40, 40, 40, 0.65); /* 透明度をさらに上げました */
        border-radius: 8px;
        box-shadow: 0 3px 12px rgba(0, 0, 0, 0.3);
        max-width: 250px;
        z-index: 999;
        display: none;
        transition: all 0.3s ease;
        border: 1px solid rgba(68, 68, 68, 0.7); /* 境界線も少し透明に */
    }
    
    .floating-toc-button {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 50px;
        height: 50px;
        background-color: rgba(40, 40, 40, 0.6); /* 透明度をさらに上げました */
        color: white;
        border-radius: 50%;
        position: fixed;
        right: 20px;
        bottom: 20px;
        cursor: pointer;
        z-index: 1000;
        box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
        transition: transform 0.3s ease, background-color 0.3s ease;
    }
    
    .toc-button-content {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
    }
    
    .toc-button-text {
        font-size: 10px;
        margin-top: 2px;
        font-weight: 500;
    }
    
    .floating-toc-button:hover {
        transform: scale(1.05);
        background-color: rgba(30, 30, 30, 0.7);
    }
    
    .floating-toc-content {
        padding: 15px;
        max-height: 300px;
        overflow-y: auto;
        color: #ffffff; /* 文字色を純白に */
    }
    
    .floating-toc-content h4 {
        margin-top: 0;
        margin-bottom: 10px;
        font-size: 16px;
        color: #fff; /* タイトルは純白に */
        border-bottom: 1px solid rgba(85, 85, 85, 0.7); /* 下線も少し透明に */
        padding-bottom: 5px;
        text-shadow: 0 1px 1px rgba(0, 0, 0, 0.5); /* 文字に影をつけて読みやすく */
    }
    
    .toc-list {
        padding-left: 20px;
    }
    
    .toc-list li {
        font-size: 14px;
        margin-bottom: 8px;
        color: #ffffff; /* リスト項目の文字色 */
    }
    
    .toc-list ul {
        padding-left: 20px;
    }
    
    .floating-toc-content a {
        color: #ffffff; /* リンクの色も白に */
        text-decoration: none;
        font-weight: 500; /* 少し太くして透明背景でも見やすく */
        text-shadow: 0 1px 1px rgba(0, 0, 0, 0.5); /* 文字に影をつけて読みやすく */
    }
    
    .floating-toc-content a:hover {
        text-decoration: underline;
        color: #f0f0f0; /* ホバー時は少し薄い白に */
    }

    /* アクティブな目次項目のスタイル */
    .floating-toc-content a.active {
        background-color: rgba(255, 255, 255, 0.2);
        box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.3);
        border-radius: 3px;
        padding: 2px 5px;
        margin-left: -5px;
        font-weight: 700;
    }
	
    @media (min-width: 769px) {
        .floating-toc-button,
        .floating-toc {
            display: none !important;
        }
    }
    </style>';
}
add_action('wp_head', 'add_floating_toc_styles');

引き続き見やすいブログを目指す

ジミログ
改善はいつまでも続く

今回はUIの改善を実施したわけだけど、改善が完了になることはない。
特に閲覧デバイスの8割がスマホということはブログのUIはスマホ向けに作る必要があるわけで、もっともっと見やすくそして離脱しにくいサイト構成を考える必要がある。

またブログ改善があれば「ブログ改善」カテゴリで自分の備忘録代わりに発信していこうと思う。

じゃ、おつ!!

おすすめアイテム

Amazon商品へのリンク (1)

おすすめの記事

-ブログ
-,

目次

目次(タップでジャンプ)