Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Focus trap #756

Open
2 of 8 tasks
joshwcomeau opened this issue Dec 5, 2021 · 4 comments
Open
2 of 8 tasks

Focus trap #756

joshwcomeau opened this issue Dec 5, 2021 · 4 comments

Comments

@joshwcomeau
Copy link

Hi there! Thanks so much for this wonderful project :)

The "Tab" key is used by React Quill for indentation, which makes sense in a text-editing context, but it also overrides the ability for keyboard users to navigate through the page. It essentially "traps" focus and blocks the user from moving past it, unless they use a pointer device like a mouse/trackpad.

Here's an example, to demonstrate the issue. Try to focus the <input> after the ReactQuill instance: https://codesandbox.io/s/react-quill-template-forked-rbmqq

I don't think this is a Quill issue, since this issue suggests that it's been fixed in Quill.

Workaround

Here's what I'm doing right now as a workaround:

<Quill
  onKeyDown={(ev) => {
    const isTabbingInEditor =
      ev.key === 'Tab' &&
      ev.target.getAttribute('class') === 'ql-editor';

    if (isTabbingInEditor) {
      ev.preventDefault();
      ev.target.blur();
      return false;
    }
  }}
/>

This kinda works, but there are two problems:

  • It doesn't focus the next item in the tab order, so you need to press tab twice
  • It doesn't stop the "tab" key from being pressed, adding additional whitespace to the editor

Has anyone found a better workaround?

Ticket due diligence

  • I have verified that the issue persists under ReactQuill v2.0.0-beta.2
  • I can't use the beta version for other reasons

ReactQuill version

  • master
  • v2.0.0-beta.4
  • v2.0.0-beta.1
  • 1.3.5
  • 1.3.4 or older
  • Other (fork)
@wfischer42
Copy link

I have the same problem. I modified that workaround to account for your two remaining problems.

  1. I moved the event capture logic into a wrapper div and put it in the onKeyDownCapture event, so it would prevent the tab from reaching the Quill element.
  2. I'm explicitly focusing on the next tabbable element, instead of blurring the editor. This can be passed in as a prop if you set up a re-usable wrapper component.
<div
  onKeyDownCapture={(ev) => {
    if (ev.key === "Tab") {
      ev.preventDefault();
      ev.stopPropagation();
      document.getElementById(props.nextTabId)?.focus();
    }
  }}>
  <ReactQuill
    ref={editor}
    ...
  />
</div>

I'd like to see a real fix for this as well, but for now at least, my use case is covered.

@dudasaron
Copy link

I also have the same issue.

Thanks @wfischer42 for sharing your solution, it helped me a lot.

I just added a few modifications, so it does not trap the tab navigation if the user goes backwards, and for forward navigation it goes through the toolbar buttons, and only goes to nextTabId when the event comes from the editor area.

Also added area-label for the wrapper div for accessibility

<div
      role="textbox"
      aria-label={ariaLabel}
      onKeyDownCapture={(e) => {
          if (e.key === 'Tab' && (e.target as HTMLElement).classList.contains('ql-editor')) {
              e.preventDefault()
              e.stopPropagation()
              document.getElementById(e.shiftKey ? prevTabId : nextTabId)?.focus()
          }
      }}
  >
      <ReactQuill {...props} preserveWhitespace />
</div>

@Shelagh-Lewins
Copy link

In your code example, Alt-tab allows a keyboard user to exit the text area. It would be great if this was documented, because I don't think it's obvious.
It might aid users if when 'Tab' is pressed, a message popped up saying "to navigate out of the text area, press Alt + Tab" or something similar?

@JeremyRippert
Copy link

JeremyRippert commented Nov 20, 2022

I found a workaround that doesn't require prevTabdId or nextTabId, by removing the tab key binding (inspired from slab/quill#110 (comment)). Here is the full snippet (I'm using NextJS, hence the unusual import for 'react-quill'):

// to type 'react-quill' import and 'quillRef'
import QuillComponent, { ReactQuillProps } from 'react-quill';

const ReactQuill = (
  typeof window === 'object' ? require('react-quill') : () => false
) as React.FC<ReactQuillProps  & { ref: React.Ref<QuillComponent> }>;

export const Quill: React.FC = () => {
  const quillRef = useRef<QuillComponent | null>(null);

  useEffect(() => {
    const removeTabBinding = () => {
      if (quillRef.current === null) {
        return;
      }
      const keyboard = quillRef.current.getEditor().getModule('keyboard');
      // 'hotkeys' have been renamed to 'bindings'
      delete keyboard.bindings[9];
    };

    removeTabBinding();
  }, [quillRef);
  
  return  <ReactQuill
        ref={quillRef}
        value={value}
        onChange={onChange}
        theme="snow"
      />
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants