「もっと見る」ボタンのJavaScript実装ガイド

# 初めに

こんにちは！taisei_code です。

私がWeb制作会社で勤務していた時、「もっと見る」ボタンの実装に関する多くの記事がjQueryに関するものであることに気づき、JavaScriptについての解説記事を執筆することにしました。jQueryの利用はコード量を減らして便利にするものですが、初学者にはJavaScriptの基本概念への深い理解が特に重要です。この基礎が、<font color="red">**将来Reactなどのライブラリーやフレームワーク**</font>を学ぶ過程で非常に役立ちます。

# ゴール

![2024-03-264.51.24-ezgif.com-video-to-gif-converter.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3536873/ee878591-a935-b6bc-cb16-cc0425ca8474.gif)



# 実装要件

- 初期表示で最初の 3 つの要素を表示し、それ以降の要素を非表示にする。
- 「more」ボタンのクリック：非表示の要素を 3 件ずつ表示する。
- すべての要素が表示された場合に「more」を「close」に変更する。

# コード全体

### HTML

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <!-- 省略 -->
  </head>
  <body>
    <div class="mt-32 max-w-5xl mx-auto">
      <div class="grid grid-cols-3 gap-4">
        <div class="item bg-white shadow-md rounded-lg overflow-hidden">
          <img src="/img/sea-img.jpg" class="w-full" />
          <h2 class="text-center text-gray-800 font-semibold py-2">Title</h2>
        </div>
        <div class="item bg-white shadow-md rounded-lg overflow-hidden">
          <img src="/img/sea-img.jpg" class="w-full" />
          <h2 class="text-center text-gray-800 font-semibold py-2">Title</h2>
        </div>
        <div class="item bg-white shadow-md rounded-lg overflow-hidden">
          <img src="/img/sea-img.jpg" class="w-full" />
          <h2 class="text-center text-gray-800 font-semibold py-2">Title</h2>
        </div>
      </div>
    </div>

    <div class="flex justify-center mt-8 mb-32">
      <button
        id="moreRead"
        class="more-read bg-blue-500 hover:bg-blue-700 text-white font-semibold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
      >
        more
      </button>
    </div>
  </body>
</html>
```

### JavaScript

```javascript
// 初期設定
const first = 3;
const hiddenClass = "hidden";
const targetClass = ".item";
const targets = document.querySelectorAll(targetClass);
const addBtn = document.querySelector("#moreRead");
let add = 3;
let targetsLength = targets.length;

// 初期表示の関数
function initializeDisplay() {
  if (targetsLength > first) {
    for (let i = first; i < targetsLength; i++) {
      targets[i].classList.add(hiddenClass);
    }
  }
}

// もっと見るボタンのクリックイベントハンドラ
addBtn.addEventListener("click", () => {
  togglePostsVisibility();
});

// 記事の表示/非表示を切り替える関数
function togglePostsVisibility() {
  const hiddenElements = document.querySelectorAll(
    targetClass + "." + hiddenClass
  );
  if (hiddenElements.length == 0) {
    addBtn.textContent = "close";
    hiddenPosts();
    return;
  }
  displayPosts(hiddenElements);
}

// 記事を表示する関数
function displayPosts(hiddenElements) {
  for (let i = 0; i < add; i++) {
    hiddenElements[i].classList.remove(hiddenClass);
    if (
      document.querySelectorAll(targetClass + "." + hiddenClass).length == 0
    ) {
      break;
    }
  }
  if (document.querySelectorAll(targetClass + "." + hiddenClass).length == 0) {
    addBtn.textContent = "close";
  }
}

// 記事を非表示にする関数
function hiddenPosts() {
  if (targetsLength > first) {
    for (let i = first; i < targetsLength; i++) {
      targets[i].classList.add(hiddenClass);
    }
  }
}

