We wrote a custom gallery slider/carousel for our app. We'd like to show the number of dots based on how many tiles are in view. Is this possible? Below is the code. The updated and mounted methods are Phoenix Liveview hooks. We'd like to prevent updating the code when they change their minds about the sizes of the images on mobile.
const GallerySlider: LiveHook = {
mounted() {
const carousel = this.el?.querySelector<HTMLElement>(".carousel")!;
const firstImg = carousel.querySelector("div")!;
const arrowIcons = this.el?.querySelectorAll<HTMLElement>(".wrapper i")!;
const dots = this.el?.querySelector<HTMLElement>(".dots");
const slideCount: number = carousel.children.length;
let isDragStart = false;
let isDragging = false;
let prevPageX: number;
let prevScrollLeft: number;
let positionDiff: number;
// calculate dots
let scrollCount: number = 0;
let isMobile = window.innerWidth >= 0 && window.innerWidth <= 768;
let paginationOffset: number = isMobile ? 1 : 2;
let pagination: number = slideCount - paginationOffset;
if ((!isMobile && slideCount <= 4) || isMobile) {
arrowIcons[0].style.display = "none";
arrowIcons[1].style.display = "none";
} else {
arrowIcons[0].style.display = "block";
arrowIcons[1].style.display = "block";
}
arrowIcons?.forEach((icon) =>
icon.addEventListener("click", (e) => {
e.preventDefault();
const isLeft = icon.id.includes("left");
const firstImgWidth = firstImg.clientWidth + 8;
let scrollEnd = scrollCount + paginationOffset >= slideCount;
carousel.scrollLeft += isLeft ? -firstImgWidth : firstImgWidth;
// add/remove active states from dots
if (!isLeft && !scrollEnd) {
scrollCount += 1;
dots?.children[scrollCount - 1].classList.remove("active");
dots?.children[scrollCount].classList.add("active");
}
if (isLeft && scrollCount > 0) {
scrollCount -= 1;
dots?.children[scrollCount + 1].classList.remove("active");
dots?.children[scrollCount].classList.add("active");
}
}),
);
const autoSlide = () => {
const position =
carousel.scrollLeft - (carousel.scrollWidth - carousel.clientWidth) >
-1;
if (position || carousel.scrollLeft <= 0) return;
positionDiff = Math.abs(positionDiff);
const firstImgWidth = firstImg.clientWidth + 16;
const valDifference = firstImgWidth - positionDiff;
const positionValue =
positionDiff > firstImgWidth / 4 ? valDifference : -positionDiff;
carousel.scrollLeft +=
carousel.scrollLeft > prevScrollLeft ? positionValue : -positionValue;
};
//Dragger
const dragStart = (e: MouseEvent | TouchEvent) => {
isDragStart = true;
prevPageX = "pageX" in e ? e.pageX : e.touches[0].pageX;
prevScrollLeft = carousel.scrollLeft;
};
const dragging = (e: MouseEvent | TouchEvent): void => {
if (!isDragStart) return;
isDragging = true;
positionDiff =
"pageX" in e
? (e.pageX as number) - prevPageX
: e.touches[0].pageX - prevPageX;
carousel.classList.add("dragging");
carousel.scrollLeft = prevScrollLeft - positionDiff;
};
const dragStop = (): void => {
isDragStart = false;
carousel.classList.remove("dragging");
if (!isDragging) return;
// add/remove active states from dots
// positionDiff is negative as we scroll right
let isLeft = Math.sign(positionDiff) > 0;
let scrollEnd = scrollCount + paginationOffset >= slideCount;
if (!isLeft && !scrollEnd) {
scrollCount += 1;
dots?.children[scrollCount - 1].classList.remove("active");
dots?.children[scrollCount].classList.add("active");
}
if (isLeft && scrollCount > 0) {
scrollCount -= 1;
dots?.children[scrollCount + 1].classList.remove("active");
dots?.children[scrollCount].classList.add("active");
}
isDragging = false;
autoSlide();
};
/*********/
/**dots**/
for (let i = 0; i <= pagination && pagination > 0; i++) {
const dot = dots?.appendChild(document.createElement("li"))!;
if (i == 0) dot.classList.add("active");
dot.classList.add("list-inline-item");
dot.classList.add(i.toString());
dot.innerHTML = i.toString();
}
/*********/
carousel.addEventListener("mousedown", dragStart);
carousel.addEventListener("touchstart", dragStart);
document.addEventListener("mousemove", dragging);
carousel.addEventListener("touchmove", dragging);
document.addEventListener("mouseup", dragStop);
carousel.addEventListener("touchend", dragStop);
},
updated() {
const carousel = this.el?.querySelector<HTMLElement>(".carousel")!;
const firstImg = carousel.querySelector("div")!;
const arrowIcons = this.el?.querySelectorAll<HTMLElement>(".wrapper i")!;
const dots = this.el?.querySelector<HTMLElement>(".dots");
const slideCount: number = carousel.children.length;
let isDragStart = false;
let isDragging = false;
let prevPageX: number;
let prevScrollLeft: number;
let positionDiff: number;
// calculate dots
let scrollCount: number = 0;
let isMobile = window.innerWidth >= 0 && window.innerWidth <= 768;
let paginationOffset: number = isMobile ? 1 : 2;
let pagination: number = slideCount - paginationOffset;
if ((!isMobile && slideCount <= 4) || isMobile) {
arrowIcons[0].style.display = "none";
arrowIcons[1].style.display = "none";
} else {
arrowIcons[0].style.display = "block";
arrowIcons[1].style.display = "block";
}
arrowIcons?.forEach((icon) =>
icon.addEventListener("click", (e) => {
e.preventDefault();
const isLeft = icon.id.includes("left");
const firstImgWidth = firstImg.clientWidth + 8;
let scrollEnd = scrollCount + paginationOffset >= slideCount;
carousel.scrollLeft += isLeft ? -firstImgWidth : firstImgWidth;
// add/remove active states from dots
if (!isLeft && !scrollEnd) {
scrollCount += 1;
dots?.children[scrollCount - 1].classList.remove("active");
dots?.children[scrollCount].classList.add("active");
}
if (isLeft && scrollCount > 0) {
scrollCount -= 1;
dots?.children[scrollCount + 1].classList.remove("active");
dots?.children[scrollCount].classList.add("active");
}
}),
);
const autoSlide = () => {
const position =
carousel.scrollLeft - (carousel.scrollWidth - carousel.clientWidth) >
-1;
if (position || carousel.scrollLeft <= 0) return;
positionDiff = Math.abs(positionDiff);
const firstImgWidth = firstImg.clientWidth + 16;
const valDifference = firstImgWidth - positionDiff;
const positionValue =
positionDiff > firstImgWidth / 4 ? valDifference : -positionDiff;
carousel.scrollLeft +=
carousel.scrollLeft > prevScrollLeft ? positionValue : -positionValue;
};
//Dragger
const dragStart = (e: MouseEvent | TouchEvent) => {
console.log("aaaa");
isDragStart = true;
prevPageX = "pageX" in e ? e.pageX : e.touches[0].pageX;
prevScrollLeft = carousel.scrollLeft;
};
const dragging = (e: MouseEvent | TouchEvent): void => {
if (!isDragStart) return;
isDragging = true;
positionDiff =
"pageX" in e
? (e.pageX as number) - prevPageX
: e.touches[0].pageX - prevPageX;
carousel.classList.add("dragging");
carousel.scrollLeft = prevScrollLeft - positionDiff;
};
const dragStop = (): void => {
isDragStart = false;
carousel.classList.remove("dragging");
if (!isDragging) return;
// add/remove active states from dots
// positionDiff is negative as we scroll right
let isLeft = Math.sign(positionDiff) > 0;
let scrollEnd = scrollCount + paginationOffset >= slideCount;
if (!isLeft && !scrollEnd) {
scrollCount += 1;
dots?.children[scrollCount - 1].classList.remove("active");
dots?.children[scrollCount].classList.add("active");
}
if (isLeft && scrollCount > 0) {
scrollCount -= 1;
dots?.children[scrollCount + 1].classList.remove("active");
dots?.children[scrollCount].classList.add("active");
}
isDragging = false;
autoSlide();
};
/*********/
/**dots**/
for (let i = 0; i <= pagination && pagination > 0; i++) {
const dot = dots?.appendChild(document.createElement("li"))!;
if (i == 0) dot.classList.add("active");
dot.classList.add("list-inline-item");
dot.classList.add(i.toString());
dot.innerHTML = i.toString();
}
/*********/
carousel.addEventListener("mousedown", dragStart);
carousel.addEventListener("touchstart", dragStart);
document.addEventListener("mousemove", dragging);
carousel.addEventListener("touchmove", dragging);
document.addEventListener("mouseup", dragStop);
carousel.addEventListener("touchend", dragStop);
},
};
export default GallerySlider;