I want to recreate the netflix slider using tailwind and owlcarousel. It's almost done as I want but I'm facing a problem when I hover the card I apply a scaling up to the card to display details but I actually can't click on them.
Does anyone can help me with this ? here is the fiddle
HTML structure
<section class="mx-auto w-screen relative flex flex-col gap-4 px-5 mb-16">
<div class="owl-carousel owl-theme">
<div class="item group transition-all sm:hover:absolute sm:hover:scale-125 border border-red-600">
<div href="#" class="card bg-neutral-900 rounded-md cursor-pointer overflow-hidden">
<div>
<img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQKSRTtOrtjLaagEC2gTK6VsPq4NEFk_72hhg&s"
alt="">
</div>
<div class="hidden group-hover:flex bg-white p-1.5 flex-col h-fit w-full">
<div class="flex gap-3 sm:group-hover:scale-[.8] origin-top-left w-[calc(100%/0.8)]">
<span class="">
MOVIE TITLE
</span>
</div>
<a href="" class="text-red-600 sm:group-hover:scale-[.8] origin-top-left w-[calc(100%/0.8)]">
try to click me you can't
</a>
</div>
</div>
</div>
<div class="item group transition-all sm:hover:absolute sm:hover:scale-125 border border-red-600 overflow-hidden">
<div href="#" class="card bg-neutral-900 rounded-md cursor-pointer overflow-hidden">
<div>
<img src="https://occ-0-6613-7435.1.nflxso.net/dnm/api/v6/Qs00mKCpRvrkl3HZAN5KwEL1kpE/AAAABQquP3MObc4Xmx8fS1E0EtFypAJFcNs8U73s6OHL2GjPOuIRbKkknfytMjCRkky6eDIe_8LHwBndef1-gQiN3IMX5NEqFRShrIi9MKSOMGvoTalZ2GRya1eswfUOXrTJ6Nnflg.webp?r=bb3"
alt="">
</div>
<div class="hidden group-hover:flex bg-white p-1.5 flex-col h-fit w-full">
<div class="flex gap-3 sm:group-hover:scale-[.8] origin-top-left w-[calc(100%/0.8)]">
<span class="">
MOVIE TITLE
</span>
</div>
<a href="" class="text-red-600 sm:group-hover:scale-[.8] origin-top-left w-[calc(100%/0.8)]">
try to click me you can't
</a>
</div>
</div>
</div>
<div class="item group transition-all sm:hover:absolute sm:hover:scale-125 border border-red-600">
<div href="#" class="card bg-neutral-900 rounded-md cursor-pointer overflow-hidden">
<div>
<img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQKSRTtOrtjLaagEC2gTK6VsPq4NEFk_72hhg&s"
alt="">
</div>
<div class="hidden group-hover:flex bg-white p-1.5 flex-col h-fit w-full">
<div class="flex gap-3 sm:group-hover:scale-[.8] origin-top-left w-[calc(100%/0.8)]">
<span class="">
MOVIE TITLE
</span>
</div>
<a href="" class="text-red-600 sm:group-hover:scale-[.8] origin-top-left w-[calc(100%/0.8)]">
try to click me you can't
</a>
</div>
</div>
</div>
<div class="item group transition-all sm:hover:absolute sm:hover:scale-125 border border-red-600">
<div href="#" class="card bg-neutral-900 rounded-md cursor-pointer overflow-hidden">
<div>
<img src="https://placecats.com/340/192" alt="">
</div>
<div class="hidden group-hover:flex bg-white p-1.5 flex-col h-fit w-full">
<div class="flex gap-3 sm:group-hover:scale-[.8] origin-top-left w-[calc(100%/0.8)]">
<span class="">
MOVIE TITLE
</span>
</div>
<a href="" class="text-red-600 sm:group-hover:scale-[.8] origin-top-left w-[calc(100%/0.8)]">
try to click me you can't
</a>
</div>
</div>
</div>
<div class="item group transition-all sm:hover:absolute sm:hover:scale-125 border border-red-600">
<div href="#" class="card bg-neutral-900 rounded-md cursor-pointer overflow-hidden">
<div>
<img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQKSRTtOrtjLaagEC2gTK6VsPq4NEFk_72hhg&s"
alt="">
</div>
<div class="hidden group-hover:flex bg-white p-1.5 flex-col h-fit w-full">
<div class="flex gap-3 sm:group-hover:scale-[.8] origin-top-left w-[calc(100%/0.8)]">
<span class="">
MOVIE TITLE
</span>
</div>
<a href="" class="text-red-600 sm:group-hover:scale-[.8] origin-top-left w-[calc(100%/0.8)]">
try to click me you can't
</a>
</div>
</div>
</div>
<div class="item group transition-all sm:hover:absolute sm:hover:scale-125 border border-red-600">
<div href="#" class="card bg-neutral-900 rounded-md cursor-pointer overflow-hidden">
<div>
<img src="https://occ-0-6613-7435.1.nflxso.net/dnm/api/v6/Qs00mKCpRvrkl3HZAN5KwEL1kpE/AAAABQquP3MObc4Xmx8fS1E0EtFypAJFcNs8U73s6OHL2GjPOuIRbKkknfytMjCRkky6eDIe_8LHwBndef1-gQiN3IMX5NEqFRShrIi9MKSOMGvoTalZ2GRya1eswfUOXrTJ6Nnflg.webp?r=bb3"
alt="">
</div>
<div class="hidden group-hover:flex bg-white p-1.5 flex-col h-fit w-full">
<div class="flex gap-3 sm:group-hover:scale-[.8] origin-top-left w-[calc(100%/0.8)]">
<span class="">
MOVIE TITLE
</span>
</div>
<a href="" class="text-red-600 sm:group-hover:scale-[.8] origin-top-left w-[calc(100%/0.8)]">
try to click me you can't
</a>
</div>
</div>
</div>
<div class="item group transition-all sm:hover:absolute sm:hover:scale-125 border border-red-600">
<div href="#" class="card bg-neutral-900 rounded-md cursor-pointer overflow-hidden">
<div>
<img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQKSRTtOrtjLaagEC2gTK6VsPq4NEFk_72hhg&s"
alt="">
</div>
<div class="hidden group-hover:flex bg-white p-1.5 flex-col h-fit w-full">
<div class="flex gap-3 sm:group-hover:scale-[.8] origin-top-left w-[calc(100%/0.8)]">
<span class="">
MOVIE TITLE
</span>
</div>
<a href="" class="text-red-600 sm:group-hover:scale-[.8] origin-top-left w-[calc(100%/0.8)]">
try to click me you can't
</a>
</div>
</div>
</div>
</div>
</section>
Javascript
const addHoverClasses = (event) => {
const owlitemHovered = event.currentTarget
owlitemHovered.classList.add("!z-10")
owlitemHovered.classList.remove("!z-0")
}
const removeHoverClasses = (event) => {
const owlitemHovered = event.currentTarget
owlitemHovered.classList.remove("!z-10")
owlitemHovered.classList.add("!z-0")
}
const getOwlcarouselResponsiveMaxItems = (event) => {
const owlInstance = event.relatedTarget
const currentBreakpoint = owlInstance._breakpoint
const breakpoints = owlInstance.options.responsive
return breakpoints[currentBreakpoint].items
}
const applyTransformOrigin = (event) => {
const owlCarousel = event.target
const owlItems = owlCarousel.querySelectorAll(".owl-item")
let owlItemActiveCounter = 1
owlItems.forEach((owlItem, index) => {
owlItem.classList.add("!z-0")
owlItem.firstChild.classList.remove(
"origin-[left_center]",
"origin-[right_center]",
"origin-center",
)
if (owlItem?.classList.contains("active")) {
if (owlItemActiveCounter === 1) {
owlItem.firstChild.classList.add("origin-[left_center]")
} else if (
owlItemActiveCounter === getOwlcarouselResponsiveMaxItems(event)
) {
owlItem.firstChild.classList.add("origin-[right_center]")
} else {
owlItem.firstChild.classList.add("origin-center")
}
owlItemActiveCounter++
}
})
}
const applyOwlStyles = (event) => {
const owlCarousel = event.target
const owlItems = owlCarousel.querySelectorAll(".owl-item")
owlCarousel.classList.add("relative")
owlCarousel
.querySelector(".owl-stage-outer")
?.classList.add("!overflow-visible", "!overflow-x-clip")
owlItems.forEach((owlItem, index) => {
owlItem.addEventListener("mouseover", addHoverClasses, false)
owlItem.addEventListener("mouseout", removeHoverClasses, false)
})
const owldotsContainer = owlCarousel.querySelector(".owl-dots")
owldotsContainer?.classList.add(
"absolute",
"!-top-4",
"right-0",
"-translate-y-full",
"!flex",
"!gap-2",
"!items-center",
)
const owldots = owldotsContainer?.querySelectorAll(".owl-dot") ?? []
owldots.forEach((olwDot) => {
const span = olwDot.querySelector("span")
span.classList.add("!m-0", "!rounded-none", "!w-4", "!h-1")
span.classList.remove("!bg-primary-600")
if (olwDot.classList.contains("active")) {
span.classList.add("!bg-primary-600")
}
})
const owlNav = owlCarousel?.querySelector(".owl-nav")
owlNav?.classList.add("!m-0", "!top-0", "!bottom-0", "!w-full")
const owlPrev = owlNav?.querySelector(".owl-prev")
const owlNext = owlNav?.querySelector(".owl-next")
owlPrev?.classList.add(
"!absolute",
"!h-full",
"!m-0",
"!bg-black",
"!bg-opacity-60",
"!text-white",
"!text-6xl",
"!font-opensans",
"!left-0",
"!w-[40px]",
"!rounded-none",
"!rounded-r-md",
"-translate-y-full",
)
owlNext?.classList.add(
"!absolute",
"!h-full",
"!m-0",
"!bg-black",
"!bg-opacity-60",
"!text-white",
"!text-6xl",
"!font-opensans",
"!right-0",
"!w-[40px]",
"!rounded-none",
"!rounded-l-md",
"-translate-y-full",
)
owlPrev?.querySelector("span").classList.add("align-super")
owlNext?.querySelector("span").classList.add("align-super")
}
jQuery(".owl-carousel").owlCarousel({
loop: true,
margin: 10,
stagePadding: 50,
nav: true,
responsive: {
0: {
items: 1,
},
600: {
items: 3,
},
1000: {
items: 5,
},
},
onInitialized: (event) => {
applyOwlStyles(event)
applyTransformOrigin(event)
},
onTranslated: (event) => {
applyTransformOrigin(event)
},
onResized: (event) => {
applyOwlStyles(event)
applyTransformOrigin(event)
},
})
The problem is that .owl-stage:after
takes the size of .owl-item
.
When you use scale
, the size recognized by JavaScript doesn’t change, and the mouseout
event on .owl-carousel
is triggered when you leave the actual carousel and not the scaled item.
To fix this, I created a parent div called .owl-carousel-container
that detects the size of the carousel and maintains the same size. Inside this div, I allow .owl-stage:after
to be as large as it wants. This prevents the undesired mouseout
behavior.
To ensure the .owl-carousel-container
div stays synchronized in size, I use the Owl Carousel events and also a repetition with setTimeout
to handle any delays that might cause issues.
I added multiple repetitions, but you can test and see if it’s possible to reduce the number.
Finally, I also had to synchronize the size of the .owl-prev
and .owl-next
buttons. For this, I created a global variable to ensure they are all updated together.
const originalWarn = console.warn;
console.warn = (msg, ...args) => {
if (!msg.includes('Tailwind')) {
originalWarn(msg, ...args);
}
};
const htmlElement = document.documentElement,
head = document.querySelector("head")
// only for stackoverflow works!
const scriptTailwind = document.createElement("script")
scriptTailwind.src = "https://cdn.tailwindcss.com/"
head.append(scriptTailwind)
scriptTailwind.onload = () => {
const addHoverClasses = (event) => {
const owlitemHovered = event.currentTarget
owlitemHovered.classList.add("!z-10")
owlitemHovered.classList.remove("!z-0")
}
const removeHoverClasses = (event) => {
const owlitemHovered = event.currentTarget
owlitemHovered.classList.remove("!z-10")
owlitemHovered.classList.add("!z-0")
}
const getOwlcarouselResponsiveMaxItems = (event) => {
const owlInstance = event.relatedTarget
const currentBreakpoint = owlInstance._breakpoint
const breakpoints = owlInstance.options.responsive
return breakpoints[currentBreakpoint].items
}
const applyTransformOrigin = (event) => {
const owlCarousel = event.target
const owlItems = owlCarousel.querySelectorAll(".owl-item")
let owlItemActiveCounter = 1
owlItems.forEach((owlItem, index) => {
owlItem.classList.add("!z-0")
owlItem.firstChild.classList.remove(
"origin-[left_center]",
"origin-[right_center]",
"origin-center",
)
if (owlItem?.classList.contains("active")) {
if (owlItemActiveCounter === 1) {
owlItem.firstChild.classList.add("origin-[left_center]")
} else if (
owlItemActiveCounter === getOwlcarouselResponsiveMaxItems(event)
) {
owlItem.firstChild.classList.add("origin-[right_center]")
} else {
owlItem.firstChild.classList.add("origin-center")
}
owlItemActiveCounter++
}
})
}
const applyOwlStyles = (event) => {
const owlCarousel = event.target
const owlItems = owlCarousel.querySelectorAll(".owl-item")
owlCarousel.classList.add("relative")
owlCarousel
.querySelector(".owl-stage-outer")
?.classList.add("!overflow-visible", "!overflow-x-clip")
owlItems.forEach((owlItem, index) => {
owlItem.addEventListener("mouseover", addHoverClasses, false)
owlItem.addEventListener("mouseout", removeHoverClasses, false)
})
const owldotsContainer = owlCarousel.querySelector(".owl-dots")
owldotsContainer?.classList.add(
"absolute",
"!-top-4",
"right-0",
"-translate-y-full",
"!flex",
"!gap-2",
"!items-center",
)
const owldots = owldotsContainer?.querySelectorAll(".owl-dot") ?? []
owldots.forEach((olwDot) => {
const span = olwDot.querySelector("span")
span.classList.add("!m-0", "!rounded-none", "!w-4", "!h-1")
span.classList.remove("!bg-primary-600")
if (olwDot.classList.contains("active")) {
span.classList.add("!bg-primary-600")
}
})
const owlNav = owlCarousel?.querySelector(".owl-nav")
owlNav?.classList.add("!m-0", "!top-0", "!bottom-0", "!w-full")
const owlPrev = owlNav?.querySelector(".owl-prev")
const owlNext = owlNav?.querySelector(".owl-next")
owlPrev?.classList.add(
"!absolute",
"!h-full",
"!m-0",
"!bg-black",
"!bg-opacity-60",
"!text-white",
"!text-6xl",
"!font-opensans",
"!left-0",
"!w-[40px]",
"!rounded-none",
"!rounded-r-md",
"-translate-y-full",
)
owlNext?.classList.add(
"!absolute",
"!h-full",
"!m-0",
"!bg-black",
"!bg-opacity-60",
"!text-white",
"!text-6xl",
"!font-opensans",
"!right-0",
"!w-[40px]",
"!rounded-none",
"!rounded-l-md",
"-translate-y-full",
)
owlPrev?.querySelector("span").classList.add("align-super")
owlNext?.querySelector("span").classList.add("align-super")
}
jQuery(".owl-carousel").owlCarousel({
loop: true,
margin: 10,
stagePadding: 50,
nav: true,
responsive: {
0: {
items: 1,
},
600: {
items: 3,
},
1000: {
items: 5,
},
},
onInitialized: (event) => {
applyOwlStyles(event)
applyTransformOrigin(event)
resizeContainer(event)
},
onTranslated: (event) => {
applyTransformOrigin(event)
},
onResized: (event) => {
applyOwlStyles(event)
applyTransformOrigin(event)
resizeContainer(event)
},
})
function resizeContainer(event) {
const owlCarousel = event.target
repeat5x(() => {
const height = owlCarousel.querySelector(".owl-item").clientHeight
if (height > 10) {
htmlElement.style.setProperty("--owl-carousel-height", `${height}px`)
}
})
}
function repeat5x(fn) {
setTimeout(fn, 0)
setTimeout(fn, 100)
setTimeout(fn, 333)
setTimeout(fn, 666)
setTimeout(fn, 2500)
}
}
.owl-carousel-container {
height: var(--owl-carousel-height) !important;
max-height: var(--owl-carousel-height) !important;
}
.owl-prev, .owl-next {
height: var(--owl-carousel-height) !important;
max-height: var(--owl-carousel-height) !important;
top: var(--owl-carousel-height) !important;
}
.owl-carousel {
position: absolute;
top: 0;
left: 0;
}
.owl-stage:hover:after {
height: var(--owl-carousel-height) !important;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<link href="https://owlcarousel2.github.io/OwlCarousel2/assets/owlcarousel/assets/owl.carousel.min.css" rel="stylesheet"/>
<link href="https://owlcarousel2.github.io/OwlCarousel2/assets/owlcarousel/assets/owl.theme.default.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/OwlCarousel2/2.3.4/owl.carousel.min.js"></script>
<p>another element</p>
<p>another element</p>
<p>another element</p>
<section class="mx-auto w-screen relative flex flex-col gap-4 px-5">
<div class="owl-carousel-container">
<div class="owl-carousel owl-theme">
<div class="item group transition-all hover:absolute hover:scale-125 border border-red-600">
<div href="#" class="card bg-neutral-900 rounded-md cursor-pointer overflow-hidden">
<div>
<img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQKSRTtOrtjLaagEC2gTK6VsPq4NEFk_72hhg&s"
alt="">
</div>
<div class="hidden group-hover:flex bg-white p-1.5 flex-col h-fit w-full">
<div class="flex gap-3 sm:group-hover:scale-[.8] origin-top-left w-[calc(100%/0.8)]">
<span class="">
MOVIE TITLE
</span>
</div>
<a href="" class="text-red-600 sm:group-hover:scale-[.8] origin-top-left w-[calc(100%/0.8)]">
try to click me you can't
</a>
</div>
</div>
</div>
<div class="item group transition-all hover:absolute hover:scale-125 border border-red-600 overflow-hidden">
<div href="#" class="card bg-neutral-900 rounded-md cursor-pointer overflow-hidden">
<div>
<img src="https://occ-0-6613-7435.1.nflxso.net/dnm/api/v6/Qs00mKCpRvrkl3HZAN5KwEL1kpE/AAAABQquP3MObc4Xmx8fS1E0EtFypAJFcNs8U73s6OHL2GjPOuIRbKkknfytMjCRkky6eDIe_8LHwBndef1-gQiN3IMX5NEqFRShrIi9MKSOMGvoTalZ2GRya1eswfUOXrTJ6Nnflg.webp?r=bb3"
alt="">
</div>
<div class="hidden group-hover:flex bg-white p-1.5 flex-col h-fit w-full">
<div class="flex gap-3 sm:group-hover:scale-[.8] origin-top-left w-[calc(100%/0.8)]">
<span class="">
MOVIE TITLE
</span>
</div>
<a href="" class="text-red-600 sm:group-hover:scale-[.8] origin-top-left w-[calc(100%/0.8)]">
try to click me you can't
</a>
</div>
</div>
</div>
<div class="item group transition-all hover:absolute hover:scale-125 border border-red-600">
<div href="#" class="card bg-neutral-900 rounded-md cursor-pointer overflow-hidden">
<div>
<img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQKSRTtOrtjLaagEC2gTK6VsPq4NEFk_72hhg&s"
alt="">
</div>
<div class="hidden group-hover:flex bg-white p-1.5 flex-col h-fit w-full">
<div class="flex gap-3 sm:group-hover:scale-[.8] origin-top-left w-[calc(100%/0.8)]">
<span class="">
MOVIE TITLE
</span>
</div>
<a href="" class="text-red-600 sm:group-hover:scale-[.8] origin-top-left w-[calc(100%/0.8)]">
try to click me you can't
</a>
</div>
</div>
</div>
<div class="item group transition-all hover:absolute hover:scale-125 border border-red-600">
<div href="#" class="card bg-neutral-900 rounded-md cursor-pointer overflow-hidden">
<div>
<img src="https://placecats.com/340/192" alt="">
</div>
<div class="hidden group-hover:flex bg-white p-1.5 flex-col h-fit w-full">
<div class="flex gap-3 sm:group-hover:scale-[.8] origin-top-left w-[calc(100%/0.8)]">
<span class="">
MOVIE TITLE
</span>
</div>
<a href="" class="text-red-600 sm:group-hover:scale-[.8] origin-top-left w-[calc(100%/0.8)]">
try to click me you can't
</a>
</div>
</div>
</div>
<div class="item group transition-all hover:absolute hover:scale-125 border border-red-600">
<div href="#" class="card bg-neutral-900 rounded-md cursor-pointer overflow-hidden">
<div>
<img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQKSRTtOrtjLaagEC2gTK6VsPq4NEFk_72hhg&s"
alt="">
</div>
<div class="hidden group-hover:flex bg-white p-1.5 flex-col h-fit w-full">
<div class="flex gap-3 sm:group-hover:scale-[.8] origin-top-left w-[calc(100%/0.8)]">
<span class="">
MOVIE TITLE
</span>
</div>
<a href="" class="text-red-600 sm:group-hover:scale-[.8] origin-top-left w-[calc(100%/0.8)]">
try to click me you can't
</a>
</div>
</div>
</div>
<div class="item group transition-all hover:absolute hover:scale-125 border border-red-600">
<div href="#" class="card bg-neutral-900 rounded-md cursor-pointer overflow-hidden">
<div>
<img src="https://occ-0-6613-7435.1.nflxso.net/dnm/api/v6/Qs00mKCpRvrkl3HZAN5KwEL1kpE/AAAABQquP3MObc4Xmx8fS1E0EtFypAJFcNs8U73s6OHL2GjPOuIRbKkknfytMjCRkky6eDIe_8LHwBndef1-gQiN3IMX5NEqFRShrIi9MKSOMGvoTalZ2GRya1eswfUOXrTJ6Nnflg.webp?r=bb3"
alt="">
</div>
<div class="hidden group-hover:flex bg-white p-1.5 flex-col h-fit w-full">
<div class="flex gap-3 sm:group-hover:scale-[.8] origin-top-left w-[calc(100%/0.8)]">
<span class="">
MOVIE TITLE
</span>
</div>
<a href="" class="text-red-600 sm:group-hover:scale-[.8] origin-top-left w-[calc(100%/0.8)]">
try to click me you can't
</a>
</div>
</div>
</div>
<div class="item group transition-all hover:absolute hover:scale-125 border border-red-600">
<div href="#" class="card bg-neutral-900 rounded-md cursor-pointer overflow-hidden">
<div>
<img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQKSRTtOrtjLaagEC2gTK6VsPq4NEFk_72hhg&s"
alt="">
</div>
<div class="hidden group-hover:flex bg-white p-1.5 flex-col h-fit w-full">
<div class="flex gap-3 sm:group-hover:scale-[.8] origin-top-left w-[calc(100%/0.8)]">
<span class="">
MOVIE TITLE
</span>
</div>
<a href="" class="text-red-600 sm:group-hover:scale-[.8] origin-top-left w-[calc(100%/0.8)]">
try to click me you can't
</a>
</div>
</div>
</div>
</div>
</div>
</section>
<p>another element</p>
<p>another element</p>
<p>another element</p>