How to Implement a Testimonial Slider with Navigation Controls in Vanilla JavaScript

You can implement a testimonial slider with navigation controls using three self-contained files—HTML, CSS, and JavaScript—that store testimonial data in an array, cache DOM references, and update the display via modulo arithmetic when users click previous or next buttons.

The testimonial slider component from the jisan-mia/dom-projects repository provides a lightweight, dependency-free solution for displaying customer reviews with manual navigation. This guide explains how to implement testimonial slider with navigation controls by examining the actual source code structure and logic used in the projects/testimonial-slider directory.

Project Architecture and File Organization

The implementation follows a strict separation of concerns across three files:

  • index.html – Defines the semantic markup, placeholder elements for dynamic content, and the navigation arrow controls.
  • style.css – Handles absolute positioning of the navigation controls, responsive breakpoints, and hover states.
  • script.js – Contains the data model, DOM manipulation logic, and event listeners for the previous and next functionality.

All files are linked in the HTML head and body without requiring build tools or external dependencies beyond Font Awesome for the quote icon.

HTML Structure for the Testimonial Container

The markup establishes a container with absolutely positioned navigation arrows and placeholder elements that JavaScript populates at runtime.

<section class="main-section">
  <div class="title">Testimonials</div>

  <!-- Navigation controls -->
  <div class="indicators">
    <div class="prev"><</div>
    <div class="next">></div>
  </div>

  <!-- Dynamic content area -->
  <div class="testimonial-container">
    <i class="fas fa-quote-left fa-2x quote"></i>
    <p class="testimonial-text"></p>
    <hr class="hr-line" />
    <div class="prfile-info">
      <div class="img-container">
        <img class="profile-img" src="" alt="profile" />
      </div>
      <div class="info-container">
        <h4 class="name"></h4>
        <p class="job-title"></p>
      </div>
    </div>
  </div>
</section>

The empty text nodes and image source attributes are filled programmatically when the page loads and when navigation events occur.

CSS Positioning and Styling for Navigation Controls

The navigation arrows are positioned absolutely within a flex container to sit horizontally across the testimonial card, with hover states providing visual feedback.

.indicators {
  position: absolute;
  width: 65%;
  display: flex;
  justify-content: space-between;
  align-items: center;
  z-index: 10;
}

.prev,
.next {
  font-size: 30px;
  padding: 5px 6px;
  background-color: rgba(224, 227, 238, 0.75);
  cursor: pointer;
  transition: all 0.3s ease-in-out;
}

.prev:hover,
.next:hover {
  background-color: rgba(202, 202, 226, 0.86);
}

The stylesheet also includes a media query at lines 136-140 in style.css that reduces the indicator container width on small screens to maintain usability on mobile devices.

JavaScript Logic for Slider Navigation

The behavior layer is implemented entirely in script.js using vanilla DOM APIs. The logic breaks down into three distinct phases: data definition, element caching, and event-driven updates.

Defining the Testimonial Data Array

Testimonials are stored as an array of objects containing id, name, job, img, and text properties.

const testimonials = [
  {
    id: 1,
    name: "Jason smith",
    job: "Web Developer",
    img: "https://path-to-image.jpg",
    text: "Working with this team has been an incredible experience..."
  },
  {
    id: 2,
    name: "anna johnson",
    job: "web designer",
    img: "https://path-to-image.jpg",
    text: "The attention to detail and creative solutions provided exceeded expectations..."
  }
  // Additional objects follow...
];

Caching DOM Elements and Initial Render

To optimize performance, the script queries DOM elements once on initialization and stores them in constants. The showTestimonials function updates these cached elements when called.

const name = document.querySelector(".name");
const jobTitle = document.querySelector(".job-title");
const img = document.querySelector(".profile-img");
const txt = document.querySelector(".testimonial-text");
const prevBtn = document.querySelector(".prev");
const nextBtn = document.querySelector(".next");

let currentItem = 0;

// Initialize first testimonial on page load
window.addEventListener("DOMContentLoaded", () => {
  showTestimonials(currentItem);
});

function showTestimonials(index) {
  const item = testimonials[index];
  name.textContent = item.name;
  jobTitle.textContent = item.job;
  img.src = item.img;
  txt.textContent = item.text;
}

Implementing Previous and Next Navigation

The navigation controls use modulo arithmetic to create infinite circular scrolling through the testimonial array.

// Next button increments with wrap-around
nextBtn.addEventListener("click", () => {
  currentItem = (currentItem + 1) % testimonials.length;
  showTestimonials(currentItem);
});

// Previous button decrements with wrap-around
prevBtn.addEventListener("click", () => {
  currentItem = (currentItem - 1 + testimonials.length) % testimonials.length;
  showTestimonials(currentItem);
});

The modulo operator (% testimonials.length) ensures that clicking "next" on the last item returns to index 0, while the adjusted formula for "prev" prevents negative indices by adding the array length before applying the modulo.

How the Circular Navigation Works

The slider implements circular navigation without boundary checks or conditional logic. When the user clicks the next button, the expression (currentItem + 1) % testimonials.length mathematically wraps the index back to zero after reaching the final array position. Conversely, the previous button uses (currentItem - 1 + testimonials.length) % testimonials.length to avoid negative numbers and wrap from the first item to the last. This approach eliminates if/else blocks and ensures consistent performance regardless of the testimonial count.

Summary

  • Separate concerns by dividing structure (index.html), presentation (style.css), and behavior (script.js) into dedicated files.
  • Cache DOM references once at initialization rather than querying inside event handlers to minimize reflow costs.
  • Use modulo arithmetic for navigation logic to create seamless circular scrolling without conditional branching.
  • Position controls absolutely within a flex container to overlay navigation arrows on the testimonial card.
  • Implement responsive adjustments via CSS media queries to maintain touch-friendly spacing on mobile devices.

Frequently Asked Questions

How do I add more testimonials to the slider?

Add new objects to the testimonials array in script.js following the existing schema with name, job, img, and text properties. The navigation logic automatically adapts to the new array length via the modulo operations in the event listeners.

Can this testimonial slider work without jQuery or other libraries?

Yes. The implementation uses only vanilla JavaScript methods including document.querySelector, addEventListener, and template literals. The only external dependency is Font Awesome for the quotation icon, which can be replaced with an SVG or text character if preferred.

How does the navigation handle the first and last items?

The navigation uses modulo arithmetic to create circular behavior. When viewing the first item and clicking "prev", the calculation (currentItem - 1 + testimonials.length) % testimonials.length evaluates to the last index. When viewing the last item and clicking "next", (currentItem + 1) % testimonials.length returns to index zero.

Is the testimonial slider responsive for mobile devices?

Yes. The style.css file includes a media query at lines 136-140 that reduces the .indicators container width to 90% on small screens, ensuring the navigation arrows remain accessible without overflowing the viewport. The flexbox layout and relative units allow the content to scale appropriately across device sizes.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →