【问题标题】:React - Prevent focus going out of modal when tabbingReact - 防止焦点在选项卡时脱离模式
【发布时间】:2021-12-17 18:13:44
【问题描述】:

我自己构建了一个反应模式。当我在模式打开时按 Tab 键时,焦点仍然转到背景页面。如何将焦点限制在 modal 内部的组件内?

下面的逻辑应该是什么?

onKeyPress (e) {
   if (e.keyCode === 9) {
       e.preventDefault();
      // logic here?
   }
}

React 模态代码:

<ReactModal onKeyPress={this.onKeyPress} >
   <input type="text"/>
   <input type="text"/>
</ReactModal>

【问题讨论】:

    标签: javascript css reactjs modal-dialog


    【解决方案1】:

    嗯,你可以使用焦点陷阱来做到这一点。看看这个npm module。只需用这样的焦点陷阱包装你的包含模式的内容。

        <FocusTrap
                  focusTrapOptions={{
                    ...
                  }}
                >
                  <div className="trap">
                    <p>
                      Here is a focus trap
                      {' '}
                      <a href="#">with</a>
                      {' '}
                      <a href="#">some</a>
                      {' '}
                      <a href="#">focusable</a>
                      {' '}
                      parts.
                    </p>
                    <p>
                      <button onClick={this.someCallback}>
                        Click Me
                      </button>
                    </p>
                  </div>
    
    </FocusTrap>
    

    我建议您不要自己实现它,而不是给您建议来实现它。考虑到可访问性,很难做到正确。

    相反,我建议您使用可访问的现成模态组件,例如 react-modal。它是完全可定制的,你可以在里面放任何你想要的东西,但它可以正确处理可访问性,以便盲人仍然可以使用你的模态。

    【讨论】:

      【解决方案2】:

      我必须将焦点锁定在我们在 React 组件中使用过的模式中。我为 KEY DOWN 添加了 eventListner 并收集了 Tab 和 Shift+Tab

      class Modal extends Component {
          componentDidMount() {
              window.addEventListener("keyup", this.handleKeyUp, false);
              window.addEventListener("keydown", this.handleKeyDown, false);
          }
      
          componentWillUnmount() {
              window.removeEventListener("keyup", this.handleKeyUp, false);
              window.removeEventListener("keydown", this.handleKeyDown, false);
          }
      
          handleKeyDown = (e) => {
      
              //Fetch node list from which required elements could be grabbed as needed.
              const modal = document.getElementById("modal_parent");
              const tags = [...modal.querySelectorAll('select, input, textarea, button, a, li')].filter(e1 => window.getComputedStyle(e1).getPropertyValue('display') === 'block');
              const focusable = modal.querySelectorAll('button, [href], input, select, textarea, li, a,[tabindex]:not([tabindex="-1"])');
              const firstFocusable = focusable[0];
              const lastFocusable = focusable[focusable.length - 1];
      
              if (e.ctrlKey || e.altKey) {
                  return;
              }
      
              const keys = {
                  9: () => { //9 = TAB
                      if (e.shiftKey && e.target === firstFocusable) {
                          lastFocusable.focus();
                      }
      
                      if (e.target === lastFocusable) {
                          firstFocusable.focus();
                      }
                  }
              };
      
              if (keys[e.keyCode]) {
                  keys[e.keyCode]();
              }
          }
      }
      

      【讨论】:

        【解决方案3】:

        我找到了一个非常简单的 vanillaJS 解决方案,可以在任何现代浏览器中使用。并且可以很容易地适应任何 React 组件。 它不需要任何额外的模块或复杂的逻辑、看键或诸如此类的东西。

        const container=document.querySelector("_selector_for_the_container_")
        
        //optional: needed only if the container element is not focusable already
        container.setAttribute("tabindex","0")
        
        container.addEventListener("focusout", (ev)=>{
          if (!container.contains(ev.relatedTarget)) container.focus()
        })
        

        操作方式很简单:

        • 使容器具有焦点(如果还没有的话)
        • focusout 事件添加一个事件监听器,当焦点即将移出容器时触发该事件
        • 检查焦点的下一个目标是否确实在容器之外,如果是,则将焦点放回容器本身

        最后一次检查是必要的,因为当焦点从容器内的一个元素移动到另一个元素时,focusout 事件也会触发。

        注意:焦点可以离开页面,例如浏览器的地址栏。这似乎无法避免 - 至少根据我在 Chrome 中的测试。

        【讨论】:

          猜你喜欢
          • 2016-11-08
          • 1970-01-01
          • 2013-11-05
          • 2019-07-03
          • 2010-09-21
          • 1970-01-01
          • 1970-01-01
          • 2018-02-04
          • 1970-01-01
          相关资源
          最近更新 更多