react-hotkeys icon indicating copy to clipboard operation
react-hotkeys copied to clipboard

HotKeys do not fire on contentEditable div (even when passing `ignore` options)

Open jkhaui opened this issue 5 years ago • 5 comments

Hey,

First thing's first - thanks for maintaining such a great library. Apologies if my issue has an obvious solution, but I couldn't find an answer in the docs.

Describe the bug I have HotKey mappings to toggle a chatbot modal open/close when fired. These HotKeys works fine anywhere in the app, except when the key combo is pressed within a focused contenteditable div.

How are you using react hotkeys components? (HotKeys, GlobalHotKeys, IgnoreKeys etc) I'm using the standard HotKeys setup as shown in the doc's example. I also pass the following options to configure: configure( { ignoreTags: [], ignoreEventsCondition: () => { return false; } } );

N.b I pass an empty array to ignoreTags so that the toggle KeyMap still works when the chatbot is open and the cursor is focused within its input/textarea. The interesting thing is that this functionality works as expected for the input area.

Expected behavior When the contenteditable div is focused, KeyMap combos should still fire and toggle the chatbot open/close.

Platform (please complete the following information):

  • Version of react-hotkeys: latest
  • Browser [e.g. chrome, safari]: chrome

The answer is probably in the function provided to ignoreEventsCondition, but I'm confused as to what should be provided here. Thank you for any guidance

edit: I've noticed that the KeyMap does fire when the contenteditable div is focused, but only like 50% of the time. The behaviour is quite random and inconsistent. I'll investigate further and see if I can get to the bottom of it.

jkhaui avatar Jul 26 '19 08:07 jkhaui

Hi @jkhaui,

Thanks for your kind words. I haven't heard from a user who is using keyboard shortcuts in a content editable div, so thank you for reaching out and providing that perspective.

I agree, from your description of how you're using react-hotkeys, that you can expect it to be working inside the content editable div.

Because you have overridden the ignoreEventsCondition function, ignoreTags isn't actually used. So I think we can rule that out as the problem, for now.

For your reference, the default function for ignoreEventsCondition is as follows:

/**
   * The function used to determine whether a key event should be ignored by React
   * Hotkeys. By default, keyboard events originating elements with a tag name in
   * ignoreTags, or a isContentEditable property of true, are ignored.
   *
   * @type {Function<KeyboardEvent>}
   */
  ignoreEventsCondition: (event) => {
    const {target} = event;

    if (target && target.tagName) {
      const tagName = target.tagName.toLowerCase();

      return Configuration.option('_ignoreTagsDict')[tagName] || target.isContentEditable;
    } else {
      return false;
    }
  },

Basically, it checks for tags in ignoreTags or whether the event target is content editable. So, you have done the right thing by overriding it for your use-case.

I believe because you are seeing inconsistent behaviour, this will be one of the optimization measures at play. Could you please enable logging to verbose and share an instance where the hotkeys fail to trigger while your content editable div is in focus?

greena13 avatar Jul 27 '19 06:07 greena13

Hey guys, I have the same problem:

On my page i have slateJS editor which is basically a div with contenteditable="true" and a wavesurfer.js audio player.

I use

<GlobalHotKeys keyMap={keyMap} handlers={handlers} /> 

and I want to play/pause player working with text (it is a voice-to-text transcription app)

BUT hotkeys work only when text editor is out of focus!

well, slate js allows me to define keybindings, but I want the one and only source of truth, but not many hotkeys function spread over app.

sorokinvj avatar Jul 30 '19 12:07 sorokinvj

@jkhaui I gather from your edit that you made the hotkey work with configure({ ignoreEventsCondition: (event) => { return false; } }) which is great. In my case, the hotkey assigned to the contentEditable div was only firing when I refocused on the div. What helped was adding this line to the handler e.preventDefault(), which prevents default hotkeys assigned to the element. Hope this helps

aamikus avatar Feb 18 '20 00:02 aamikus

@aamikus to be honest I can't remember if I ever did get it working. I don't think I did... But I'm working on a similar project now which also uses react-hotkeys, so I'll probably run into the issue again. Your solution will help me and anyone else experiencing issues with contenteditable, thanks for posting it!

jkhaui avatar Feb 18 '20 08:02 jkhaui

Running into this now, also with Slate.js. Is there a way to call react-hotkeys on each keyboard event manually rather than have react-hotkeys capture events? That would be a reasonable solution.

coffeemug avatar Aug 14 '20 06:08 coffeemug