简体   繁体   English

带有 React 的 Roving tabindex

[英]Roving tabindex w/ React

What is most simple way to make "roving tabindex" in React?在 React 中制作“移动 tabindex”的最简单方法是什么? It's basically switch focus and tabindex=0/-1 between child elements.它基本上是在子元素之间切换焦点和tabindex=0/-1 Only a single element have tabindex of 0 , while other receives -1 .只有一个元素的tabindex0 ,而其他元素接收-1 Arrow keys switch tabindex between child elements, and focus it.箭头键在子元素之间切换tabindex ,并聚焦它。

For now, I do a simple children mapping of required type, and set index prop and get ref , to use it later.现在,我做了一个所需类型的简单子映射,并设置index prop 和 get ref ,以便稍后使用。 It looks robust, but may be there more simple solution?它看起来很健壮,但可能有更简单的解决方案吗?

My current solution (pseudo-javascript, for idea illustration only):我目前的解决方案(伪javascript,仅用于创意说明):

ElementWithFocusManagement.js 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 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任何东西.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>

react-roving-tabindex看起来不错。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM