<link id="fontawesome642min" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css" rel="stylesheet"><link id="fontawesome-anime111min" href="https://cdn.jsdelivr.net/npm/font-awesome-animation@1.1.1/css/font-awesome-animation.min.css" rel="stylesheet">

<link id="fukidashi" rel="stylesheet" href="https://dl.dropboxusercontent.com/scl/fi/n84qlo8bygfzcsq2n5yw8/markdown_style_sheet.css?rlkey=dy8sm4di4likz27vx2jdf6dbs">

<ul class="table-of-contents">
    <li><a href="#目次を固定配置する">目次を固定配置する</a><ul>
            <li><a href="#目次の構造">目次の構造</a></li>
            <li><a href="#固定配置するコード">固定配置するコード</a></li>
        </ul>
    </li>
    <li><a href="#表示非表示をボタンで切り替え">表示非表示をボタンで切り替え</a><ul>
            <li><a href="#ボタンを作成">ボタンを作成</a><ul>
                    <li><a href="#HTML">HTML</a></li>
                    <li><a href="#CSS">CSS</a></li>
                    <li><a href="#JavaScript">JavaScript</a></li>
                </ul>
            </li>
            <li><a href="#ボタンを目次の上に配置">ボタンを目次の上に配置</a><ul>
                    <li><a href="#CSS-1">CSS</a></li>
                </ul>
            </li>
            <li><a href="#表示非表示を切り替えるコード">表示非表示を切り替えるコード</a><ul>
                    <li><a href="#JavaScript-1">JavaScript</a></li>
                </ul>
            </li>
        </ul>
    </li>
</ul>

<section class="pm"><p></p>

<h2 id="目次を固定配置する">目次を固定配置する</h2>

<h3 id="目次の構造">目次の構造</h3>

<p>はてなブログでは、<code>[:contents]</code>と書くと記事内の<code>&lt;H&gt;</code>タグ(見出し)を読み取って、目次を自動生成してくれます。
HTMLで確認すると、だいたい以下<i class="fas fa-arrow-circle-down"></i>のような構造になっています。</p>

`<!-- コード省略 -->`

<p>上のコード<i class="fa-solid fa-code"></i> で生成される目次はこちら<i class="fas fa-arrow-circle-down"></i>です。</p>

<ul id="mokuji-mihon">
  <li><a href="#大見出し1">大見出し1</a>
    <ul>
      <li><a href="#中見出し1">中見出し1</a>
        <ul>
          <li><a href="小見出し1">小見出し1</a></li>
        </ul>
      </li>
      <li><a href="#中見出し2">中見出し2</a></li>
    </ul>
  </li>
  <li><a href="#大見出し2">大見出し2</a>
    <ul>
      <li><a href="#中見出し2">中見出し2</a></li>
    </ul>
  </li>
</ul>

<p><code>&lt;ul&gt;</code>タグでリストを構成し、<code>table-of-contents</code>というクラス名を付与しています。</p>

`<!-- コード省略 -->`

<h3 id="固定配置するコード">固定配置するコード</h3>

<p>このタグとクラス名は常に一致するので、こちらを利用してCSS<i class="fa-brands fa-css3-alt fa-lg" style="color: #2864f0;"></i>でスタイルを指定すれば、目次の表示をページ内の任意の場所に固定することができます。</p>

<p>以下<i class="fas fa-arrow-circle-down"></i>がそのコードです。</p>

`<!-- コード省略 -->`

<ol>
<li><code>position: fixed</code>というプロパティを指定すると、ページをスクロールしても、要素を常に同じ位置に固定します。</li>
<li><code>top</code>、<code>left</code>で上端、左端からの位置を指定しています。ここでは、上端から40px、左端から10px、それぞれ間を空ける指定をしています。</li>
<li><code>z-index: 100</code>は、記事本編など多要素と重なったとき、前面に出すために指定しています。数値が大きいほど、優先的に前面で表示されます。</li>
</ol>

<p>こちらを、「<i class="blogicon-bracket"></i><label>デザインCSS</label>」に貼り付けるか、ページ内に<code>&lt;style&gt;</code>~<code>&lt;/style&gt;</code>で囲んで設置すると、目次を記事ページの指定した場所に固定配置することができます。</p>

<p></p></section>

<section class="pm"><p></p>

<h2 id="表示非表示をボタンで切り替え">表示非表示をボタンで切り替え</h2>

<p>常に固定されていると、記事を読んでいる方がブラウザ幅を縮めたり、見出しの文字数が多かったりで目次の幅が広いなど、目次を非表示にしたいことがあると思います。</p>

<p>そこで、目次の表示非表示を切り替えるボタンを設置して、ユーザー(読者)が使いやすいように配慮します。</p>

<h3 id="ボタンを作成">ボタンを作成<i class="fa-solid fa-code"></i></h3>

<p>HTMLの<code>&lt;button&gt;</code>タグでボタンを配置し、CSSで装飾、JavaScriptでボタンをクリックすると目次を出し入れする機能を装備します。</p>

<h4 id="HTML"><i class="fa-brands fa-html5 fa-lg" style="color: #f06429;"></i>HTML</h4>

