Embla-carousel: Scroll/Drag doesn't work until window is resized

Created on 23 Mar 2021  路  5Comments  路  Source: davidcetinkaya/embla-carousel

Bug is related to

  • [ ] The Vanilla JavaScript version
  • [x] The React version
  • [ ] Documentation website
  • [ ] Other

Embla Carousel version

  • v4.2.0

Describe the bug

  • When I first populate the carousel with several items, I am unable to use the navigation arrows or to drag through my items.
  • When I resize the window to a different size (either larger or smaller), then restore it to its original size, then the arrows and drag functionality works.
  • When I log embla (which is set to useEmblaCarousel()) before I do the resizing, the object is _not_ null or undefined.
  • I'm still trying to determine whether there are parent styles (in overarching components) causing this issue, but I can't see anything in these components that would cause the dragging and scrollNext/scrollPrev methods to not work.

CodeSandbox

  • Unfortunately, I'm working for a client and am unable to share the full code. However, I scrubbed the important variable names and have the carousel code below. (I'm using styled components.)
const OverflowContainer = styled.div`
    overflow: hidden;
    height: 100%;
    min-width: 80%;
`;

const FlexContainer = styled.div`
    display: flex;
    height: 100%;
    width: 80%;
`;

const Slide = styled.div`
    position: relative;
    flex: 0 0 15%;
    height: 80%;
`;

const PreviousButton = styled.button`
    cursor: pointer;
    background-color: green;
    width: 30px;
    height: 30px;
`;

const NextButton = styled.button`
    cursor: pointer;
    background-color: blue;
    width: 30px;
    height: 30px;
`;

interface Props {
    slides: JSX.Element[];
}

const ExampleCarousel = ({mergedVideos}: Props) => {
    const [carouselRef, embla] = useEmblaCarousel();

    const scrollPrev = useCallback(() => {
        embla && embla.scrollPrev(); // embla evaluates properly here - failure happens in scrollPrev()?
    }, [embla]);
    const scrollNext = useCallback(() => {
        embla && embla.scrollNext(); // embla evaluates properly here - failure happens in scrollNext()?
    }, [embla]);

    const slideVideos = slides.map((m) => <Slide key={m.key}>{m}</Slide>);

    return (
        <>
            <OverflowContainer ref={carouselRef}>
                <FlexContainer>{slideVideos}</FlexContainer>
            </OverflowContainer>
            <PreviousButton onClick={scrollPrev} />
            <NextButton onClick={scrollNext} />
        </>
    );
};

export default ExampleCarousel;

Steps to reproduce

  1. Populate the carousel with enough items to merit scrolling
  2. Clicking the navigation buttons will not do anything.
  3. Dragging the carousel items to scroll will not do anything.
  4. Resize the window and then put it back to its original size.
  5. You should be able to use the navigation buttons and drag to scroll.

Expected behavior

  • I expect the scrolling behaviors to work immediately, and not after resizing my window.

Thank you! Let me know if there's anything else I can provide... If this weren't a client project, I would have gifs and other accompanying pieces for you. :( Apart from this strange issue, I've loved using Embla so far!

not a bug react

Most helpful comment

Thanks for confirming @ej-mitchell 馃憤. Enjoy!

All 5 comments

Hi EJ (@ej-mitchell),

Thank you for the clear issue description. I can't be 100% sure because I don't have access to your full setup, but I don't think this is a bug. I think this is happening because one of the following reasons:

  1. Embla Carousel is initialized before it has any slides to pick up. Embla will do a querySelector to check for any slides inside its container, so if there鈥檚 a very brief moment that you probably won鈥檛 even notice, that will be enough to cause the issue.
  2. Any parent of the carousel has display: none or similar when it's initialized the first time. This makes it impossible for Embla to pick up any slide or container dimensions.

Embla Carousel has no internal mechanism for picking up any changes in slides. This has to be done manually by telling Embla to reinitialize. The reason why it works when you resize the window is because Embla automatically calls embla.reInit() on window resize, which will do a hard reset and pick up any slides inside its container.

If this is happening because of reason 1, try the following solution:

useEffect(() => {
  if (embla && embla.slideNodes().length !== slides.length) {
    embla.reInit() // If the slides prop length changes, pick it up
  }
}, [
  embla,
  slides /* Add slides as a dependency to trigger this when the prop changes */
])

If it's happening because of reason 2, pass null instead of the carouselRef until you've removed the display: none style. This will work because Embla will reinitialize when the ref changes. Something along these lines:

const [carouselReady, setCarouselReady] = useState(false)

return (
  ...

  <OverflowContainer ref={carouselReady ? carouselRef : null}>
    <FlexContainer>{slideVideos}</FlexContainer>
  </OverflowContainer>

  ...
)

Let me know if it helps.

Best,
David

Thank you so much, @davidcetinkaya ! This was a lovely and concise answer. I will give that a go. 馃樃 I will close the issue, too since this is not a bug.

@ej-mitchell let me know how it goes when you've tried my suggestions 馃憤.

It worked 馃樆 thank you! (I had a hunch that it was scenario 1)

Thanks for confirming @ej-mitchell 馃憤. Enjoy!

Was this page helpful?
0 / 5 - 0 ratings