Reputation: 1
Having issues with creating a multi-card carousel where each card has tabs for displaying additional information.
I'm trying to create a carousel with multiple cards, which I figured out how to do alright I believe, but now I'm having issues because I have to add tabs to the top of each card with each tab showing different information. Whenever I click on a card that has tabs, the entire carousel collapses to the last slide and even if the first card isn't clicked, it will never cycle back to the beginning. I've tried just about every solution I can possibly find and am about to rip my hair out...
Ideally I would like to have the event listener tied to arrows to navigate the cards, make it easier to navigate on mobile by enabling swiping, and have an option for being able to add more than 5 cards. However, I'd just be happy with being able to cycle through the cards continuously and for the tabs to work correctly. I do not want this to autoplay. The variable gradient backgrounds are something I would like to keep as well as the tabs, I haven't started the pedigree section of the tabs but was planning on laying it out with divs instead of making a table. This does need to be something where the actual content is easily editable and that can handle having a Lightbox/modal triggered when the card's image is clicked to display an image slideshow.
This is a link that shows the problems that I'm having: Codepen
HTML
<div class="carousel">
<div class="carousel__list">
<div class="carousel__card" data-pos="-2">1</div>
<div class="carousel__card" data-pos="-1">
</div>
<div class="carousel__card" data-pos="0">
<div class="puppy">
<nav>
<div class="nav nav-tabs" id="nav-tab" role="tablist">
<button class="nav-link active" id="nav-details-tab" data-bs-toggle="tab" data-bs-target="#details03" type="button" role="tab" aria-controls="nav-details" aria-selected="true">Details</button>
<button class="nav-link" id="nav-description-tab" data-bs-toggle="tab" data-bs-target="#description03" type="button" role="tab" aria-controls="nav-description" aria-selected="false">Description</button>
<button class="nav-link" id="nav-pedigree-tab" data-bs-toggle="tab" data-bs-target="#pedigree03" type="button" role="tab" aria-controls="nav-pedigree" aria-selected="false">Pedigree</button>
</div>
</nav>
<div class="tab-content" id="nav-tabContent">
<div class="tab-pane fade show active" id="details03" role="tabpanel" aria-labelledby="nav-details-tab">
<div class="row">
<div class="col-8">
<div class="card-block">
<h4 class="card-title">Name Here</h4>
<p class="card-text">
Puppy information goes here...
</p>
<p class="card-text">Currently have several puppies and young adults available!</p>
<br>
</div>
</div>
<div class="col-4">
<img src="http://www.goodwinshelties.com/available/companions/noa004.jpg" alt="noa" class="saleimage">
<br>
<div class="price">
Noa is $2800 with AKC Limited Registration on a neuter agreement.
</div>
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="description03" role="tabpanel" aria-labelledby="nav-description-tab">...</div>
<div class="tab-pane fade" id="pedigree03" role="tabpanel" aria-labelledby="nav-pedigree-tab">...</div>
</div>
</div>
<div class="carousel__card" data-pos="1">4</div>
<div class="carousel__card" data-pos="2">5</div>
</div>
</div>
CSS
html,
body {
padding: 0;
margin: 0;
background: #102331;
}
html {
height: 100vh;
}
body {
height: 100vh;
}
.carousel {
display: flex;
width: 100%;
height: 100%;
align-items: center;
font-family: "Quicksand", sans serif;
}
.carousel__list {
display: flex;
list-style: none;
position: relative;
width: 100%;
height: 400px;
justify-content: center;
perspective: 400px;
}
.carousel__card {
display: flex;
align-items: center;
justify-content: center;
font-size: 0px;
width: 700px;
height: 380px;
border-radius: 12px;
box-shadow: 0px 2px 8px 0px rgba(50, 50, 50, 0.5);
position: absolute;
transition: all .3s ease-in;
}
.carousel__card:nth-child(1) {
background: linear-gradient(45deg, #2D35EB 0%, #904ED4 100%);
}
.carousel__card:nth-child(2) {
background: linear-gradient(45deg, #2D35EB 0%, #22c1c3 100%);
}
.carousel__card:nth-child(3) {
background: linear-gradient(-150deg, #a2d2e9, #ada2e9 100%);
}
.carousel__card:nth-child(4) {
background: linear-gradient(45deg, #2D35EB 0%, #22c1c3 100%);
}
.carousel__card:nth-child(5) {
background: linear-gradient(45deg, #22c1c3 0%, #904ED4 100%);
}
.carousel__card[data-pos="0"] {
z-index: 5;
}
.carousel__card[data-pos="-1"],
.carousel__card[data-pos="1"] {
opacity: 0.7;
filter: blur(1px) grayscale(10%);
}
.carousel__card[data-pos="-1"] {
transform: translateX(-20%) scale(.9);
z-index: 4;
}
.carousel__card[data-pos="1"] {
transform: translateX(20%) scale(.9);
z-index: 4;
}
.carousel__card[data-pos="-2"],
.carousel__card[data-pos="2"] {
opacity: 0.4;
filter: blur(3px) grayscale(20%);
}
.carousel__card[data-pos="-2"] {
transform: translateX(-40%) scale(.8);
z-index: 3;
}
.carousel__card[data-pos="2"] {
transform: translateX(40%) scale(.8);
z-index: 3;
}
}
.card {
font-size: 16px;
color: #102331;
font-family: "Quicksand", sans-serif;
overflow: hidden;
border: none;
border-radius: .28571429rem;
box-shadow: 0 1px 3px 0 #d4d4d5, 0 0 0 1px #d4d4d5;
margin-top: 0px;
height: 380px;
position: relative;
width: inherit;
background: inherit;
border-radius: 12px;
}
.puppy {
background: linear-gradient(-150deg, #a2d2e9, #ada2e9 100%);
width: 700px;
height: 380px;
border-radius: 12px;
}
.col-8, .col-4 {
height: 330px;
}
.card-block {
font-size: 16px;
position: relative;
top: 5px;
padding: 10px;
margin-top: 0px;
line-height: 17px;
border: none;
box-shadow: none;
height: auto;
background: transparent;
}
.col-4 .saleimage {
width: 300px;
margin-left: -80px;
height: 250px;
border: 1px solid #2d35eb;
border-radius: 8px;
margin-top: 10px;
margin-bottom: 10px;
}
.saleimage img {
height: 250px;
width: 300px;
border: 1px solid #2d35eb;
}
.carousel__card .block {
font-size: 16px;
top: 5px;
padding: 5px;
margin-top: 0px;
line-height: 17px;
border: none;
box-shadow: none;
height: auto;
}
.price {
font-size: 18px;
font-weight: 700px;
top: 5px;
padding: 5px;
margin-top: 0px;
margin-left: -80px;
line-height: 17px;
text-align: center;
color: #2d35eb;
border: none;
box-shadow: none;
height: auto;
}
.row {
width: 100%;
height: auto;
background: transparent;
}
.card-text {
color: #102331;
font-size: 16px;
font-family: "Quicksand", sans-serif;
text-align: justify;
padding-right: 10px;
width: 85%;
padding-top: 5px;
}
.card-block h4 {
letter-spacing: 8px;
font-size: 20pt;
font-weight: 400;
font-family: "Quicksand", sans-serif;
line-height: 22px;
position: relative;
background-image: linear-gradient(
180deg,
hsl(98 100% 62%),
hsl(204 100% 59%));
background-image: -webkit-linear-gradient(
180deg,
hsl(98 100% 62%),
hsl(204 100% 59%));
margin-top: 5px;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
background-size: 100%;
background-repeat: repeat;
background-position: 0 0;
text-shadow: 0 1px 3px 0 #d4d4d5, 0 0 0 1px #d4d4d5;
}
.carousel-arrow {
position: absolute;
display: flex;
top: 0;
bottom: 0;
margin: auto;
height: 4rem;
background-color: white;
border: none;
width: 2rem;
font-size: 3rem;
padding: 0;
cursor: pointer;
opacity: 0.5;
transition: opacity 100ms;
}
.carousel-arrow:hover,
.carousel-arrow:focus {
opacity: 1;
}
#carousel-arrow-prev {
left: -100px;
padding-left: 0.25rem;
border-radius: 0 2rem 2rem 0;
}
#carousel-arrow-next {
right: -100px;
padding-right: 0.25rem;
border-radius: 2rem 0 0 2rem;
}
.content {
margin: 0px auto;
text-align: left;
color: #666;
font-size: 13px;
line-height: 20px;
position: relative;
height: 220px;
}
.content .item {
opacity: 0;
visibility: hidden;
transition: all 0.3s;
position: absolute;
padding: 10px 20px;
}
.content p {
margin: 10px 0;
}
.nav {
color: #102331;
background: linear-gradient(-150deg, #a2d2e9, #ada2e9 100%);
border-radius: 7px;
height: 45px;
border-bottom: 1px solid #102331;
position: relative;
top: 0px;
}
.nav .navtab {
background: linear-gradient(-150deg, #a2d2e9, #ada2e9 100%);
font-size: 16pt;
border: 1px solid #102331;
}
#nav-tab {
border-bottom: 1px solid #102331;
}
.nav-link:enabled {
font-size: 18px;
}
#nav-details-tab:enabled {
background: linear-gradient(45deg, #a2d2e9 0%, #22c1c3 100%);
color:#102331;
border: 1px solid #102331;
}
#nav-details-tab:hover, #nav-details-tab:active {
background: linear-gradient(45deg, #a2d2e9 0%, #22c1c3 100%);
color:#102331;
border: 1px solid #102331;
}
#nav-description-tab:enabled {
background: linear-gradient(-150deg, #a2d2e9, #ada2e9 100%);
border-top: 1px solid #a2d2e9;
border-left: 1px solid #a2d2e9;
border-right: 1px solid #a2d2e9;
border-bottom: 1px solid #102331;
filter: opacity(0.6);
}
#nav-description-tab:active {
background: linear-gradient(45deg, #a2d2e9 0%, #22c1c3 100%);
color:#102331;
border: 1px solid #102331;
filter: opacity(0.3);
}
#nav-description-tab:hover, #nav-description-tab:active {
border: 1px solid #102331;
background: linear-gradient(45deg, #a2d2e9 0%, #22c1c3 100%);
filter: opacity(0.5);
}
#nav-pedigree-tab:enabled {
background: linear-gradient(-150deg, #a2d2e9, #ada2e9 100%);
border-top: 1px solid #a2d2e9;
border-left: 1px solid #a2d2e9;
border-right: 1px solid #a2d2e9;
border-bottom: 1px solid #102331;
filter: opacity(0.6);
}
#nav-pedigree-tab:active {
background: linear-gradient(45deg, #a2d2e9 0%, #22c1c3 100%);
color:#102331;
border: 1px solid #102331;
filter: opacity(0.3);
}
#nav-pedigree-tab:hover, #nav-pedigree-tab:active {
border: 1px solid #102331;
background: linear-gradient(45deg, #a2d2e9 0%, #22c1c3 100%);
filter: opacity(0.5);
}
JS
const state = {};
const carouselList = document.querySelector('.carousel__list');
const carouselItems = document.querySelectorAll('.carousel__card');
const elems = Array.from(carouselItems);
carouselList.addEventListener('click', function (event) {
var newActive = event.target;
var isItem = newActive.closest('.carousel__card');
if (!isItem || newActive.classList.contains('carousel__card_active')) {
return;
}
update(newActive);
});
const update = function(newActive) {
const newActivePos = newActive.dataset.pos;
const current = elems.find((elem) => elem.dataset.pos == 0);
const prev = elems.find((elem) => elem.dataset.pos == -1);
const next = elems.find((elem) => elem.dataset.pos == 1);
const first = elems.find((elem) => elem.dataset.pos == -2);
const last = elems.find((elem) => elem.dataset.pos == 2);
current.classList.remove('carousel__card_active');
[current, prev, next, first, last].forEach(item => {
var itemPos = item.dataset.pos;
item.dataset.pos = getPos(itemPos, newActivePos);
});
};
const getPos = function (current, active) {
const diff = current - active;
if (Math.abs(current - active) > 2) {
return -current;
}
return diff;
};
This is a link that illustrates how the carousel works without any content in the card: Codepen Default
I'm guessing that the issue is happening because both the tabs and the carousel have the same event listener, but I haven't been successful in adding or changing the event listener for the script and having it still work. I'm open to any solutions, would love to not have to use React because I don't really understand it (it's been about six years since I've even looked at code and this project has proven to have a steep learning curve), but really just want this to work and any help would be much much much appreciated!!
Upvotes: 0
Views: 160
Reputation: 76
I rewrote slide click handler like this and slide with tabs is working
carouselList.addEventListener('click', function (event) {
var newActive = event.target.closest('.carousel__card');
if (!newActive || newActive.classList.contains('carousel__card_active')) {
return;
}
update(newActive);
});
Codepen full example https://codepen.io/konstantin-agafonov/pen/JjaPKGW
Upvotes: 0