`<!-- コード省略 -->`

<p class="output-text"><i class="fas fa-solid fa-arrow-right-from-bracket fa-beat-fade">&nbsp;</i>Output</p>

<p><button><img src="https://dl.dropboxusercontent.com/scl/fi/82khbr7r4l0dzw0m3aw8o/mokuji.svg?rlkey=10hlwjjr74id4gi5i07dfdp3e" alt="mokuji icon" width="36" height="36" loading="lazy" itemprop="svg image"></button></p>

<p><code>&lt;button&gt;</code>タグでボタン要素にして、画像を配置することにしました。これだけだと、まだアイコン画像だけですね。</p>

<h4 id="CSS"><i class="fa-brands fa-css3-alt fa-lg" style="color: #2864f0;"></i>CSS</h4>

`<!-- コード省略 -->`

<p class="output-text"><i class="fas fa-solid fa-arrow-right-from-bracket fa-beat-fade">&nbsp;</i>Output</p>

<p><button id="toggle-button" class="nomal-style"><img src="https://dl.dropboxusercontent.com/scl/fi/82khbr7r4l0dzw0m3aw8o/mokuji.svg?rlkey=10hlwjjr74id4gi5i07dfdp3e" alt="mokuji icon" width="18" height="18" loading="lazy" itemprop="svg image"></button></p>

<p>CSSで、装飾しました。</p>

<ol>
<li><code>:hover</code>要素:ボタンをマウスオーバーしたとき、少し上にずらして影をつけています。</li>
<li><code>:active</code>要素:ボタンをクリック(アクティブ)した瞬間、影を消して少し右にずらしています。</li>
<li><code>::after</code>要素:<code>content</code>プロパティで「CONTENTS OPEN」と「CONTENTS CLOSE」というテキストを表示しています。</li>
</ol>

<p>さらに、ボタンクリックを押して目次が開いたときと閉じたときのデザインを変更するために、JavaScript<i class="fas fa-arrow-circle-down"></i>でクラスを切り替えています。</p>

<h4 id="JavaScript"><i class="fa-brands fa-square-js fa-lg" style="color: #efd81d;"></i>JavaScript</h4>

`<!-- コード省略 -->`

<p>このスクリプトは、ボタン要素にクラス名<code>nomal-style</code>がある場合はそれを消し、<code>active-style</code>クラスを追加してswap(切り替え)します。また逆に、<code>active-style</code>クラスが存在するときは、それを削除してクラス名<code>nomal-style</code>を付与します。</p>

<p>これにより、ボタンに指定したCSSスタイルの適用が変わり、デザインを変化させています。</p>
  
<h3 id="ボタンを目次の上に配置">ボタンを目次の上に配置<i class="fa-solid fa-code"></i></h3>

<p>CSSでボタンを目次の上に配置します。</p>

<h4 id="CSS-1"><i class="fa-brands fa-css3-alt fa-lg" style="color: #2864f0;"></i>CSS</h4>

`<!-- コード省略 -->`

<ol>
<li><code>position: fixed</code>:目次と同様、ボタンの要素を常に同じ位置に固定します。</li>
<li><code>top</code>、<code>left</code>:上端、左端からの位置を指定します。先ほど目次は上端から10pxの位置に配置したので、ボタンは上端から10pxの位置に配置することにします。</li>
<li><code>z-index: 100</code>:目次同様、記事本編など多要素と重なったとき、前面にないとボタンが押せずに目次の表示非表示切り替えができなくなってしまうのを防ぐため、要素の重なりを前面に指定しています。</li>
</ol>

<p>これで、作成した表示の切り替えボタンが目次の上に配置されました。次に、目次の表示非表示機能を、JavaScriptにコードを追加して、実装します。</p>

<h3 id="表示非表示を切り替えるコード">表示非表示を切り替えるコード<i class="fa-solid fa-code"></i></h3>

<h4 id="JavaScript-1"><i class="fa-brands fa-square-js fa-lg" style="color: #efd81d;"></i>JavaScript</h4>

`<!-- コード省略 -->`

  <p>このJavaScriptコードは、記事ページ上での目次(table of contents)の表示と非表示を制御するものです。以下コードの解説です。</p>

<div class="fukidashi2">
  <div class="icon">
    <div class="wphg">
      <img src="https://cdn-ak.f.st-hatena.com/images/fotolife/f/fobitows/20230818/20230818153009.png" alt="らんらん、楽しい動物の仲間たち">
    </div>
  <div class="iconfont">ranran♪</div>
  </div>  
  <div class="bubble">
興味ないよ~、という方は飛ばしてくださいね笑
  </div>
  </div>

