
Adding an interactive and visually engaging history timeline to your website can captivate your audience and effectively showcase your milestones. This blog post will walk you through creating a dynamic timeline component using JavaScript, CSS, and HTML.
Why Use a History Timeline?
A history timeline organizes your content chronologically, making it easy for users to explore your journey. By incorporating interactive features like clickable events and corresponding images, you can further engage your audience while delivering information intuitively.
Step 1: HTML Structure for the Timeline
To start, create a basic structure for the timeline. Below is a generic, reusable structure for any history timeline:
<section class="history-section">
<div class="container">
<div class="heading-3 text-center">
History Timeline:<br>
<span>A proven journey of innovation and milestones</span>
</div>
<div class="history-row">
<div class="history-col">
<div class="history-img-group">
<img src="path-to-your-image/history-1960s.webp" alt="History - 1960s" class="img-fluid" style="position: absolute;">
<img src="path-to-your-image/history-1968.png" alt="History - 1968" class="img-fluid" style="position: absolute;">
<img src="path-to-your-image/history-late-1970s.webp" alt="History - Late 1970s to Early 2000s" class="img-fluid d-block" style="position: absolute;">
<img src="path-to-your-image/history-2015.webp" alt="History - 2015" class="img-fluid" style="position: absolute;">
<img src="path-to-your-image/history-2018.webp" alt="History - 2018" class="img-fluid" style="position: absolute;">
<img src="path-to-your-image/history-today.webp" alt="History - Today" class="img-fluid" style="position: absolute;">
</div>
</div>
<div class="history-col">
<nav class="history-nav">
<div class="history-nav-inner">
<div class="history-item" style="user-select: none;">
<strong>1960s</strong>
<p><strong>Dr. Pioneer:</strong><br>
Dedicated to innovating better solutions during this decade.</p>
</div>
<div class="history-item" style="user-select: none;">
<strong>1968</strong>
<p><strong>The First Breakthrough:<br>
</strong>An invention that laid the groundwork for future success globally.</p>
</div>
<div class="history-item history-item-current" style="user-select: none;">
<strong>Late 1970s - early 2000s</strong>
<p><strong>Challenges and Resilience:<br>
</strong>Overcoming market challenges and refining solutions for modern needs.</p>
</div>
<div class="history-item" style="user-select: none;">
<strong>2015</strong>
<p><strong>Legacy Revisited:<br>
</strong>A renewed focus on the original innovation, leading to advancements.</p>
</div>
<div class="history-item" style="user-select: none;">
<strong>2018</strong>
<p><strong>Modern Innovations:<br>
</strong>Bringing the legacy product into the modern era with updated designs.</p>
</div>
<div class="history-item" style="user-select: none;">
<strong>Today</strong>
<p><strong>Shaping the Future:</strong> Setting new standards with innovation that prioritizes user needs.</p>
</div>
</div>
</nav>
</div>
</div>
<p class="text-center text-smaller w-50 mx-auto">
Note: All benefits are based on ongoing validations and reviews. Content is not intended for marketing or as medical advice. </p>
</div>
</section>
This structure includes:
- A navigation panel (
history-nav
) listing key milestones. - A corresponding image group (
history-img-group
) to highlight each event visually.
Step 2: CSS for Styling
Here’s a snippet of CSS for styling your timeline component:
.history {
background-color: $color-primary;
color: $color-white;
padding: 80px 0;
.heading-3 {
color: $color-secondary;
span {
font-family: $font-family-sub-heading;
font-size: 24px;
font-weight: 400;
}
}
.history-row {
align-items: center;
margin: 80px 0;
}
.history-col {
flex: 0 0 calc(35% - 40px);
&:nth-child(2) {
flex: 0 0 calc(65% - 40px);
}
}
.history-img-group {
height: 400px;
position: relative;
@media(max-width: 768px) {
height: 360px;
}
img {
border-radius: 45px;
display: none;
left: 0;
position: absolute;
top: 0;
}
}
.history-nav {
max-height: 600px;
overflow: hidden;
scroll-behavior: smooth;
@media (max-width: 768px) {
margin-top: 40px;
}
}
.history-nav-inner {
display: flex;
flex-direction: column;
}
.history-item {
align-items: center;
color: rgba($color-white, 0.55);
cursor: pointer;
display: flex;
font-family: $font-family-heading;
height: 120px;
text-decoration: none;
@media (max-width: 768px) {
display: block;
height: auto;
text-align: center;
}
p {
color: rgba($color-white, 0.55);
font-family: $font-family-sub-heading;
font-size: 16px;
}
> strong {
flex: 0 0 320px;
font-size: 24px;
text-align: center;
}
&-current {
p, strong {
color: $color-white;
transition: color 500ms ease-in-out;
}
}
}
}
This CSS ensures the timeline is visually appealing and responsive.
Step 3: JavaScript for Interactivity
To make the timeline interactive, use the following JavaScript:
document.addEventListener("DOMContentLoaded", function () {
const historyNav = document.querySelector(".history-nav");
const historyNavInner = document.querySelector(".history-nav-inner");
const historyImagesContainer = document.querySelector(".history-img-group");
let historyItems = Array.from(document.querySelectorAll(".history-item"));
let historyImages = Array.from(document.querySelectorAll(".history-img-group img"));
if (!historyNavInner || !historyImages.length || !historyItems.length || !historyImagesContainer) {
console.error("Required elements are missing. Please check the HTML structure.");
return;
}
function centerItem(item) {
const itemTop = item.getBoundingClientRect().top;
const navTop = historyNav.getBoundingClientRect().top;
const navHeight = historyNav.clientHeight;
const itemHeight = item.offsetHeight;
const offset = itemTop - navTop - navHeight / 2 + itemHeight / 2;
historyNav.scrollTop += offset;
}
function prependItems() {
const navFragment = document.createDocumentFragment();
const imagesFragment = document.createDocumentFragment();
historyItems.forEach((item) => {
const clone = item.cloneNode(true);
clone.classList.remove("history-item-current");
clone.addEventListener("click", onItemClick);
navFragment.appendChild(clone);
});
historyImages.forEach((img) => {
const clone = img.cloneNode(true);
clone.classList.remove("d-block");
imagesFragment.appendChild(clone);
});
historyNavInner.insertBefore(navFragment, historyNavInner.firstChild);
historyImagesContainer.insertBefore(imagesFragment, historyImagesContainer.firstChild);
historyItems = Array.from(historyNavInner.querySelectorAll(".history-item"));
historyImages = Array.from(historyImagesContainer.querySelectorAll("img"));
historyNavInner.scrollTop += historyNavInner.scrollHeight / 2;
}
function appendItems() {
const navFragment = document.createDocumentFragment();
const imagesFragment = document.createDocumentFragment();
historyItems.forEach((item) => {
const clone = item.cloneNode(true);
clone.classList.remove("history-item-current");
clone.addEventListener("click", onItemClick);
navFragment.appendChild(clone);
});
historyImages.forEach((img) => {
const clone = img.cloneNode(true);
clone.classList.remove("d-block");
imagesFragment.appendChild(clone);
});
historyNavInner.appendChild(navFragment);
historyImagesContainer.appendChild(imagesFragment);
historyItems = Array.from(historyNavInner.querySelectorAll(".history-item"));
historyImages = Array.from(historyImagesContainer.querySelectorAll("img"));
}
function onItemClick() {
const item = this;
const isCurrent = item.classList.contains("history-item-current");
historyItems.forEach((i) => i.classList.remove("history-item-current"));
item.classList.add("history-item-current");
const index = historyItems.indexOf(item);
historyImages.forEach((img) => img.classList.remove("d-block"));
historyImages[index % historyImages.length].classList.add("d-block");
if (!isCurrent) {
centerItem(item);
}
if (index === 0) {
prependItems();
centerItem(historyItems[Math.floor((historyItems.length + 1) / 2)]);
} else if (index === historyItems.length - 1) {
appendItems();
centerItem(historyItems[Math.floor((historyItems.length - 1) / 2)]);
}
}
historyItems.forEach((item) => {
item.addEventListener("click", onItemClick);
item.style.userSelect = "none";
item.addEventListener("mousedown", (e) => e.preventDefault());
});
const initialActiveIndex = Math.floor(historyItems.length / 3);
historyItems.forEach((item, index) => {
if (index === initialActiveIndex) {
item.classList.add("history-item-current");
historyImages.forEach((img) => img.classList.remove("d-block"));
historyImages[index].classList.add("d-block");
}
});
centerItem(historyItems[initialActiveIndex]);
window.addEventListener("resize", () => {
const historyRow = document.querySelector(".history-row");
if (window.innerWidth < 768) {
document.querySelectorAll(".history-img-group img").forEach((img) => {
img.style.position = "static";
});
} else {
document.querySelectorAll(".history-img-group img").forEach((img) => {
img.style.position = "absolute";
});
}
});
window.dispatchEvent(new Event("resize"));
});
This script handles:
- Clicking an event in the timeline to show the associated image.
- Automatically setting the first milestone as active.
Benefits of This Timeline
- Interactive User Experience: Allows users to explore milestones in a dynamic way.
- Visual Storytelling: Combines text and visuals to better communicate your history.
- Customizable: Easy to adapt to any project or branding.
Conclusion
This history timeline provides an elegant and reusable way to present milestones. With a solid foundation of HTML, CSS, and JavaScript, you can tailor this solution to fit your website’s needs. Let me know if you’d like further assistance or customization!