带React的漫游tabindex

时间:2018-08-23 19:24:25

标签: javascript reactjs accessibility

在React中制作“ roving tabindex”的最简单方法是什么?基本上是在子元素之间切换焦点和tabindex=0/-1。仅单个元素具有tabindex中的0,而其他元素接收-1。箭头键在子元素之间切换tabindex并对其进行聚焦。

现在,我对所需类型进行简单的子级映射,并设置index prop并获取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;
}

Anything.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>

2 个答案:

答案 0 :(得分:0)

此库可能会帮助您:
https://www.npmjs.com/package/keyboard-navigator
它是一个可配置的选项卡和焦点管理库,还提供了箭头导航策略。

答案 1 :(得分:0)

react-roving-tabindex看起来不错。