davidjerleke / embla-carousel

A lightweight carousel library with fluid motion and great swipe precision.
https://www.embla-carousel.com
MIT License
6.31k stars 187 forks source link

A way to figure out if a scroll is happening #132

Closed mattensor closed 3 years ago

mattensor commented 3 years ago

Hey

Awesome library, Thank you!

I have slides which have click events associated with them, I'd like to only allow click events when scrolling is not in progress. Is there a way I can figure this out?

My current config is: image

Thanks

davidjerleke commented 3 years ago

Hi @Mattensor,

Thank you for your question. Before I suggest a solution, I would like to know if you want to disallow clicks as soon as the carousel is scrolling? Or only when the pointer interaction is a drag move?

Kindly, David

mattensor commented 3 years ago

Hey @davidcetinkaya,

Thanks for getting back to me so quick!

Ideally I'd like a combination of both.

When I drag and release (init a scroll) the element below receives the click event. My assumption is the click event is fired when the carousel is first scrolling, but this may be also may be drag event related. During the scroll elements are clickable I'd like to disallow this too. The initial click going intro the scroll is more important to get a solution for.

Appreciate your help, Thanks Matt

davidjerleke commented 3 years ago

Hi @Mattensor,

Thank you for the additional details. I think you're looking for the Embla opt-in method named embla.clickAllowed(). I don't know if you're using the VanillaJS or ReactJS version of Embla. Anyhow, example usage looks something along these lines:

VanillaJS

const onSlideClick = (index) => {
  if (!embla.clickAllowed()) return

  // Embla says the pointer interaction was a static click, and not a drag move.
  // Do your magic here...
}
embla.slideNodes().forEach((slide, index) => {
  slide.addEventListener('click', () => onSlideClick(index), false)
})

ReactJS

const onSlideClick = useCallback((index) => {
  if (!embla || !embla.clickAllowed()) return

  // Embla says the pointer interaction was a static click, and not a drag move.
  // Do your magic here...
}, [embla])
<div className="embla__container">
  {slides.map((slide, index) => {
    <div className="embla__slide" onClick={() => onSlideClick(index)}>
      ...
    </div>
  })}
</div>

How it works under the hood

If the pointer is touch, Embla will:

If the pointer is mouse, Embla will:

The reason behind this design choice is that touch pointers scroll the document. And when the document is natively scrolling on touch devices, it prevents clicks until the user stops the scrolling and clicks again when it's settled.

But mouse pointers are not used to scroll the document, unless you're dragging the scrollbar. And if you're dragging the scroll bar, it will prevent you from accidentally clicking anything on the webpage anyway, because the pointer is within the scroll bar area. So the assumption here is that we can be more certain that a click with a mouse pointer is intentional.

I hope my explanation makes sense. Do you still want to prevent clicks when the carousel is clicked during scroll for mouse pointers? In that case I'll look into it.

Best, David

mattensor commented 3 years ago

Thanks David!

Thats was a great explanation, fixed my main issue. Don't worry about the looking into how to prevent clicks while the carousel is in scroll, the clickAllowed on drag event is more than enough.

Here's my solution in react

Parent Component

image

Child Component

image

davidjerleke commented 3 years ago

Nicely done @Mattensor. Thank you for sharing your code. It might help others wanting to achieve the same thing.

Enjoy 🙂.