Issue
I have a Timeline on my Website using SwiperJS and several functions to archieve the desired behavior. My problem is, that these functions conflicting each other.
I use the Timeline to navigate the swiper-slides. when I click a timespan in the timeline the corresponding swiper-slide is supposed to show based on the slideIndex data attribute set for each timespan.
the timeline should have these functionality:
- slideTo the corresponding slide when clicking on a timespan, then set the clicked timespan to active and scroll it to the middle of the viewport.
init: function () {
const timeline = document.querySelectorAll(".timespan");
timeline.forEach(function (timespan) {
timespan.addEventListener("click", function () {
// Remove active class from all timespans
timeline.forEach(function (item) {
item.classList.remove("active");
});
// Add active class to the clicked timespan
timespan.classList.add("active");
// Scroll to the clicked timespan
scrollToTimespan(timespan);
// Slide to the corresponding slide
let slideIndex = parseInt(timespan.dataset.slideIndex);
timelineSwiper.slideTo(slideIndex);
});
});
},
- Set the timespan which is in the middle of the viewport to active. So when a user scrolls the timeline (the timespan have scroll-snap) the centered timespan is the active one and it shows it's corresponding slide.
const timespans = document.querySelectorAll(".timespan");
const scrollContainer = document.querySelector(".about .container");
let scrolling = false;
// Function to check if an element is in the middle of the viewport
function isElementInViewport(element) {
const rect = element.getBoundingClientRect();
const viewportHeight =
window.innerHeight || document.documentElement.clientHeight;
const middleViewport = viewportHeight / 2;
return rect.top <= middleViewport && rect.bottom >= middleViewport;
}
// Function to remove the active class from all timespan elements
function removeActiveClass() {
timespans.forEach((timespan) => {
timespan.classList.remove("active");
});
}
// Function to set the active class to the timespan element in the middle of the viewport
function setActiveClass() {
removeActiveClass();
timespans.forEach((timespan) => {
if (isElementInViewport(timespan)) {
timespan.classList.add("active");
const slideIndex = parseInt(timespan.dataset.slideIndex);
timelineSwiper.slideTo(slideIndex);
}
});
}
// Function to scroll the timespan into view
function scrollToTimespan(timespan) {
timespan.scrollIntoView({
behavior: "smooth",
block: "center",
inline: "center",
});
}
// Call the setActiveClass function initially on window load and on scroll
window.addEventListener("load", setActiveClass);
scrollContainer.addEventListener("scroll", function () {
if (!scrolling) {
scrolling = true;
window.requestAnimationFrame(function () {
setActiveClass();
scrolling = false;
});
}
});
- Set the corresponding timespan to active and scroll it to the middle of the viewport, when dragging the swiper to the next or previous slide.
slideChange: function () {
if (!this.isBeginning && !this.isEnd) {
const timeline = document.querySelectorAll(".timespan");
const activeSlideIndex = timelineSwiper.activeIndex;
// Remove active class from all timespans
timeline.forEach(function (item) {
item.classList.remove("active");
});
// Add active class to the corresponding timespan of the active slide
timeline[activeSlideIndex].classList.add("active");
// Scroll to the corresponding timespan
const activeTimespan = timeline[activeSlideIndex];
scrollToTimespan(activeTimespan);
}
},
As mentioned above these functions conflicting each other. The setActiveClass function restricts me from changing slides based on a click or a drag. Any idears how I could achieve the functionality mentioned above?
Here is The code snippet:
/* --------------- Timeline Swiper --------------- */
const timelineSwiper = new Swiper(".timeline-swiper", {
loop: false,
autoplay: false,
on: {
init: function () {
const timeline = document.querySelectorAll(".timespan");
timeline.forEach(function (timespan) {
timespan.addEventListener("click", function () {
// Remove active class from all timespans
timeline.forEach(function (item) {
item.classList.remove("active");
});
// Add active class to the clicked timespan
timespan.classList.add("active");
// Scroll to the clicked timespan
scrollToTimespan(timespan);
// Slide to the corresponding slide
let slideIndex = parseInt(timespan.dataset.slideIndex);
timelineSwiper.slideTo(slideIndex);
});
});
},
slideChange: function () {
const timeline = document.querySelectorAll(".timespan");
const activeSlideIndex = timelineSwiper.activeIndex;
// Remove active class from all timespans
timeline.forEach(function (item) {
item.classList.remove("active");
});
// Add active class to the corresponding timespan of the active slide
timeline[activeSlideIndex].classList.add("active");
// Scroll to the corresponding timespan
const activeTimespan = timeline[activeSlideIndex];
scrollToTimespan(activeTimespan);
},
},
});
/* --------------- Timeline Scroll --------------- */
const timespans = document.querySelectorAll(".timespan");
const scrollContainer = document.querySelector(".about .container");
let scrolling = false;
// Function to check if an element is in the middle of the viewport
function isElementInViewport(element) {
const rect = element.getBoundingClientRect();
const viewportHeight =
window.innerHeight || document.documentElement.clientHeight;
const middleViewport = viewportHeight / 2;
return rect.top <= middleViewport && rect.bottom >= middleViewport;
}
// Function to remove the active class from all timespan elements
function removeActiveClass() {
timespans.forEach((timespan) => {
timespan.classList.remove("active");
});
}
// Function to set the active class to the timespan element in the middle of the viewport
function setActiveClass() {
removeActiveClass();
timespans.forEach((timespan) => {
if (isElementInViewport(timespan)) {
timespan.classList.add("active");
const slideIndex = parseInt(timespan.dataset.slideIndex);
timelineSwiper.slideTo(slideIndex);
}
});
}
// Function to scroll to a timespan element
function scrollToTimespan(timespan) {
timespan.scrollIntoView({
behavior: "smooth",
block: "center",
inline: "center",
});
}
// Call the setActiveClass function initially on window load and on scroll
window.addEventListener("load", setActiveClass);
scrollContainer.addEventListener("scroll", function () {
if (!scrolling) {
scrolling = true;
window.requestAnimationFrame(function () {
setActiveClass();
scrolling = false;
});
}
});
.section {
position: relative;
width: 100%;
font-family: "Poppins", sans-serif;
overflow: hidden;
}
.about .container {
position: relative;
overflow: hidden;
width: 100%;
padding: 0 1.5rem;
display: grid;
grid-template-columns: 1.05fr 1fr;
overflow-y: scroll;
height: 100vh;
scroll-snap-type: y mandatory; /* For snapping timespan */
scrollbar-width: thin; /* For Firefox */
-ms-overflow-style: none; /* For Internet Explorer and Edge */
}
.about-timeline {
margin: 500px auto;
}
.timespan {
max-width: 450px;
min-height: 100px;
display: flex;
flex-direction: column;
justify-content: center;
text-align: center;
padding: 0 4rem;
cursor: pointer;
border: 2px solid black;
border-radius: 20px;
/* snap when scrolling */
scroll-snap-align: center;
scroll-snap-stop: always;
}
.line {
position: relative;
margin: auto;
width: 2px;
height: 50px;
background-color: black;
}
.timespan.active {
background-color: rgba(0, 255, 0, 0.1);
}
.about-info {
height: 100%;
margin: 4rem
}
.timeline-swiper .swiper-wrapper {
display: flex;
align-items: center;
width: 300px;
}
.info-container {
position: sticky;
top: 50%;
transform: translateY(-50%);
display: flex;
flex-direction: column;
align-items: center;
border: 2px solid black;
background-color: rgba(0, 255, 0, 0.1);
border-radius: 20px;
}
.about .text {
margin: 3rem 2rem;
}
<!-- --------- Swiper Library --------- -->
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/swiper@10/swiper-bundle.min.css"
/>
<section class="about section" id="about">
<div class="container">
<div class="about-timeline">
<!-- First Timespan -->
<div class="timespan active" data-slide-index="0">
Timespan1
</div>
<div class="line"></div>
<!-- Second Timespan -->
<div class="timespan" data-slide-index="1">
Timespan2
</div>
<div class="line"></div>
<!-- Third Timespan -->
<div class="timespan" data-slide-index="2">
Timespan3
</div>
<div class="line"></div>
<!-- Fourth Timespan -->
<div class="timespan" data-slide-index="3">
Timespan4
</div>
<div class="line"></div>
<!-- Fifth Timespan -->
<div class="timespan" data-slide-index="4">
Timespan5
</div>
<div class="line"></div>
<!-- Sixth Timespan -->
<div class="timespan" data-slide-index="5">
Timespan6
</div>
<div class="line"></div>
</div>
<div class="about-info">
<div class="info-container">
<div class="swiper timeline-swiper">
<div class="swiper-wrapper">
<!-- First Timespan -->
<div class="swiper-slide">
<p class="text">
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
Error rerum eaque, praesentium, fugiat repudiandae dolorem
suscipit consequatur neque nisi harum delectus
exercitationem odit expedita tempora ipsum facere, rem hic
laudantium?
</p>
</div>
<!-- Second Timespan -->
<div class="swiper-slide">
<p class="text">
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
Error rerum eaque, praesentium, fugiat repudiandae dolorem
suscipit consequatur neque nisi harum delectus
exercitationem odit expedita tempora ipsum facere, rem hic
laudantium?
</p>
</div>
<!-- Third Timespan -->
<div class="swiper-slide">
<p class="text">
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
Error rerum eaque, praesentium, fugiat repudiandae dolorem
suscipit consequatur neque nisi harum delectus
exercitationem odit expedita tempora ipsum facere, rem hic
laudantium?
</p>
</div>
<!-- Fourth Timespan -->
<div class="swiper-slide">
<p class="text">
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
Error rerum eaque, praesentium, fugiat repudiandae dolorem
suscipit consequatur neque nisi harum delectus
exercitationem odit expedita tempora ipsum facere, rem hic
laudantium?
</p>
</div>
<!-- Fifth Timespan -->
<div class="swiper-slide">
<p class="text">
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
Error rerum eaque, praesentium, fugiat repudiandae dolorem
suscipit consequatur neque nisi harum delectus
exercitationem odit expedita tempora ipsum facere, rem hic
laudantium?
</p>
</div>
<!-- Sixth Timespan -->
<div class="swiper-slide">
<p class="text">
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
Error rerum eaque, praesentium, fugiat repudiandae dolorem
suscipit consequatur neque nisi harum delectus
exercitationem odit expedita tempora ipsum facere, rem hic
laudantium?
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<script src="https://cdn.jsdelivr.net/npm/swiper@10/swiper-bundle.min.js"></script>
Solution
Ok so debounce the scroll event did the trick.
/* --------------- Timeline Swiper --------------- */
const timelineSwiper = new Swiper(".timeline-swiper", {
loop: false,
autoplay: false,
on: {
init: function () {
const timeline = document.querySelectorAll(".timespan");
timeline.forEach(function (timespan) {
timespan.addEventListener("click", function () {
// Remove active class from all timespans
timeline.forEach(function (item) {
item.classList.remove("active");
});
// Add active class to the clicked timespan
timespan.classList.add("active");
// Scroll to the clicked timespan
scrollToTimespan(timespan);
// Slide to the corresponding slide
let slideIndex = parseInt(timespan.dataset.slideIndex);
timelineSwiper.slideTo(slideIndex);
});
});
},
slideChange: function () {
const timeline = document.querySelectorAll(".timespan");
const activeSlideIndex = timelineSwiper.activeIndex;
// Remove active class from all timespans
timeline.forEach(function (item) {
item.classList.remove("active");
});
// Add active class to the corresponding timespan of the active slide
timeline[activeSlideIndex].classList.add("active");
// Scroll to the corresponding timespan
const activeTimespan = timeline[activeSlideIndex];
scrollToTimespan(activeTimespan);
},
},
});
/* --------------- Timeline Scroll --------------- */
const timespans = document.querySelectorAll(".timespan");
const scrollContainer = document.querySelector(".about .container");
let scrolling = false;
// Function to check if an element is in the middle of the viewport
function isElementInViewport(element) {
const rect = element.getBoundingClientRect();
const viewportHeight =
window.innerHeight || document.documentElement.clientHeight;
const middleViewport = viewportHeight / 2;
return rect.top <= middleViewport && rect.bottom >= middleViewport;
}
// Function to remove the active class from all timespan elements
function removeActiveClass() {
timespans.forEach((timespan) => {
timespan.classList.remove("active");
});
}
// Function to set the active class to the timespan element in the middle of the viewport
function setActiveClass() {
removeActiveClass();
timespans.forEach((timespan) => {
if (isElementInViewport(timespan)) {
timespan.classList.add("active");
const slideIndex = parseInt(timespan.dataset.slideIndex);
timelineSwiper.slideTo(slideIndex);
}
});
}
// Function to scroll to a timespan element
function scrollToTimespan(timespan) {
scrolling = true;
timespan.scrollIntoView({
behavior: "smooth",
block: "center",
inline: "center",
});
}
// Call the setActiveClass function initially on window load and on scroll
window.addEventListener("load", setActiveClass);
scrollContainer.addEventListener("scroll", scrollHandler);
scrollContainer.addEventListener("scrollend", () => {
debounce(scrollEndHandler, 100);
});
function scrollEndHandler() {
scrolling = false;
}
function scrollHandler() {
if (!scrolling) {
window.requestAnimationFrame(function () {
setActiveClass();
});
}
}
function debounce(method, delay) {
clearTimeout(method._tId);
method._tId= setTimeout(function(){
method();
}, delay);
}
.section {
position: relative;
width: 100%;
font-family: "Poppins", sans-serif;
overflow: hidden;
}
.about .container {
position: relative;
overflow: hidden;
width: 100%;
padding: 0 1.5rem;
display: grid;
grid-template-columns: 1.05fr 1fr;
overflow-y: scroll;
height: 100vh;
scroll-snap-type: y mandatory; /* For snapping timespan */
scrollbar-width: thin; /* For Firefox */
-ms-overflow-style: none; /* For Internet Explorer and Edge */
}
.about-timeline {
margin: 500px auto;
}
.timespan {
max-width: 450px;
min-height: 100px;
display: flex;
flex-direction: column;
justify-content: center;
text-align: center;
padding: 0 4rem;
cursor: pointer;
border: 2px solid black;
border-radius: 20px;
/* snap when scrolling */
scroll-snap-align: center;
scroll-snap-stop: always;
}
.line {
position: relative;
margin: auto;
width: 2px;
height: 50px;
background-color: black;
}
.timespan.active {
background-color: rgba(0, 255, 0, 0.1);
}
.about-info {
height: 100%;
margin: 4rem
}
.timeline-swiper .swiper-wrapper {
display: flex;
align-items: center;
width: 300px;
}
.info-container {
position: sticky;
top: 50%;
transform: translateY(-50%);
display: flex;
flex-direction: column;
align-items: center;
border: 2px solid black;
background-color: rgba(0, 255, 0, 0.1);
border-radius: 20px;
}
.about .text {
margin: 3rem 2rem;
}
<!-- --------- Swiper Library --------- -->
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/swiper@10/swiper-bundle.min.css"
/>
<section class="about section" id="about">
<div class="container">
<div class="about-timeline">
<!-- First Timespan -->
<div class="timespan active" data-slide-index="0">
Timespan1
</div>
<div class="line"></div>
<!-- Second Timespan -->
<div class="timespan" data-slide-index="1">
Timespan2
</div>
<div class="line"></div>
<!-- Third Timespan -->
<div class="timespan" data-slide-index="2">
Timespan3
</div>
<div class="line"></div>
<!-- Fourth Timespan -->
<div class="timespan" data-slide-index="3">
Timespan4
</div>
<div class="line"></div>
<!-- Fifth Timespan -->
<div class="timespan" data-slide-index="4">
Timespan5
</div>
<div class="line"></div>
<!-- Sixth Timespan -->
<div class="timespan" data-slide-index="5">
Timespan6
</div>
<div class="line"></div>
</div>
<div class="about-info">
<div class="info-container">
<div class="swiper timeline-swiper">
<div class="swiper-wrapper">
<!-- First Timespan -->
<div class="swiper-slide">
<p class="text">
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
Error rerum eaque, praesentium, fugiat repudiandae dolorem
suscipit consequatur neque nisi harum delectus
exercitationem odit expedita tempora ipsum facere, rem hic
laudantium?
</p>
</div>
<!-- Second Timespan -->
<div class="swiper-slide">
<p class="text">
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
Error rerum eaque, praesentium, fugiat repudiandae dolorem
suscipit consequatur neque nisi harum delectus
exercitationem odit expedita tempora ipsum facere, rem hic
laudantium?
</p>
</div>
<!-- Third Timespan -->
<div class="swiper-slide">
<p class="text">
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
Error rerum eaque, praesentium, fugiat repudiandae dolorem
suscipit consequatur neque nisi harum delectus
exercitationem odit expedita tempora ipsum facere, rem hic
laudantium?
</p>
</div>
<!-- Fourth Timespan -->
<div class="swiper-slide">
<p class="text">
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
Error rerum eaque, praesentium, fugiat repudiandae dolorem
suscipit consequatur neque nisi harum delectus
exercitationem odit expedita tempora ipsum facere, rem hic
laudantium?
</p>
</div>
<!-- Fifth Timespan -->
<div class="swiper-slide">
<p class="text">
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
Error rerum eaque, praesentium, fugiat repudiandae dolorem
suscipit consequatur neque nisi harum delectus
exercitationem odit expedita tempora ipsum facere, rem hic
laudantium?
</p>
</div>
<!-- Sixth Timespan -->
<div class="swiper-slide">
<p class="text">
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
Error rerum eaque, praesentium, fugiat repudiandae dolorem
suscipit consequatur neque nisi harum delectus
exercitationem odit expedita tempora ipsum facere, rem hic
laudantium?
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<script src="https://cdn.jsdelivr.net/npm/swiper@10/swiper-bundle.min.js"></script>
Answered By - Louis Eiden
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.