<ol>
<li><p><code>const tableOfContents = document.querySelector(".table-of-contents");</code>: ウェブページ内からクラス名が "table-of-contents" で指定された要素を取得して、<code>tableOfContents</code> という変数に格納します。これは目次を表す要素です。</p></li>
<li><p><code>const toggleButton = document.getElementById("toggle-button");</code>: ウェブページ内からIDが "toggle-button" の要素を取得して、<code>toggleButton</code> という変数に格納します。この要素は目次の表示/非表示をトグルするボタンを表します。</p></li>
<li><p><code>tableOfContents.style.display = "none";</code>: 初めに目次を非表示に設定します。これにより、ページを読み込んだときに目次が非表示になります。</p></li>
<li><p><code>toggleButton.addEventListener("click", () =&gt; { ... });</code>: "toggle-button" 要素(トグルボタン)がクリックされたときの処理を設定します。クリック時に目次の表示状態をトグルするコードが含まれています。</p>

<ul>
<li>もし目次が表示中であれば、非表示に変更し、</li>
<li>もし目次が非表示であれば、表示に変更します。</li>
</ul>
</li>
<li><p><code>tableOfContents.addEventListener("click", (event) =&gt; { ... });</code>: 目次がクリックされたときの処理を設定します。もしクリックされた要素が "A" タグ(リンク)であれば、目次を非表示にすることで、目次が選択されたリンクをクリックしたら自動的に閉じるようになります。</p></li>
</ol>

<p>このコードは、ボタンをクリックすることで目次を表示/非表示できるようにします。また、目次内のリンクをクリックすると目次が自動的に非表示になります。</p>
  
</section>
View Compiled
ul.table-of-contents {
  position: fixed;
  top: 30px;
  left: 10px;
  z-index: 100;

  /* ココから下はデザイン */
  background: rgb(229, 242, 239, 0.9);
  border-radius: 7px;
  box-shadow: rgb(0, 0, 0, 0.1) 0 0 9px;
}

/* ボタンの基本スタイル */
button#toggle-button {
  border: none;
  border-radius: 10px;
  padding: 5px 0 8px;
  font-weight: 600;
  text-shadow: 0px 0px #fff;
  background: rgba(126, 170, 212, 0.5);
  width: 189px;
}

/* ホバー時のスタイル変更 */
button#toggle-button:hover {
  transform: translateY(-4px);
  box-shadow: rgba(0, 0, 0, 20%) 0 0 9px;
}

/* ボタンがアクティブ(クリック)されたときのスタイル */
button#toggle-button:active {
  box-shadow: rgba(0, 0, 0, 0.15) 0 0;
  transform: translate(4px);
}

/* 通常スタイルのボタンに表示されるテキストコンテンツ(after要素) */
button#toggle-button.nomal-style::after {
  content: "CONTENTS OPEN";
}

/* アクティブスタイルのボタンに表示されるテキストコンテンツ(after要素) */
button#toggle-button.active-style::after {
  content: "CONTENTS CLOSE";
}

/* 目次が開いているときのボタンスタイル */
button#toggle-button.active-style {
  background: #5a7b99;
  color: #fff;
  text-shadow: 0px 0px #000;
}

/* ボタン内の画像(目次アイコン)スタイル */
#toggle-button img {
  margin: 2px 5px -3px 0px;
}

/* ボタンの配置 */
button#toggle-button {
  position: fixed;
  top: 10px;
  left: 10px;
  z-index: 100;
}

/*//////////////////////////////////////////////////
  ココから下↓、ページの装飾CSS(記事とは無関係です) 
//////////////////////////////////////////////////*/

body {
  margin-top: 100px;
  margin-left: 100px;
}

.fukidashi2 {
  width: 95%;
  margin: 40px 0;
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  align-items: flex-start;
  right: -20px;
  position: relative;
}
.fukidashi2 .icon img {
  object-fit: cover;
  overflow: visible;
}
.fukidashi2 .icon .wphg {
  height: 100px;
}
.fukidashi2 .iconfont {
  padding-top: 0;
  padding-left: 0;
  text-align: center;
}
.fukidashi2 .bubble {
  margin: 60px 100px 20px 0px;
  padding: 13px;
  border-radius: 17px;
  max-width: 500px;
  border-right: solid 3px #aaa;
  border-bottom: solid 3px #aaa;
}
.fukidashi2 .bubble:before {
  top: 14px;
  right: -20.5px;
  border-width: 21px 21px 0 0;
}
.fukidashi2 .bubble:after {
  border-width: 14px 16px 0 0;
}

ul.table-of-contents a {
  text-decoration: none;
}
ul.table-of-contents {
  padding: 25px 20px 20px 35px;
  border: solid 1px;
  border-color: dimgray;
  line-height: 1.6em;
}
var button = document.getElementById("toggle-button");
button.addEventListener("click", function () {
  if (button.classList.contains("nomal-style")) {
    button.classList.remove("nomal-style");
    button.classList.add("active-style");
  } else {
    button.classList.remove("active-style");
    button.classList.add("nomal-style");
  }
});

const tableOfContents = document.querySelector(".table-of-contents");
const toggleButton = document.getElementById("toggle-button");

tableOfContents.style.display = "none";
toggleButton.addEventListener("click", () => {
  if (tableOfContents.style.display === "block") {
    tableOfContents.style.display = "none";
  } else {
    tableOfContents.style.display = "block";
  }
});
tableOfContents.addEventListener("click", (event) => {
  if (event.target.tagName === "A") {
    tableOfContents.style.display = "none";
  }
});
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.