Skip to content

How do I prevent draggable on input and btns #477

Closed

Description

@AsasinCree

I have some operations btns and input inside the draggable area. I can't select text by moving mouse in input but triggering the drag event.

Although I can set "DelayConstraint" by sensors, but it's not a perfect solution while I need to balance the drag delay and operations in input.

I want to know is there a config for disable the part of "white" content from dragging but keep dragging on "red" area

image

Activity

  1. clauderic commented on Oct 19, 2021

    @clauderic
    Owner

    You can override the activator function of the sensors you're using to achieve this. If you return false in the activator handler, the sensor will not be activated.

    Something like:

    class MyPointerSensor extends PointerSensor {
      static activators = [
        {
          eventName: 'onPointerDown',
          handler: ({nativeEvent: event}) => {
            if (
              !event.isPrimary ||
              event.button !== 0 ||
              isInteractiveElement(event.target)
            ) {
              return false;
            }
    
            return true;
          },
        },
      ];
    }
    
    function isInteractiveElement(element) {
      const interactiveElements = [
        'button',
        'input',
        'textarea',
        'select',
        'option',
      ];
    
      if (interactiveElements.includes(element.tagName.toLowerCase())) {
        return true;
      }
    
      return false;
    }
  2. AsasinCree commented on Oct 22, 2021

    @AsasinCree
    Author

    Thanks a lot!!!! Best solution. It would be better if we can achieve this by a prop config like "disableDragInteractiveElements" array

    By the way, I post the Typescript version if anyone need it

    import type { PointerEvent } from "react";
    import { PointerSensor } from "@dnd-kit/core";
    
    /**
     * An extended "PointerSensor" that prevent some
     * interactive html element(button, input, textarea, select, option...) from dragging
     */
    export class SmartPointerSensor extends PointerSensor {
        static activators = [
            {
                eventName: "onPointerDown" as any,
                handler: ({ nativeEvent: event }: PointerEvent) => {
                    if (
                        !event.isPrimary ||
                        event.button !== 0 ||
                        isInteractiveElement(event.target as Element)
                    ) {
                        return false;
                    }
    
                    return true;
                },
            },
        ];
    }
    
    function isInteractiveElement(element: Element | null) {
        const interactiveElements = [
            "button",
            "input",
            "textarea",
            "select",
            "option",
        ];
        if (
            element?.tagName &&
            interactiveElements.includes(element.tagName.toLowerCase())
        ) {
            return true;
        }
    
        return false;
    }
  3. HelKyle commented on Dec 3, 2021

    @HelKyle

    For someone who may have the same issue. Just add data-no-dnd="true" to the parent node and all the descendant nodes will not handle the dnd events.

    import type { MouseEvent, KeyboardEvent } from 'react'
    import {
      MouseSensor as LibMouseSensor,
      KeyboardSensor as LibKeyboardSensor
    } from '@dnd-kit/core'
    
    export class MouseSensor extends LibMouseSensor {
      static activators = [
        {
          eventName: 'onMouseDown' as const,
          handler: ({ nativeEvent: event }: MouseEvent) => {
            return shouldHandleEvent(event.target as HTMLElement)
          }
        }
      ]
    }
    
    export class KeyboardSensor extends LibKeyboardSensor {
      static activators = [
        {
          eventName: 'onKeyDown' as const,
          handler: ({ nativeEvent: event }: KeyboardEvent<Element>) => {
            return shouldHandleEvent(event.target as HTMLElement)
          }
        }
      ]
    }
    
    function shouldHandleEvent(element: HTMLElement | null) {
      let cur = element
    
      while (cur) {
        if (cur.dataset && cur.dataset.noDnd) {
          return false
        }
        cur = cur.parentElement
      }
    
      return true
    }
  4. JANEKKO commented on Aug 16, 2023

    @JANEKKO

    when i use CustomMouseSensor, i got this error, how can i use CustomMouseSensor?
    TypeError: Class constructor MouseSensor cannot be invoked without 'new'

    here's my code:
    const sensors = useSensors( useSensor(CustomMouseSensor, { activationConstraint: { distance: 5, }, }))

  5. DjVreditel commented on Sep 11, 2023

    @DjVreditel

    For someone who may have the same issue. Just add data-no-dnd="true" to the parent node and all the descendant nodes will not handle the dnd events.

    import type { MouseEvent, KeyboardEvent } from 'react'
    import {
      MouseSensor as LibMouseSensor,
      KeyboardSensor as LibKeyboardSensor
    } from '@dnd-kit/core'
    
    export class MouseSensor extends LibMouseSensor {
      static activators = [
        {
          eventName: 'onMouseDown' as const,
          handler: ({ nativeEvent: event }: MouseEvent) => {
            return shouldHandleEvent(event.target as HTMLElement)
          }
        }
      ]
    }
    
    export class KeyboardSensor extends LibKeyboardSensor {
      static activators = [
        {
          eventName: 'onKeyDown' as const,
          handler: ({ nativeEvent: event }: KeyboardEvent<Element>) => {
            return shouldHandleEvent(event.target as HTMLElement)
          }
        }
      ]
    }
    
    function shouldHandleEvent(element: HTMLElement | null) {
      let cur = element
    
      while (cur) {
        if (cur.dataset && cur.dataset.noDnd) {
          return false
        }
        cur = cur.parentElement
      }
    
      return true
    }

    Thanks!

    This is improved TypeScript example:

    import { MouseSensor as LibMouseSensor, TouchSensor as LibTouchSensor } from '@dnd-kit/core';
    import { MouseEvent, TouchEvent } from 'react';
    
    // Block DnD event propagation if element have "data-no-dnd" attribute
    const handler = ({ nativeEvent: event }: MouseEvent | TouchEvent) => {
        let cur = event.target as HTMLElement;
    
        while (cur) {
            if (cur.dataset && cur.dataset.noDnd) {
                return false;
            }
            cur = cur.parentElement as HTMLElement;
        }
    
        return true;
    };
    
    export class MouseSensor extends LibMouseSensor {
        static activators = [{ eventName: 'onMouseDown', handler }] as typeof LibMouseSensor['activators'];
    }
    
    export class TouchSensor extends LibTouchSensor {
        static activators = [{ eventName: 'onTouchStart', handler }] as typeof LibTouchSensor['activators'];
    }
  6. JANEKKO commented on Sep 11, 2023

    @JANEKKO
  7. RafaelCENG commented on Jan 24, 2024

    @RafaelCENG

    Hello everyone, I tried this approach but for some reason my didnt work.
    I have a very similar approach with this sandbox.
    https://codesandbox.io/p/sandbox/dnd-kit-form-builder-fii0zh?file=%2Fsrc%2Fstyles.css
    image
    I want to make it draggable only from the dots and being able to expand my component,click on the fields etc.
    I added the sensors the data-no-dnd="true" to my parent but nothing..

  8. JANEKKO commented on Jan 24, 2024

    @JANEKKO
  9. JANEKKO commented on Feb 27, 2024

    @JANEKKO
  10. HyungwonJang0327 commented on May 13, 2024

    @HyungwonJang0327

    Hello everyone, I tried this approach but for some reason my didnt work. I have a very similar approach with this sandbox. https://codesandbox.io/p/sandbox/dnd-kit-form-builder-fii0zh?file=%2Fsrc%2Fstyles.css image I want to make it draggable only from the dots and being able to expand my component,click on the fields etc. I added the sensors the data-no-dnd="true" to my parent but nothing..

    I'm experiencing the same problem.
    I think it'll work if I don't spread the listeners of useSortable to children, but I don't know how to stop it.

  11. JANEKKO commented on May 13, 2024

    @JANEKKO
  12. tom-leamon commented on May 22, 2024

    @tom-leamon

    This seems very fundamental, I think the proposed solution should either be added to the docs or built into the library behind a prop like "allowEventPropagation" that does it behind the scenes.

  13. paddu1246 commented on Jun 13, 2024

    @paddu1246

    Hi All,

    in Dnd kit library input tag onchage event not fired, I was tried above approach , I didn't get any luck. could please help me out this problem.

    Thank you in advance

  14. JANEKKO commented on Jun 13, 2024

    @JANEKKO
  15. ChTiSh commented on Jul 10, 2024

    @ChTiSh

    @DjVreditel your solution worked beautifully, thank you. Post this as when I saw the recent comments regarding solutions not working I lost some faith wondering if some recent updates caused changes, but I shouldn't.

  16. JANEKKO commented on Jul 10, 2024

    @JANEKKO
  17. mikalai-sauchanka commented on Oct 9, 2024

    @mikalai-sauchanka

    If you get "TypeError: Class constructor MouseSensor cannot be invoked without 'new'", patching the activators (since it's a static prop, can be done once) should work. Also canStartDrag can be anything you want.

    const FORBID_DRAG_ON = ["INPUT", "TEXTAREA"];
    const canStartDrag = () => {
      if (document.activeElement) {
        const tagName = (document.activeElement as HTMLElement).tagName;
        if (
          FORBID_DRAG_ON.includes(tagName) ||
          (tagName === "DIV" && (document.activeElement as HTMLDivElement).contentEditable === "true")
        ) {
          return false;
        }
      }
    
      return true;
    };
    
    const patchActivator = <T extends SensorOptions>(activators: Activators<T>, eventName: string): Activators<T> => {
      const originalActivatorIndex = activators.findIndex((x) => x.eventName === eventName);
      const originalActivator = activators[originalActivatorIndex];
    
      activators[originalActivatorIndex] = {
        eventName: originalActivator.eventName,
        handler(...args) {
          return canStartDrag() && originalActivator.handler(...args);
        }
      };
      return activators;
    };
    
    patchActivator(KeyboardSensor.activators, "onKeyDown");
    patchActivator(PointerSensor.activators, "onPointerDown");
  18. JANEKKO commented on Oct 9, 2024

    @JANEKKO
  19. 1used23 commented on Nov 2, 2024

    @1used23

    More beautiful solution in my opinion, if you get "TypeError: Class constructor MouseSensor cannot be invoked without 'new'" with previous solutions:

    import type { MouseEvent, TouchEvent } from 'react';
    import { MouseSensor, TouchSensor } from '@dnd-kit/core';
    
    const IGNORE_TAGS = ['BUTTON'];
    
    // Block Dnd, if IGNORE_TAGS includes elements tag or element has data-no-dnd attribute
    const customHandleEvent = (element: HTMLElement | null) => {
      let cur = element;
    
      while (cur) {
        if (IGNORE_TAGS.includes(cur.tagName) || cur.dataset.noDnd) {
          return false;
        }
        cur = cur.parentElement;
      }
    
      return true;
    };
    
    MouseSensor.activators = [
      {
        eventName: 'onMouseDown',
        handler: ({ nativeEvent: event }: MouseEvent) => customHandleEvent(event.target as HTMLElement),
      },
    ];
    
    TouchSensor.activators = [
      {
        eventName: 'onTouchStart',
        handler: ({ nativeEvent: event }: TouchEvent) => customHandleEvent(event.target as HTMLElement),
      },
    ];
  20. JANEKKO commented on Nov 2, 2024

    @JANEKKO
  21. added a commit that references this issue on Jul 22, 2025
  22. added a commit that references this issue on Aug 11, 2025
  23. benjamin-andersen commented on Oct 5, 2025

    @benjamin-andersen

    In case anyone else stumbles on this issue like I did...

    I was having issues with this solution #477 (comment) not working very well when rendering a shadcn/ui Sheet (or Popover/Dialog etc.) within the draggable element. Clicking anywhere within the Sheet would bubble the event up to the draggable element and start dragging the element.

    Stopping propagation of pointerdown and keydown events would prevent the Sheet from closing, so that had me stuck without any way to fix this using the custom sensor solution.

    I was able to solve this without adding a visual drag handle by using a trick like this:

    <div className="relative">
      {/* this should be the first child, so the siblings can be clickable */}
      <button className="absolute inset-0" {...attributes} {...listeners} ref={setActivatorNodeRef}/>
    
      {/* ... rest of the interactable UI ... */}
      <button>look ma, no dragging</button>
    </div>

    Essentially you can have the handle positioned in a way so it fills the parent element by giving it an absolute position. If you can't make it the first child of the parent, then you could probably get away with messing with z-index on the children elements so the interactable elements (buttons, inputs, etc.) can be clicked without dragging.

    The pointerdown and keydown events in the Sheet won't propagate to the <button> and start dragging because it's not an ancestor of the <Sheet> component.

    You also don't need to create custom sensors and set data attributes on interactable elements.

  24. JANEKKO commented on Oct 5, 2025

    @JANEKKO
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions