Creating book search app with pagination using HTML, CSS, and JavaScript
December 31, 20245 min read

Creating book search app with pagination using HTML, CSS, and JavaScript

Web developmentJavaScriptCSS3Programming
share this article on
In this article, we'll build a book search app using JavaScript and the Open Library API. We'll implement pagination to display 25 books per page (5 rows x 5 books) and allow users to navigate between pages.

Features of the App

  1. Search for books by title or author.
  2. Display 25 books per page in a grid format.
  3. Add pagination controls (Previous, Next, and numbered buttons).
  4. Handle edge cases like no results and empty input.

Prerequisites

Basic knowledge of HTML, CSS, and JavaScript is sufficient to follow this tutorial.


Step 1: Setting Up the HTML Structure

The HTML will include a search bar, a container to display book results, and a pagination section.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Book Search App</title>
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <!-- Header with background image -->
  <div class="header">
    <h1>Book Worm</h1>
  </div>

  <div class="container">
    <!-- Search bar -->
    <div class="search-bar">
      <input type="text" id="search-input" placeholder="Search books by title or author...">
      <button id="search-button">Search</button>
    </div>

    <!-- Results container -->
    <div class="results" id="results"></div>
    <!-- Pagination -->
    <div id="pagination" class="pagination"></div>
  </div>

  <script src="app.js"></script>
</body>
</html>

Key Sections:

  1. Header: Displays the app title with a background image.
  2. Search Bar: An input field and button for searching books.
  3. Results: A container to display the search results dynamically.
  4. Pagination: A section for navigation buttons.

Step 2: Adding CSS for Styling

Here, we’ll style the app, including the header, search bar, results grid, and pagination.

/* General styles */
body {
  font-family: Arial, sans-serif;
  margin: 0;
  padding: 0;
  background-color: #f4f4f9;
  color: #333;
}

/* Header with background image */
.header {
  background: url('https://via.placeholder.com/1920x500?text=Books+Background') no-repeat center center/cover;
  height: 25vh;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  text-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
}

.header h1 {
  font-size: 2.5rem;
  margin: 0;
}

.container {
  max-width: 1200px;
  margin: 0 auto;
  padding: 1rem;
}

/* Search bar */
.search-bar {
  display: flex;
  gap: 10px;
  margin-top: 20px;
}

.search-bar input {
  flex: 1;
  padding: 12px;
  font-size: 16px;
  border: 1px solid #ccc;
  border-radius: 5px;
}

.search-bar button {
  padding: 10px 15px;
  font-size: 16px;
  cursor: pointer;
  border: none;
  border-radius: 5px;
  background-color: #333;
  color: #fff;
  transition: background-color 0.3s;
}

.search-bar button:hover {
  background-color: #555;
}

/* Results section */
.results {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 20px;
  margin-top: 30px;
}

/* Book card */
.book-card {
  background: #fff;
  border-radius: 10px;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
  padding: 15px;
  text-align: center;
  transition: transform 0.3s;
}

.book-card:hover {
  transform: translateY(-5px);
}

.book-card img {
  max-width: 100%;
  height: auto;
  border-radius: 5px;
}

.book-card h3 {
  font-size: 1.1rem;
  margin: 10px 0 5px;
}

.book-card p {
  color: #555;
  font-size: 0.9rem;
  margin: 0;
}

/* Pagination styles */
.pagination {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 10px;
  margin: 20px 0;
}

.pagination button {
  padding: 10px 15px;
  font-size: 16px;
  border: none;
  border-radius: 5px;
  background-color: #333;
  color: #fff;
  cursor: pointer;
  transition: background-color 0.3s;
}

.pagination button:hover {
  background-color: #555;
}

.pagination button[disabled] {
  background-color: #aaa;
  cursor: not-allowed;
}

Step 3: Writing the JavaScript

The JavaScript handles fetching data from the Open Library API, rendering results, and managing pagination.

// Select DOM elements
const searchInput = document.getElementById("search-input");
const searchButton = document.getElementById("search-button");
const resultsContainer = document.getElementById("results");

// Variables for pagination
let currentPage = 1;
let booksPerPage = 25; // 5 rows x 5 books
let booksData = [];


