ハンドルネームの敬称は省略できます

🦀パソコンを叩く日々🐈

はてなブログの見出しをクリックしてアンカーリンクをつけるようにする

はてなブログを見ていて足りないものに気付きました。それは見出しごとのアンカーリンクです。

割と参照するときによく使うのであってほしい機能ですが、パッと見たところ公式テーマにもそのようなものはないためDIYしましょうというやつぽいです。

まずは結論

以下のようなコードをデザイン設定のフッタ枠に貼り付けてください。

<script>
document.addEventListener('DOMContentLoaded', function() {
    const headings = document.querySelectorAll('h1, h2, h3, h4, h5, h6');
    const excludeIds = ['title', 'blog-description']; // ブログタイトルとブログ説明は除外する
    
    headings.forEach(function(heading) {
        if (heading.id && !excludeIds.includes(heading.id)) {
            heading.style.cursor = 'pointer';
            heading.style.position = 'relative';
            heading.classList.add('clickable-heading');
            
            heading.addEventListener('click', function(e) {
                if (e.target.tagName === 'A') return;
                window.location.hash = heading.id;
            });
            
            // PCでのみ表示するアイコンを追加
            const anchor = document.createElement('span');
            anchor.innerHTML = '#';
            anchor.className = 'heading-anchor-icon';
            
            heading.appendChild(anchor);
        }
    });
});
</script>
<style>
html {
    scroll-behavior: smooth;
}

.clickable-heading:hover {
    color: #007cba;
    transition: color 0.2s ease;
}

.heading-anchor-icon {
    position: absolute;
    left: -25px;
    top: 50%;
    transform: translateY(-50%);
    opacity: 0;
    transition: opacity 0.2s ease;
    color: #666;
    font-weight: normal;
    pointer-events: none;
}

.clickable-heading:hover .heading-anchor-icon {
    opacity: 0.7;
}

/* PC表示時のみアイコンを表示(768px以上) */
@media (min-width: 768px) {
    .heading-anchor-icon {
        display: inline;
    }
    
    /* 見出しの左余白を確保(PC時のみ) */
    h1, h2, h3, h4, h5, h6 {
        margin-left: 30px;
    }
}

/* スマホ表示時はアイコンを非表示 */
@media (max-width: 767px) {
    .heading-anchor-icon {
        display: none;
    }
    
    /* スマホ時は左余白をリセット */
    h1, h2, h3, h4, h5, h6 {
        margin-left: 0;
    }
}
</style>

はてなブログでアンカーリンクをつける · GitHub

実際にブログに適用するときは minify するなどしてから埋め込んでやると良いでしょう。

上記のコードを Terserで minify した結果を以下に示します。

<script>document.addEventListener("DOMContentLoaded",(function(){const e=document.querySelectorAll("h1, h2, h3, h4, h5, h6"),n=["title","blog-description"];e.forEach((function(e){if(e.id&&!n.includes(e.id)){e.style.cursor="pointer",e.style.position="relative",e.classList.add("clickable-heading"),e.addEventListener("click",(function(n){"A"!==n.target.tagName&&(window.location.hash=e.id)}));const n=document.createElement("span");n.innerHTML="#",n.className="heading-anchor-icon",e.appendChild(n)}}))}))</script><style>html{scroll-behavior:smooth}.clickable-heading:hover{color:#007cba;transition:color .2s ease}.heading-anchor-icon{position:absolute;left:-25px;top:50%;transform:translateY(-50%);opacity:0;transition:opacity .2s ease;color:#666;font-weight:400;pointer-events:none}.clickable-heading:hover .heading-anchor-icon{opacity:.7}@media (min-width:768px){.heading-anchor-icon{display:inline}h1,h2,h3,h4,h5,h6{margin-left:30px}}@media (max-width:767px){.heading-anchor-icon{display:none}h1,h2,h3,h4,h5,h6{margin-left:0}}</style>

terser.org

やっていること

今回紹介した JavaScript のコードを以下に示します。

document.addEventListener('DOMContentLoaded', function() {
    const headings = document.querySelectorAll('h1, h2, h3, h4, h5, h6');
    const excludeIds = ['title', 'blog-description']; // ブログタイトルとブログ説明は除外する
    
    headings.forEach(function(heading) {
        if (heading.id && !excludeIds.includes(heading.id)) {
            heading.style.cursor = 'pointer';
            heading.style.position = 'relative';
            heading.classList.add('clickable-heading');
            
            heading.addEventListener('click', function(e) {
                if (e.target.tagName === 'A') return;
                window.location.hash = heading.id;
            });
            
            // PCでのみ表示するアイコンを追加
            const anchor = document.createElement('span');
            anchor.innerHTML = '#';
            anchor.className = 'heading-anchor-icon';
            
            heading.appendChild(anchor);
        }
    });
});

やっていることはシンプルで、見出しに id 属性が設定されているのでクリックされたら location.hash に id をつけるという処理です。

ただ、ヘッダにリンクが設定されている場合やブログタイトル、ブログ説明については除外するような仕組みにしています。

html {
    scroll-behavior: smooth;
}

.clickable-heading:hover {
    color: #007cba;
    transition: color 0.2s ease;
}

.heading-anchor-icon {
    position: absolute;
    left: -25px;
    top: 50%;
    transform: translateY(-50%);
    opacity: 0;
    transition: opacity 0.2s ease;
    color: #666;
    font-weight: normal;
    pointer-events: none;
}

.clickable-heading:hover .heading-anchor-icon {
    opacity: 0.7;
}

/* PC表示時のみアイコンを表示(768px以上) */
@media (min-width: 768px) {
    .heading-anchor-icon {
        display: inline;
    }
    
    /* 見出しの左余白を確保(PC時のみ) */
    h1, h2, h3, h4, h5, h6 {
        margin-left: 30px;
    }
}

/* スマホ表示時はアイコンを非表示 */
@media (max-width: 767px) {
    .heading-anchor-icon {
        display: none;
    }
    
    /* スマホ時は左余白をリセット */
    h1, h2, h3, h4, h5, h6 {
        margin-left: 0;
    }
}

CSS の方は PC 表示とスマホ表示で挙動がやや異なる用意しています。

PC の方はちょっとおしゃれに # がタイトルの左に出るようにしています。

一方でスマホ表示の場合は幅がないので # は表示させずに色が変わるだけにとどめています。

アンカーリンクお試し会場

アンカーリンクの動作をお試しする会場

Heading 3

Heading 3 の中身

Heading 4

Heading 4 の中身

https://example.com のリンク

ここの見出しは https://example.com に飛ぶよ。