React-beautiful-dnd: Clicking on draggable items doesn't unfocus currently focused inputs

Created on 29 Mar 2018  路  8Comments  路  Source: atlassian/react-beautiful-dnd

Bug

Focusing an input field and clicking on a draggable item doesn't unfocus that input:

Expected behavior

Any focused items should be unfocused when a draggable element is clicked.

Demo

dnd_unfocus

docs 馃摉

Most helpful comment

Unfortunately we need to use event.preventDefault() in mousedown to prevent the item from getting focus. As you mentioned, if you are clicking you want to give focus.

Here is what you can do:

  • Add your own onClick handler to your drag handle
  • In your onClick handler, check to see if event.defaultPrevented is true. If it is true then it was used as a part of a drag and you do want want to give focus (see how we use dom events)
  • If event.defaultPrevented is false then manually give the item focus! Now you have correctly given the item focus on click!

Rough code:

onClick = (event: MouseEvent) => {
  // click was used as a part of the drag
  if(event.defaultPrevented) {
    return;
  }

  this.ref.focus();
}

render() {
    return (
      <Draggable draggableId="draggable" index={0}>
        {(provided, snapshot) => (
          <div>
            <div 
               ref={ref => (this.ref = ref && provided.innerRef(ref))}
               {...provided.draggableProps}
               {...provided.dragHandleProps}
               onClick={this.onClick}
            >
               Drag me or click me!
            </div>
          </div>
        )}
      </Draggable>
}

All 8 comments

馃槻

We'll look into it!

This is actually intentional:

https://github.com/atlassian/react-beautiful-dnd/blob/master/src/view/drag-handle/sensor/create-mouse-sensor.js#L267-L270

If we do not do this then when dragging the Draggable would always get browser focus. For now we have decided that a drag and drop interaction does not demand that the focus shift to the dragging item.

I think we should think about this a bit more. But for now we want to avoid the Draggable getting focus when dragging

I understand the reasoning, and I agree the _drag_ action might not require the shift in focus. I do think there's a difference between click and a drag and this is blocking both. In the app I'm working on we are selecting rerderable items with a click and have a keyboard shortcut for removing/copying/pasting them. If an input was selected before the action then all the keyboard signals will go to the input field and nothing will happen. That sounds like a possibly common scenario to me.

Unfortunately we need to use event.preventDefault() in mousedown to prevent the item from getting focus. As you mentioned, if you are clicking you want to give focus.

Here is what you can do:

  • Add your own onClick handler to your drag handle
  • In your onClick handler, check to see if event.defaultPrevented is true. If it is true then it was used as a part of a drag and you do want want to give focus (see how we use dom events)
  • If event.defaultPrevented is false then manually give the item focus! Now you have correctly given the item focus on click!

Rough code:

onClick = (event: MouseEvent) => {
  // click was used as a part of the drag
  if(event.defaultPrevented) {
    return;
  }

  this.ref.focus();
}

render() {
    return (
      <Draggable draggableId="draggable" index={0}>
        {(provided, snapshot) => (
          <div>
            <div 
               ref={ref => (this.ref = ref && provided.innerRef(ref))}
               {...provided.draggableProps}
               {...provided.dragHandleProps}
               onClick={this.onClick}
            >
               Drag me or click me!
            </div>
          </div>
        )}
      </Draggable>
}

It could be worth adding a note about this to the docs..

Let me know how you go @smogg

That seems to have worked 馃帀 Thanks!

@alexreardon thank you for your solution. I have found that if you would like to avoid using a ref to focus, you can also call event.currentTarget.focus() which should focus the item's dnd wrapper:

const onClick = (event) => {
  if (event.defaultPrevented) {
    return;
  }

  event.currentTarget.focus();
};

Useful if you rendering multiple items and don't want to store state for each individual entity.

One other point, if I may add, is that if you have input fields in your dnd item such as dropdowns, you should stopPropagation onClick as to avoid focus wars with the above snippet:

<input onClick={e => e.stopPropagation()} />

// or

<div onClick={e => e.stopPropagation()}>
  <input />
</div>
Was this page helpful?
0 / 5 - 0 ratings