Reputation: 45
I am trying to build a carousel for the header section of a website. I am trying to prevent touch events that enable scrolling past the first element and the last element but I have been unable to do this. I looked up some past answers and tried to use the getBoundingClientRect to get the first element's width and prevent scrolling but this disables the whole scrolling feature.
HTML and PHP. The website is a WordPress website so I had to query the carousel's information from the database.
const prev = document.querySelector('.prev');
const next = document.querySelector('.next');
const track = document.querySelector('.track');
const carouselWidth = document.querySelector('.carousel-container').getBoundingClientRect().width;
let index = 0;
let initialPosition = null;
let moving = false;
let transform = 0;
next.addEventListener('click', () => {
index++;
prev.classList.add('show');
track.style.transform = `translateX(-${index * carouselWidth}px)`;
if (track.offsetWidth - (index * carouselWidth) < carouselWidth) {
next.classList.add('hide');
}
});
prev.addEventListener('click', () => {
index--;
next.classList.remove('hide');
if (index === 0) {
prev.classList.remove('show');
}
track.style.transform = `translateX(-${index * carouselWidth}px)`
})
const gestureStart = (e) => {
initialPosition = e.pageX;
moving = true;
const transformMatrix = window.getComputedStyle(track).getPropertyValue('transform');
if (transformMatrix !== 'none') {
transform = parseInt(transformMatrix.split(',')[4].trim());
}
prev.classList.add('show');
}
const gestureMove = (e) => {
if (moving) {
const currentPosition = e.pageX;
const diff = currentPosition - initialPosition;
track.style.transform = `translateX(${transform + diff}px)`;
}
};
const gestureEnd = (e) => {
moving = false;
}
if (window.PointerEvent) {
window.addEventListener('pointerdown', gestureStart);
window.addEventListener('pointermove', gestureMove);
window.addEventListener('pointerup', gestureEnd);
} else {
window.addEventListener('touchdown', gestureStart);
window.addEventListener('touchmove', gestureMove);
window.addEventListener('touchup', gestureEnd);
window.addEventListener('mousedown', gestureStart);
window.addEventListener('mousemove', gestureMove);
window.addEventListener('mouseup', gestureEnd);
}
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.slider-container {
background-color: #fff;
border-bottom: 1px solid #d0d0d0;
display: block;
height: 120px;
overflow: hidden;
padding: 0;
}
.slider-header {
line-height: 8px;
margin: 10px 0 0;
padding: 0 0 0 15px;
text-transform: uppercase;
}
.slider-header:before {
background-color: #d0d0d0;
content: '';
display: block;
height: 1px;
margin-right: -20px;
position: relative;
top: 6px;
}
.slider-header_text {
background-color: #fff;
display: inline-block;
padding: 0 10px 0 0;
position: relative;
}
.slider {
position: relative;
}
button {
width: 30px;
height: 30px;
border-radius: 50%;
border: 1px solid #aaa;
position: absolute;
top: 50%;
transform: translateY(-50%);
background-color: #F9E929;
cursor: pointer;
}
.carousel-container {
width: 1280px;
margin: auto;
min-height: 100px;
position: relative;
}
.carousel-container .carousel-inner {
overflow: hidden;
}
.carousel-container .track {
display: inline-flex;
transition: transform 0.5s;
touch-action: none;
}
.carousel-container .track .card-container {
width: 256px;
height: 100px;
padding-right: 5px;
}
.carousel-container .track .card-container .card {
border-right: 1px solid #ccc;
display: flex;
justify-content: space-around;
margin-top: 20px;
text-decoration: none;
color: black;
}
.nav .prev {
left: -30px;
display: none;
}
.nav .prev.show {
display: block;
}
.nav .next {
right: -30px;
}
.nav .next.hide {
display: none;
}
.card .img img {
height: auto;
width: auto;
max-width: 88px;
max-height: 59px;
}
.card .info h5 {
padding-left: 5px;
max-width: 150px;
}
@media (min-width: 768px) and (max-width: 1024px) {
.button {
display: none;
}
.carousel-container {
width: 100vw;
}
.carousel-container .track .card-container {
width: 25vw;
}
}
@media(max-width: 500px) {
.slider-container {
border-bottom: none;
height: 200px;
margin-bottom: 50px;
}
.slider-header {
display: none;
}
button {
display: none;
}
.carousel-container {
width: 100vw;
min-height: 200px;
}
.carousel-container .track .card-container {
width: 40vw;
height: 200px;
}
.card .img img {
width: 100%;
height: 100%;
position: relative;
}
.carousel-container .track .card-container .card {
border-right: none;
display: block;
position: relative;
margin-top: 20px;
margin-left: 5px;
}
.card .info h5 {
padding-left: 0;
max-width: 100%;
font-weight: 700;
}
}
<div class="slider-container">
<h4 class="slider-header">
<span class="slider-header_text">Trending</span>
</h4>
<div class="slider">
<div class="carousel-container">
<div class="carousel-inner">
<div class="track">
<?php
global $wpdb;
$query = new WP_Query(array(
'posts_per_page' => 12,
'category_name' => 'posts',
));
$posts = $query -> posts;
foreach ($posts as $post) {
echo '
<div class="card-container">
<a href="'.$post->guid.'" class="card">
<div class="img">
<img src="'.get_the_post_thumbnail_url( $post -> ID, 'thumbnail' ).'" alt="">
</div>
<div class="info">
<h5>'.$post->post_title.'</h5>
</div>
</a>
</div>';
}
?>
</div>
</div>
<div class="nav">
<button class="prev">
<i class="fas fa-angle-left"></i>
</button>
<button class="next">
<i class="fas fa-angle-right"></i>
</button>
</div>
</div>
</div>
</div>
I already did this in my local environment before I moved it online using purely HTML, CSS and Javascript. I posted the code on jsfiddle. It could be accessed through this link https://jsfiddle.net/jqs62tp1/.
Upvotes: 1
Views: 2176
Reputation: 331
Not sure if this is exactly what you need but you could try disabling touch-events
via CSS:
/* styles applied to all card-containers except the first and last */
.card-container:not(:first-child),
.card-container:not(:last-child) {
touch-action: none;
}
More details https://css-tricks.com/almanac/properties/t/touch-action/
Upvotes: 1