Как мне сделать div или доступными с помощью клавиш со стрелками вверх / вниз

#javascript #html #wai-aria #web-accessibility

#javascript #HTML #Специальные возможности #вай-ария

Вопрос:

Итак, у меня есть несколько div файлов, расположенных один под другим. Я хочу, чтобы они были «доступны через Интернет» с помощью клавиш со стрелками, то есть, если я нажму клавишу down, следующая div должна быть выделена, а если я нажму клавишу up, должна быть выделена предыдущая.

Если бы у меня была кнопка, я мог бы использовать tabIndex свойство для ее достижения.

Дело в том, что div s не являются элементами, подлежащими действию. Так что это может сбить с толку пользователей, если я каким-то образом сделаю их доступными через вкладки. Следовательно, я хочу это только с помощью клавиш со стрелками на клавиатуре.

Я даже готов преобразовать его в <ul><li><li></ul> , если можно добиться желаемого поведения.

 <div>Div 1</div>
<div>Div 2</div>
<div>Div 3</div>
<div>Div 4</div>  

Это то, что я пробовал с <li> s. Но если я нажму на первый li , я не могу перейти и выделить следующий, нажав стрелку вниз.

 <ul role="menu" aria-label="menu">
            <li role="menuitem" tabindex="0">list 1</li>
            <li role="menuitem" tabindex="0">list 2</li>
            <li role="menuitem" tabindex="0">list 3</li>
            <li role="menuitem" tabindex="0">list 4</li>
            <li role="menuitem" tabindex="0">list 5</li>
    </ul>  

Комментарии:

1. Пожалуйста, определите «доступный div».

2. @Teemu Accessibility (веб-доступность) — доступно для пользователей с различными возможностями, которые используют клавиатуру для навигации или для чтения содержимого страницы.

3. Но что это означает на практике? Вы хотите выделить div или просто прокрутить до него или что-то еще?

4. Затем преобразуйте их в ul lis

5. @Teemu да, выделения достаточно.

Ответ №1:

Существует виджет ARIA, который позволяет осуществлять навигацию с клавиатуры с помощью клавиш со стрелками, с одной остановкой табуляции вместо многих: ваш список похожих <div> файлов может быть сформирован в виде сетки с одним столбцом.

Изначально будет получена ячейка в первой строке tabindex="0" , а все остальные — tabindex="-1" .

Используя JavaScript, наведение стрелки вниз фокусирует ячейку во второй строке. Оно выполняется tabindex="0" одновременно с получением ячейки в первой строке tabindex="-1" , так что остается только одна ячейка, доступная для табуляции / фокусировки. Полная обработка клавиатуры указана в примерах / документах.

В JavaScript вам нужно будет отслеживать текущий элемент, на котором сосредоточен.

Вот как я бы настроил это с помощью React:

 class MyGrid extends React.Component {
    constructor(props) {
        super(props)

        this.state = {
            focusedItemIndex: 0
        }

        this.cellRefs = []

        this.handleFocus = (index => {
            this.setState(() => ({
                focusedItemIndex: index
            }))
        }).bind(this)

        this.handleKeydown = ((index, {
            key
        }) => {
            switch (key) {
                case 'ArrowUp': {
                    this.setState(() => ({
                        focusedItemIndex: Math.max(0, index - 1)
                    }))
                }
                break
                case 'ArrowDown': {
                    this.setState(() => ({
                        focusedItemIndex: Math.min(index   1, this.props.items.length - 1)
                    }))
                }
                break
                case 'Home': {
                    this.setState(() => ({
                        focusedItemIndex: 0
                    }))
                }
                break
                case 'End': {
                    this.setState(() => ({
                        focusedItemIndex: this.props.items.length - 1
                    }))
                }
                break
                default: {
                    return
                }
            }
        }).bind(this)
    }
    componentDidUpdate(_, prevState) {
        if (prevState.focusedItemIndex !== this.state.focusedItemIndex) {
            this.cellRefs[this.state.focusedItemIndex].focus()
        }
    }
    render() {
        return (
            <div
                role="grid"
                aria-label="This needs a label">
                <div role="row">
                    {this.props.items.map((item, index) => (
                        <div
                            role="cell"
                            key={item}
                            ref={el => this.cellRefs[index] = el}
                            tabIndex={this.state.focusedItemIndex === index ? '0' : '-1'}
                            onFocus={this.handleFocus.bind(this, index)}
                            onKeyDown={this.handleKeydown.bind(this, index)}>
                            {item}
                        </div>
                    ))}
                </div>
            </div>
        )
    }
}

ReactDOM.render(
    <MyGrid items={['A', 'B', 'C']} />,
    document.querySelector('body')
)
  

Комментарии:

1. Учитывая, что OP не помечен и не упомянут React, насколько точно вы представляете, насколько уместен этот ответ?

2. @floodlitworld В комментарии спрашивает, доступна ли версия ответа на jQuery в React, поэтому я использовал React, чтобы приблизить их к тому, что они могли бы использовать. Я не думаю, что это заслуживает понижения.

Ответ №2:

Привет, вы должны использовать jquery для того, что вы хотите. Вашим последним источником будет это:

 $(function(){
        document.onkeydown = function(e) {
            var selected = $('.mnu-item:focus').index();

            var next = selected   1;
            var pre = selected - 1;

            if(pre < 0)
                pre = 0;
            if(next > $('.mnu-item').length)
                next = $('.mnu-item').length;

            if(e.which == 38) { // up
                $('.mnu-item').eq(pre).focus();
            }
            if(e.which == 40) { // down
                $('.mnu-item').eq(next).focus();
            }
            e.preventDefault(); // prevent the default action (scroll / move caret)
        };
    })  
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul role="menu" aria-label="menu">
    <li role="menuitem" tabindex="0" class="mnu-item">list 1</li>
    <li role="menuitem" tabindex="0" class="mnu-item">list 2</li>
    <li role="menuitem" tabindex="0" class="mnu-item">list 3</li>
    <li role="menuitem" tabindex="0" class="mnu-item">list 4</li>
    <li role="menuitem" tabindex="0" class="mnu-item">list 5</li>
</ul>  

Я надеюсь, что это будет полезно.

Комментарии:

1. Хм, я вижу это. Я хотел избежать jquery / javascript. Но, похоже, это единственный способ. Кстати, есть идеи, как это сделать с помощью react-hooks?

2. У вас есть другой способ, и вы можете написать комментарий под своим списком и сказать, используйте «Кнопку табуляции, чтобы изменить выбранную строку». Но я не знаю react и понятия не имею об этом!

3. Не беспокойтесь. Для этого я свяжу события keyup и key down в react. Дело в том, что для достижения этого нам придется перехватывать события нажатия клавиш.