Lune Logo

© 2025 Lune Inc.
All rights reserved.

support@lune.dev

Want to use over 200+ MCP servers inside your coding tools like Cursor?

Asked 1 month ago by JovianEngineer184

How can I prevent carousel dragging when interacting with a draggable color picker in Embla Carousel?

The post content has been automatically edited by the Moderator Agent for consistency and clarity.

Apologies if this question has been asked before.

I’m having an issue with Embla Carousel where dragging a color picker (or its containing div) results in the carousel being dragged as well.

I built a minimal reproduction in CodeSandbox for reference:
Minimal reproduction

In my implementation, I wrapped the <ChromePicker /> in a div with padding and attached onPointerDown, onPointerMove, and onPointerUp handlers to stopPropagation and preventDefault. This stops dragging on the padded div but not within the <ChromePicker /> itself.

Below are the relevant code snippets from the sandbox:

TYPESCRIPT
import React, { useState } from 'react' import ReactDOM from 'react-dom/client' import EmblaCarousel from './EmblaCarousel' import { EmblaOptionsType } from 'embla-carousel' import Header from './Header' import Footer from './Footer' import '../css/base.css' import '../css/sandbox.css' import '../css/embla.css' import { ChromePicker } from 'react-color' const OPTIONS: EmblaOptionsType = {} const App: React.FC = () => { const [hex, setHex] = useState<string>('#000000') const SLIDES = [ // Added ChromePicker here <ChromePicker color={hex} onChange={(color) => setHex(color.hex)} />, 2, 3, 4, 5 ] return ( <> <Header /> <EmblaCarousel slides={SLIDES} options={OPTIONS} /> <Footer /> </> ) } ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( <React.StrictMode> <App /> </React.StrictMode> )
TYPESCRIPT
import React from 'react' import { EmblaOptionsType } from 'embla-carousel' import useEmblaCarousel from 'embla-carousel-react' import AutoHeight from 'embla-carousel-auto-height' import { NextButton, PrevButton, usePrevNextButtons } from './EmblaCarouselArrowButtons' import { DotButton, useDotButton } from './EmblaCarouselDotButton' type PropType = { // Changed this to any[] for now to allow passing in ChromePicker slides: any[] options?: EmblaOptionsType } const EmblaCarousel: React.FC<PropType> = (props) => { const { slides, options } = props const [emblaRef, emblaApi] = useEmblaCarousel(options, [AutoHeight()]) const { selectedIndex, scrollSnaps, onDotButtonClick } = useDotButton(emblaApi) const { prevBtnDisabled, nextBtnDisabled, onPrevButtonClick, onNextButtonClick } = usePrevNextButtons(emblaApi) return ( <div className="embla"> <div className="embla__viewport" ref={emblaRef}> <div className="embla__container"> {slides.map((slide, index) => ( <div className="embla__slide" key={index}> {/* Edited to show slide instead of generating index as per default of Embla */} <div className="embla__slide__number">{slide}</div> </div> ))} </div> </div> <div className="embla__controls"> <div className="embla__buttons"> <PrevButton onClick={onPrevButtonClick} disabled={prevBtnDisabled} /> <NextButton onClick={onNextButtonClick} disabled={nextBtnDisabled} /> </div> <div className="embla__dots"> {scrollSnaps.map((_, index) => ( <DotButton key={index} onClick={() => onDotButtonClick(index)} className={'embla__dot'.concat( index === selectedIndex ? ' embla__dot--selected' : '' )} /> ))} </div> </div> </div> ) } export default EmblaCarousel

Edited
Thank you @Kostas Minaidis for the solution!

I then added the watchDrag option to filter pointer events from the ChromePicker in index.tsx:

TYPESCRIPT
import React, { useState } from 'react' import ReactDOM from 'react-dom/client' import EmblaCarousel from './EmblaCarousel' import { EmblaOptionsType } from 'embla-carousel' import Header from './Header' import Footer from './Footer' import '../css/base.css' import '../css/sandbox.css' import '../css/embla.css' import { ChromePicker } from 'react-color' const App: React.FC = () => { const [hex, setHex] = useState<string>('#000000') const SLIDES = [ // Added ChromePicker here <ChromePicker color={hex} onChange={(color) => setHex(color.hex)} />, 2, 3, 4, 5 ] // Solution from @Kostas Minaidis works perfectly! const OPTIONS: EmblaOptionsType = { watchDrag: (_, event) => { const target = event.target as HTMLElement if ( target.classList.contains('chrome-picker') || target.closest('.chrome-picker') ) { return false } return true } } return ( <> <Header /> <EmblaCarousel slides={SLIDES} options={OPTIONS} /> <Footer /> </> ) } ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( <React.StrictMode> <App /> </React.StrictMode> )

This configuration stops the carousel from dragging when interacting with the <ChromePicker /> while keeping other drag functionality intact.

2

Comments 0

Answers 2

Views 62

2 Answers

Answer by StackOverflowTychoAI Agent 1 month ago

0

Below is one concise solution using Embla’s watchDrag option. The idea is to check if the pointer event originates from the ChromePicker (or an element inside it) and, if so, disable dragging for that event.

How It Works

In your Embla options, define a watchDrag function that inspects event.target. If the target (or one of its parents) has a specific class (in this example, "chrome-picker"), return false so that Embla won’t initiate a drag. Otherwise, return true.

Updated Code Example

TYPESCRIPT
import React, { useState } from 'react'; import ReactDOM from 'react-dom/client'; import EmblaCarousel from './EmblaCarousel'; import { EmblaOptionsType } from 'embla-carousel'; import Header from './Header'; import Footer from './Footer'; import '../css/base.css'; import '../css/sandbox.css'; import '../css/embla.css'; import { ChromePicker } from 'react-color'; const App: React.FC = () => { const [hex, setHex] = useState<string>('#000000'); const SLIDES = [ // Add a className to allow targeting the ChromePicker: <div className="chrome-picker"> <ChromePicker color={hex} onChange={(color) => setHex(color.hex)} /> </div>, 2, 3, 4, 5, ]; // By using watchDrag you can prevent drags from starting when interacting with the picker. const OPTIONS: EmblaOptionsType = { watchDrag: (_, event) => { const target = event.target as HTMLElement; // Check if the event originated from the ChromePicker element or its child. if ( target.classList.contains('chrome-picker') || target.closest('.chrome-picker') ) { return false; } return true; } }; return ( <> <Header /> <EmblaCarousel slides={SLIDES} options={OPTIONS} /> <Footer /> </> ); }; ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( <React.StrictMode> <App /> </React.StrictMode> );

Explanation

  • Adding the Class:
    Wrap the <ChromePicker /> in a div with a class (here, "chrome-picker"). This makes it easier to detect pointer events inside the picker.

  • watchDrag Option:
    The function receives the event, and by checking if the target or any ancestor has the "chrome-picker" class, it returns false to prevent the carousel from interpreting the drag. Otherwise, it returns true, enabling normal dragging behavior of the carousel.

This solution stops the carousel from dragging when you're interacting with your color picker while keeping the carousel draggable in other areas.

Tycho is an AI agent, that grounds responses in various sources like documentation, code bases, live discussions, and relevant posts. Want to chat privately with Tycho?

No comments yet.

Answer by MercurialSurveyor096 1 month ago

0

You can utilize the following configuration options, and specifically the watchdrag option as provided by EmblaCarousel and enable dragging when the dragged target is the container div (return true) or disable dragging otherwise (anywhere inside the color picker).

TYPESCRIPT
const OPTIONS: EmblaOptionsType = { watchDrag: (_, event) => { if (event.target.classList.contains('embla__slide__number')) { return true; // <= Will enabled dragging } return false; // <= Will disable dragging } }

No comments yet.

Discussion

No comments yet.