【问题标题】:Vanilla javascript Trap Focus in modal (accessibility tabbing )模式中的香草 javascript 陷阱焦点(可访问性选项卡)
【发布时间】:2015-10-07 15:49:34
【问题描述】:

这应该很简单,但由于某种原因它不起作用,我在正确的时间获得了正确的 console.logs,但焦点没有转到正确的位置,请参考我的 jsfiddle

https://jsfiddle.net/bqt0np9d/

function checkTabPress(e) {
    "use strict";
    // pick passed event of global event object
    e = e || event;

    if (e.keyCode === 9) {
        if (e.shiftKey) {
            console.log('back tab pressed');
            firstItem.onblur=function(){
                console.log('last a focus left');
                lastItem.focus();
            };
            e.preventDefault();
        }
        console.log('tab pressed');
        lastItem.onblur=function(){
            console.log('last a focus left');
            firstItem.focus();
        };
        e.preventDefault();
    }
}
modal.addEventListener('keyup', checkTabPress);

【问题讨论】:

    标签: javascript focus accessibility tabbing


    【解决方案1】:

    我必须将焦点锁定在我们在 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]();
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      其中一个问题是您使用的是keyup 而不是keydown。只有在选项卡已经触发后,keyup 才会触发。但是,对代码进行更改会导致键盘被困在其中一个链接上。代码有缺陷。

      这里有一些代码可以做你想做的事(使用 jQuery)

      http://dylanb.github.io/javascripts/periodic-1.1.js

      // Add keyboard handling for TAB circling
      
        $modal.on('keydown', function (e) {
          var cancel = false;
          if (e.ctrlKey || e.metaKey || e.altKey) {
            return;
          }
          switch(e.which) {
            case 27: // ESC
              $modal.hide();
              lastfocus.focus();
              cancel = true;
              break;
            case 9: // TAB
              if (e.shiftKey) {
                if (e.target === links[0]) {
                  links[links.length - 1].focus();
                  cancel = true;
                }
              } else {
                if (e.target === links[links.length - 1]) {
                  links[0].focus();
                  cancel = true;
                }
              }
              break;
          }
          if (cancel) {
            e.preventDefault();
          }
        });
      

      您可以在此处查看此对话框的工作版本

      http://dylanb.github.io/periodic-aria11-attributes.html

      单击其中一个彩色框中的文本以查看弹出对话框。

      【讨论】:

      • 谢谢,不过我已经让它与 jQuery 一起工作了。对于我正在从事的项目,我需要使用纯 JavaScript。关于如何修改当前代码以使其正常工作的任何想法?
      【解决方案3】:

      e.preventDefault()keyup 事件没有影响(因为默认浏览器操作已被触发)

      尽管如此,您的示例仍然有效。但前提是模态前后有链接

      如果您使用以下内容更改您的 HTML 代码,在模态框之前添加一个链接,在模态框之后添加一个链接;你会发现你的注意力被困在了模态中:

       <a href="#">other link</a>
       <div id="modal">
           <a href="#">Link One</a>
           <a href="#">Link Two</a>
       </div>
       <a href="#">other link</a>
      

      那是因为在这种情况下没有默认的浏览器操作,然后没有阻止的操作。

      【讨论】:

        【解决方案4】:

        在模态框内捕获焦点很难靠自己完成。如果您能够在项目中安装第三方依赖项,则可以使用 focus-trap 包。

        您可以使用原生 Javascript 轻松地将焦点锁定到任何组件;

        import { createFocusTrap } from 'focus-trap'
        
        const modal = document.getElementById('modal')
        
        const focusTrap = createFocusTrap('#modal', {
            onActivate: function () {
                modal.className = 'trap is-visible'
            },
            onDeactivate: function () {
                modal.className = 'trap'
            },
        })
        
        document.getElementById('show').addEventListener('click', function () {
            focusTrap.activate()
        })
        
        document.getElementById('hide').addEventListener('click', function () {
            focusTrap.deactivate()
        })
        

        甚至反应;

        import React from 'react'
        import ReactDOM from 'react-dom'
        // Use the wrapper package of `focus-trap` to use with React.
        import FocusTrap from 'focus-trap-react'
        
        const Demo = () => {
            const [showModal, setShowModal] = React.useState(false)
        
            return (
                <div>
                    <button onClick={() => setShowModal(true)}>show modal</button>
        
                    <FocusTrap active={showModal}>
                        <div id="modal">
                            Modal with <a href="#">with</a> <a href="#">some</a>{' '}
                            <a href="#">focusable</a> elements.
                            <button onClick={() => setShowModal(false)}>
                                hide modal
                            </button>
                        </div>
                    </FocusTrap>
                </div>
            )
        }
        
        ReactDOM.render(<Demo />, document.getElementById('demo'))
        
        

        我写了一篇关于 here 包的小文章,解释了如何将它与 vanilla Javascript 或 React 一起使用。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-03-11
          • 2021-06-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-05-09
          • 2021-11-09
          相关资源
          最近更新 更多