首页 > 解决方案 > 带有 React 的漫游 tabindex

问题描述

在 React 中制作“漫游 tabindex”的最简单方法是什么?它基本上tabindex=0/-1是在子元素之间切换焦点。只有一个元素拥有tabindexof 0,而其他元素接收-1。箭头键tabindex在子元素之间切换,并聚焦它。

现在,我做一个所需类型的简单子映射,并设置indexprop 和 get ref,以便以后使用。它看起来很健壮,但可能有更简单的解决方案吗?

我当前的解决方案(伪javascript,仅用于想法说明):

ElementWithFocusManagement.js

function recursivelyMapElementsOfType(children, isRequiredType, getProps) {
  return Children.map(children, function(child) {
    if (isValidElement(child) === false) {return child;}

    if (isRequiredType(child)) {

      return cloneElement(
        child,
        // Return new props
        // {
        //   index, iterated in getProps closure
        //   focusRef, saved to `this.focusable` aswell, w/ index above
        // }
        getProps()
      );
    }

    if (child.props.children) {
      return cloneElement(child, {
        children: recursivelyMapElementsOfType(child.props.children, isRequiredType, getProps)
      });
    }

    return child;
  });
}

export class ElementWithFocusManagement {
  constructor(props) {
    super(props);

    // Map of all refs, that should receive focus
    // {
    //   0: {current: HTMLElement}
    //   ...
    // }
    this.focusable = {};
    this.state = {
      lastInteractionIndex: 0
    };
  }

  handleKeyDown() {
    // Handle arrow keys,
    // check that element index in `this.focusable`
    // update state if it is
    // focus element
  }

  render() {
    return (
      <div onKeyDown={this.handleKeyDown}>
        <Provider value={{lastInteractionIndex: this.state.lastInteractionIndex}}>
          {recursivelyMapElementsOfType(
            children,
            isRequiredType, // Check for required `displayName` match
            getProps(this.focusable) // Get index, and pass ref, that would be saved to `this.focusable[index]`
          )}
        </Provider>
      </div>
    );
  }
}

with-focus.js

export function withFocus(WrappedComponent) {
  function Focus({index, focusRef, ...props}) {
    return (
      <Consumer>
        {({lastInteractionIndex}) => (
          <WrappedComponent
            {...props}

            elementRef={focusRef}
            tabIndex={lastInteractionIndex === index ? 0 : -1}
          />
        )}
      </Consumer>
    );
  }

  // We will match for this name later
  Focus.displayName = `WithFocus(${WrappedComponent.name})`;

  return Focus;
}

任何东西.js

const FooWithFocus = withFocus(Foo);

<ElementWithFocusManagement> // Like toolbar, dropdown menu and etc.
  <FooWithFocus>Hi there</FooWithFocus> // Button, menu item and etc.

  <AnythingThatPreventSimpleMapping>
    <FooWithFocus>How it's going?</FooWithFocus>
  </AnythingThatPreventSimpleMapping>

  <SomethingWithoutFocus />
</ElementWithFocusManagement>

标签: javascriptreactjsaccessibility

解决方案


react-roving-tabindex看起来相当不错。


推荐阅读