// 初期表示の呼び出し
initializeDisplay();
```

# JavaScript の解説

### 初期設定

<table>
  <thead>
    <tr>
      <th>変数名</th> <th>役割</th>
    </tr>
  </thead>
  <tr>
    <td> first </td> <td>初期状態で表示する要素の個数（3件）</td>
  </tr>
  <tr>
    <td> hiddenClass </td> <td>非表示の要素に適用するCSSクラス名</td>
  </tr>
  <tr>
    <td> targetClass </td> <td>対象となる要素を取得するCSSクラス名（.item）</td>
  </tr>
  <tr>
    <td> targets </td> <td>対象となる要素全てを取得</td>
  </tr>
  <tr>
    <td> addBtn </td> <td>「more」ボタンを取得</td>
  </tr>
  <tr>
    <td> add </td> <td>ボタンクリック時に表示される要素の数（3個）</td>
  </tr>
  <tr>
    <td> targetsLength </td> <td>対象となる要素全てを取得（6個）</td>
  </tr>
</table>

```javascript
const first = 3;
const hiddenClass = "hidden";
const targetClass = ".item";
const targets = document.querySelectorAll(targetClass);
const addBtn = document.querySelector("#moreRead");
let add = 3;
let targetsLength = targets.length;
```

### 初期表示の関数

```targetsLength``` の要素数（6個）が first（初期値：3個）よりも大きい場合、<font color="red">**4個目以降の要素を非表示にする機能**</font>を実装しています。

```javascript
function initializeDisplay() {
  if (targetsLength > first) {
    for (let i = first; i < targetsLength; i++) {
      targets[i].classList.add(hiddenClass);
    }
  }
}
```

### 「もっと見る」ボタンのクリックイベントハンドラ

「more」ボタンクリックで次に説明する<font color="red">**要素の表示/非表示を切り替える関数**</font>を実行しています。

```javascript
addBtn.addEventListener("click", () => {
  togglePostsVisibility();
});
```

### 表示/非表示を切り替える関数

togglePostsVisibility 関数は<font color="red">**要素の表示と非表示を切り替える機能**</font>を持っています。

**非表示の要素を選択**
`document.querySelectorAll(targetClass + "." + hiddenClass);` で
ページ内から `targetClass` と `hiddenClass` の両方をクラス名に持つ HTML 要素をすべて選択します。例えば、targetClass が post で、hiddenClass が hidden の場合、.post.hidden に一致する要素を全て選択します。

**表示状態の確認と切り替え**
`if (hiddenElements.length == 0) { }`
選択された非表示の要素が 0 個（つまり、すべての要素が既に表示されている状態）の場合、以下の処理を行います。

- `addBtn.textContent = "close";`
- ボタンのテキストを `close` に変更します。
- `hiddenPosts` 関数を呼び出して、要素を非表示にする処理を実行

**displayPosts 関数**
この行では、`hiddenElements` が空ではない（つまり、一つ以上の要素が非表示になっている場合）場合に displayPosts 関数を呼び出して、非表示の要素を表示する処理を実行しています。

```javascript
function togglePostsVisibility() {
  const hiddenElements = document.querySelectorAll(
    targetClass + "." + hiddenClass
  );
  if (hiddenElements.length == 0) {
    addBtn.textContent = "close";
    hiddenPosts();
    return;
  }
  displayPosts(hiddenElements);
}
```

### 要素を表示する関数

displayPosts 関数は、特定の条件下で<font color="red">**非表示になっている要素を表示させる機能**</font>を持っています。

**ループ処理**
for ループを使用して、`hiddenElements` 配列の要素に対して反復処理を行います。ループの各ステップで、非表示(`hiddenClass` が付与された)の要素からこのクラスを削除し、要素を表示状態に戻します。

**表示条件**
`add` 変数に指定された数だけ要素を表示させます。これは、一度に表示する要素の最大数を制限するために使用されます。今回は、3 を `add` 変数に代入しているので 3 個ずつ表示します。

**終了条件**
if 文を使って、非表示になっている要素がもう存在しないかどうかをチェックします。もし非表示の要素がなくなった場合、ループから `break` によって抜け出します。

**ボタンのテキスト変更**
全ての要素が表示された後、if 文で再度非表示になっている要素がないかをチェックします。もし非表示の要素がない場合、「`more`」ボタンのテキストを「`close`」に変更します。

```javascript
function displayPosts(hiddenElements) {
  for (let i = 0; i < add; i++) {
    hiddenElements[i].classList.remove(hiddenClass);
    if (
      document.querySelectorAll(targetClass + "." + hiddenClass).length == 0
    ) {
      break;
    }
  }
  if (document.querySelectorAll(targetClass + "." + hiddenClass).length == 0) {
    addBtn.textContent = "close";
  }
}
```

### 要素を非表示にする関数

hiddenPosts 関数は<font color="red">**表示されてる要素を非表示にする機能**</font>を持っています。

**条件式**
if 文を使用して、`targetsLength`（ページ上の全要素の数）が `first`（初期表示する要素の数：3 個）より大きいかを確認します。この条件が真であれば、処理を続けます。

**ループ処理**
for ループを使って、`first` の次の要素（4 個目）から `targetsLength` までの要素に対して繰り返し処理を行います。このループでは、初期表示された要素以外のすべての要素を対象とします。

**非表示にする処理**
ループ内で、`classList.add(hiddenClass);` を使用して、対象の要素に `hiddenClass` を追加します。`hiddenClass` は CSS クラスで、このクラスが要素に適用されると、要素が非表示になります。

```javascript
function hiddenPosts() {
  if (targetsLength > first) {
    for (let i = first; i < targetsLength; i++) {
      targets[i].classList.add(hiddenClass);
    }
  }
}
```

# 終わりに

この記事では、JavaScriptを使用して「もっと見る」ボタンを実装する方法について解説しました。具体的には、関数、for文、if文、DOM操作、イベントリスナーといった基本概念に焦点を置いています。これらの基本を習得することで、Web開発の現場で遭遇する様々な問題に対処する能力が向上します。ぜひ実践してみてください。