ライブデモ Live Demo

ページ番号を表示するページネーション。アクティブなページはハイライト表示され、ホバー時にエフェクトが適用されます。

A pagination component displaying page numbers. The active page is highlighted with hover effects applied.

1

AI向け説明 AI Description

ページ番号を表示するページネーション。アクティブなページは`is-active`クラスで背景色とテキスト色を変更します。ホバー時は`transition`でスムーズに背景色とボーダーを変更します。前へ/次へボタンは`is-disabled`クラスで無効化し、`pointer-events: none`でクリック不可にします。

A pagination component displaying page numbers. The active page changes background and text color with the `is-active` class. On hover, background and border change smoothly with `transition`. Previous/next buttons are disabled with `is-disabled` class and `pointer-events: none` to prevent clicks.

調整可能パラメータ Adjustable Parameters

実装 Implementation

HTML + CSS + JS

<nav class="pagination" aria-label="Pagination"></nav>

<style>
.pagination {
  display: flex;
  gap: 8px;
  align-items: center;
}

.pagination__item {
  display: flex;
}

.pagination__link {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 40px;
  height: 40px;
  padding: 0 12px;
  border-radius: 8px;
  color: #1d2242;
  text-decoration: none;
  transition: all 0.2s ease;
  border: 1px solid transparent;
}

.pagination__link:hover {
  background: #f5f7ff;
  border-color: #dfe3f6;
}

.pagination__link.is-active {
  background: #5c6ac4;
  color: #fff;
  border-color: #5c6ac4;
}

.pagination__link.is-disabled {
  opacity: 0.4;
  pointer-events: none;
  cursor: not-allowed;
}
</style>

<script>
const pagination = document.querySelector(".pagination");
const totalPages = 10;
let currentPage = 1;

function renderPagination(page) {
  const pages = [];
  const showEllipsis = totalPages > 7;

  if (!showEllipsis) {
    for (let i = 1; i <= totalPages; i++) {
      pages.push(i);
    }
  } else {
    if (page <= 3) {
      pages.push(1, 2, 3, "ellipsis", totalPages);
    } else if (page >= totalPages - 2) {
      pages.push(1, "ellipsis", totalPages - 2, totalPages - 1, totalPages);
    } else {
      pages.push(1, "ellipsis", page - 1, page, page + 1, "ellipsis", totalPages);
    }
  }

  pagination.innerHTML = "";

  // 前ボタン
  const prevItem = document.createElement("div");
  prevItem.className = "pagination__item";
  const prevLink = document.createElement("a");
  prevLink.href = "#";
  prevLink.className = `pagination__link ${page === 1 ? "is-disabled" : ""}`;
  prevLink.setAttribute("aria-label", "Previous");
  prevLink.textContent = "‹";
  prevLink.addEventListener("click", (e) => {
    e.preventDefault();
    if (page > 1) renderPagination(page - 1);
  });
  prevItem.appendChild(prevLink);
  pagination.appendChild(prevItem);

  // ページ番号
  pages.forEach((p) => {
    const item = document.createElement("div");
    item.className = "pagination__item";
    if (p === "ellipsis") {
      const ellipsis = document.createElement("span");
      ellipsis.className = "pagination__link";
      ellipsis.style.cssText = "pointer-events: none; opacity: 0.5;";
      ellipsis.textContent = "...";
      item.appendChild(ellipsis);
    } else {
      const link = document.createElement("a");
      link.href = "#";
      link.className = `pagination__link ${page === p ? "is-active" : ""}`;
      link.textContent = p;
      link.addEventListener("click", (e) => {
        e.preventDefault();
        renderPagination(p);
      });
      item.appendChild(link);
    }
    pagination.appendChild(item);
  });

  // 次ボタン
  const nextItem = document.createElement("div");
  nextItem.className = "pagination__item";
  const nextLink = document.createElement("a");
  nextLink.href = "#";
  nextLink.className = `pagination__link ${page === totalPages ? "is-disabled" : ""}`;
  nextLink.setAttribute("aria-label", "Next");
  nextLink.textContent = "›";
  nextLink.addEventListener("click", (e) => {
    e.preventDefault();
    if (page < totalPages) renderPagination(page + 1);
  });
  nextItem.appendChild(nextLink);
  pagination.appendChild(nextItem);

  currentPage = page;
}

renderPagination(1);
</script>

React (JSX + CSS)

// react/N-005.jsx
import { useState } from "react";
import "./N-005.css";

const totalPages = 10;

export default function Pagination() {
  const [currentPage, setCurrentPage] = useState(1);

  function getPages() {
    const pages = [];
    const showEllipsis = totalPages > 7;

    if (!showEllipsis) {
      for (let i = 1; i <= totalPages; i++) {
        pages.push(i);
      }
    } else {
      if (currentPage <= 3) {
        pages.push(1, 2, 3, "ellipsis", totalPages);
      } else if (currentPage >= totalPages - 2) {
        pages.push(1, "ellipsis", totalPages - 2, totalPages - 1, totalPages);
      } else {
        pages.push(1, "ellipsis", currentPage - 1, currentPage, currentPage + 1, "ellipsis", totalPages);
      }
    }
    return pages;
  }

  return (
    <nav className="pagination" aria-label="Pagination">
      <div className="pagination__item">
        <a
          href="#"
          className={`pagination__link ${currentPage === 1 ? "is-disabled" : ""}`}
          onClick={(e) => {
            e.preventDefault();
            if (currentPage > 1) setCurrentPage(currentPage - 1);
          }}
        >
          ‹
        </a>
      </div>
      {getPages().map((page, index) => (
        <div key={index} className="pagination__item">
          {page === "ellipsis" ? (
            <span className="pagination__link" style={{ pointerEvents: "none", opacity: 0.5 }}>
              ...
            </span>
          ) : (
            <a
              href="#"
              className={`pagination__link ${currentPage === page ? "is-active" : ""}`}
              onClick={(e) => {
                e.preventDefault();
                setCurrentPage(page);
              }}
            >
              {page}
            </a>
          )}
        </div>
      ))}
      <div className="pagination__item">
        <a
          href="#"
          className={`pagination__link ${currentPage === totalPages ? "is-disabled" : ""}`}
          onClick={(e) => {
            e.preventDefault();
            if (currentPage < totalPages) setCurrentPage(currentPage + 1);
          }}
        >
          ›
        </a>
      </div>
    </nav>
  );
}
/* react/N-005.css */
.pagination {
  display: flex;
  gap: 8px;
  align-items: center;
}

.pagination__item {
  display: flex;
}

.pagination__link {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 40px;
  height: 40px;
  padding: 0 12px;
  border-radius: 8px;
  color: #1d2242;
  text-decoration: none;
  transition: all 0.2s ease;
  border: 1px solid transparent;
}

.pagination__link:hover {
  background: #f5f7ff;
  border-color: #dfe3f6;
}

.pagination__link.is-active {
  background: #5c6ac4;
  color: #fff;
  border-color: #5c6ac4;
}

.pagination__link.is-disabled {
  opacity: 0.4;
  pointer-events: none;
  cursor: not-allowed;
}

AIへの指示テンプレート AI Prompt Template

以下のテンプレートをコピーしてAIアシスタントに貼り付けると、このパターンの実装を依頼できます。 Copy the template below and paste it into your AI assistant to request an implementation of this pattern.