// Function to fetch books
async function fetchBooks(query) {
    const apiUrl = `https://openlibrary.org/search.json?q=${encodeURIComponent(query)}`;
    try {
      const response = await fetch(apiUrl);
      if (!response.ok) throw new Error("Failed to fetch data");
      const data = await response.json();
      booksData = data.docs;
      currentPage = 1; // Reset to first page on new search
      displayBooks();
      displayPagination();
    } catch (error) {
      resultsContainer.innerHTML = `<p>Error: ${error.message}</p>`;
    }
  }
  
  // Function to display books based on current page
  function displayBooks() {
    resultsContainer.innerHTML = "";
    const start = (currentPage - 1) * booksPerPage;
    const end = start + booksPerPage;
    const booksToShow = booksData.slice(start, end);
  
    if (booksToShow.length === 0) {
      resultsContainer.innerHTML = "<p>No books found.</p>";
      return;
    }
  
    booksToShow.forEach((book) => {
      const bookCover = book.cover_i
        ? `https://covers.openlibrary.org/b/id/${book.cover_i}-M.jpg`
        : "https://via.placeholder.com/150x200?text=No+Image";
      const bookElement = document.createElement("div");
      bookElement.className = "book-card";
      bookElement.innerHTML = `
        <img src="${bookCover}" alt="Book Cover">
        <h3>${book.title}</h3>
        <p>${book.author_name ? book.author_name.join(", ") : "Unknown Author"}</p>
      `;
      resultsContainer.appendChild(bookElement);
    });
  }

// Function to display pagination controls
function displayPagination() {
    const totalPages = Math.ceil(booksData.length / booksPerPage);
    const paginationContainer = document.getElementById("pagination");
    paginationContainer.innerHTML = "";
  
    // Previous button
    const prevButton = document.createElement("button");
    prevButton.textContent = "Previous";
    prevButton.disabled = currentPage === 1;
    prevButton.addEventListener("click", () => {
      if (currentPage > 1) {
        currentPage--;
        displayBooks();
        displayPagination();
      }
    });
    paginationContainer.appendChild(prevButton);
  
    // Page numbers
    for (let i = 1; i <= totalPages; i++) {
      const pageButton = document.createElement("button");
      pageButton.textContent = i;
      pageButton.disabled = i === currentPage;
      pageButton.addEventListener("click", () => {
        currentPage = i;
        displayBooks();
        displayPagination();
      });
      paginationContainer.appendChild(pageButton);
    }
  
    // Next button
    const nextButton = document.createElement("button");
    nextButton.textContent = "Next";
    nextButton.disabled = currentPage === totalPages;
    nextButton.addEventListener("click", () => {
      if (currentPage < totalPages) {
        currentPage++;
        displayBooks();
        displayPagination();
      }
    });
    paginationContainer.appendChild(nextButton);
  }

// Event listener for search button
searchButton.addEventListener("click", () => {
  const query = searchInput.value.trim();
  if (!query) {
    resultsContainer.innerHTML = "<p>Please enter a search term.</p>";
    return;
  }
  fetchBooks(query);
});

Explanation of the Code

  1. Fetching Data: The fetchBooks function retrieves book data from the Open Library API based on the user’s query. The data is stored in the booksData array.
  2. Displaying Books: The displayBooks function calculates which books to show on the current page and dynamically creates book cards.
  3. Pagination: The displayPagination function generates navigation buttons and updates the currentPage when a button is clicked.
  4. Event Handling: The search button listens for clicks and triggers a fetch request with the input query.

Final Output

The app displays books in a grid format, 25 books per page (5 rows with 5 books each). Each book card includes the book’s cover image at the top, followed by the title and the author(s). If the book cover is unavailable, a placeholder image is shown instead. Users can navigate between pages using the pagination controls at the bottom, which include Previous, Next, and numbered page buttons. The UI dynamically updates the displayed books and pagination controls based on the current page, ensuring a smooth and interactive experience.


API Reference

  1. Open Library API
  2. Open Library Search API
  3. Open Library Book Cover API

Back to Articles