javascriptreactjsmaterial-ui

Scroll events unintentionally changing Material UI slider values


I have a React application that is utilizing Material UI. The application has slider components implemented in a lot of places (https://material.io/components/sliders). When using a touch screen device, I am unintentionally impacting slider components (the value is getting changed) while trying to scroll up or down the page. This does not happen with other page components, I suspect this is because sliders do (and should) respond to swipe events.

Material UI has documentation that implies a way to discern between a "scroll" and a "swipe" (https://material.io/design/interaction/gestures.html#types-of-gestures). Is there a way for me to indicate to my slider components that a "scroll" should be ignored. Or, can I discern between a vertical or horizontal swipe, telling the slider to ignore vertical swipes?


Solution

  • I have come up with a fairly elegant solution, I believe, which allows the user to scroll if their scroll position begins on the track but not on the thumbs. This replicates the native HTML range input so I feel that this is the best solution.

    There's two parts to this

    Step 1, allow touch-action on the slider root element as it is disabled by default and prevents the user from starting a scroll on the slider

    const useStyles = makeStyles({
      sliderRoot: {
        touchAction: "auto"
      }
    });
    
    return (
      <Slider
        classes={{
          root: classes.sliderRoot
        }}
        ...
      />
    

    Step 2, stop propagation on the root element with a ref

    const ref = useRef(null);
    
    useEffect(() => {
      if (ref.current) {
        ref.current.addEventListener(
          "touchstart",
          (e) => {
            const isThumb = e.target?.dataset.index;
    
            if (!isThumb) {
              e.stopPropagation();
            }
          },
          { capture: true }
        );
      }
    });
    
    return (
      <Slider
        ref={ref}
        ...
      />
    

    And here is a fully working demo on Codesandbox.

    Edit Material demo (forked)