JS ArrowDown addEventListener для нескольких элементов в цикле (демо)

Есть следующий слушатель для клавиатуры ArrowDown событие (это ключевой код 40):

window.onload = function() {    
var itemsContainer = document.getElementById('cities-drop');
document.addEventListener('keyup',function(event){
if (event.keyCode == 40 && itemsContainer.style.display=='block') {
event.preventDefault();
    for (var i=0;i<itemsContainer.children.length-1;i++){
        if (itemsContainer.getAttribute('class').substr('hovered')!=-1){
            itemsContainer.children[i].setAttribute('class','');
            itemsContainer.children[i].nextSibling.setAttribute('class','hovered');
                //break;
                }
            }
        }
    });

в этом случае hovering переходит к последнему элементу в списке после ArrowDown нажат.

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

Не могу понять принцип, как это сделать, что слушатель всегда слушает...

РЕДАКТИРОВАТЬ демо
возможно, это вопрос закрытия, но я не уверен

3 ответа

Решение

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

if (itemsContainer.getAttribute('class').indexOf('hovered') != -1)



Более подробно: то, что вы на самом деле делали, использовало substr со строковым значением для start индекс. Не уверен, каков будет результат этого, но, по-видимому, это не -1, поскольку условное выражение возвращало true каждый раз, вызывая зависание следующего элемента КАЖДЫЙ раз, вплоть до самого конца списка. С break оператор там, он выполнял оператор if в первом элементе (вызывая "зависание" второго элемента), а затем завершал работу.

Я бы оставил break после исправления вашего кода, так что цикл останавливается после того, как он находит свое совпадение, и не перебирает остальные элементы без необходимости.


РЕДАКТИРОВАТЬ:

Я нашел еще пару проблем в вашем коде. Вот пример, который работает для меня в IE и FF, по крайней мере (не тестировал в Safari, Opera или Chrome):

<html>
<head>
    <style type="text/css">
        .hovered
        {
            color:red;
        }
    </style>
    <script type="text/JavaScript">
        function move(event)
        {
            var itemsContainer = document.getElementById('cities-drop');
            if (event.keyCode == 40 && itemsContainer.style.display == 'block')
            {
                if (event.preventDefault)
                    event.preventDefault();
                if (event.cancelBubble)
                    event.cancelBubble();
                if (event.stopImmediatePropagation)
                    event.stopImmediatePropagation();

                for (var i=0; i<itemsContainer.children.length-1; i++)
                {
                    if (itemsContainer.children[i].className.indexOf('hovered') != -1)
                    {
                        itemsContainer.children[i].className = "";
                        itemsContainer.children[i+1].className = "hovered";
                        break;
                    }
                }
            }
        };
    </script>
</head>
<body onkeydown="move(event)">
    <div id="cities-drop" style="display:block;">
        <p class="hovered">Item 1</p>
        <p>Item 2</p>
        <p>Item 3</p>
        <p>Item 4</p>
        <p>Item 5</p>
        <p>Item 6</p>
        <p>Item 7</p>
    </div>
</body>
</html>


Подробно: для меня в IE9 функция никогда не вызывалась. Вместо этого я просто сделал его обычной функцией и добавил onkeydown событие в body тег.

Далее, для совместимости с разными браузерами, вы должны убедиться, что event.preventDefault существует до его использования. Я получил ошибку JS в IE.

В вашем if-утверждении вы itemsContainer.getAttribute('class'), Во-первых, вам нужно было использовать itemsContainer.children[i], Во-вторых, .getAttribute('class') у меня не работал в IE, поэтому я переключился на просто .className,

И, наконец, itemsContainer.children[i].nextSibling не работает для меня, но достаточно просто изменить его на itemsContainer.children[i+1] чтобы получить следующий брат.

Я вижу несколько вещей, которые могут быть проблемой. Прежде всего, вы обновляетеitemsContainer.children[i].nextSibling который itemsContainer.children[i+1], Вот почему всегда последний элемент, который он выбрал, если вы пропустите разрыв. itemsComtainer[i+1] всегда будет зависать, если есть элемент, соответствующий классу.

Вторая проблема - это то, на что указывает Travesty3 в своем ответе.

Я также изменил условие if, чтобы проверить, находится ли класс hovered на одном из дочерних элементов, а не на самом контейнере.

if (itemsContainer.children[i].getAttribute('class').match('hovered'))

Я изменил обработчик событий со следующими строками кода, и это, кажется, работает нормально:

document.addEventListener('keyup',function(event){
            if (event.keyCode === 40 && itemsContainer.style.display==='block') {
                event.preventDefault();
                for (var i=0,l=itemsContainer.children.length;i<l;++i){
                    if (itemsContainer.children[i].getAttribute('class').match('hovered')){
                        itemsContainer.children[i].setAttribute('class','');
                        itemsContainer.children[i+1].setAttribute('class','hovered');
                        break;
                    }
                }
            }
        });

Имейте в виду, что создание такого выпадающего элемента управления - это довольно много работы. Пользователь ожидает навигации по нему с помощью клавиатуры. Чтобы обеспечить удобство работы с пользователем, вы должны использовать несколько клавиш, таких как клавиши со стрелками, вкладка для фокусировки элемента управления, пробел для его открытия и закрытия, буквенно-цифровой ввод для фокусировки на первом сопоставленном элементе и т. Д.

Если важно взаимодействие с пользователем, я бы порекомендовал использовать для этого фреймворк и плагин. Я лично предпочитаю jquery и jquery ui, и есть несколько плагинов для выпадающих меню. Другое преимущество состоит в том, что если клиент отключил JavaScript, или ваш JavaScript по какой-либо причине ошибся, большинство плагинов используют обычный элемент выбора select, который по-прежнему будет нормально работать функционально.

Я использовал этот плагин для коробки выбора для простого раскрывающегося списка: http://www.abeautifulsite.net/blog/2011/01/jquery-selectbox-plugin/

Изменить: я отменяю эту рекомендацию, так как она не работает, если несколько элементов имеют одинаковое имя. Если это важно, вы должны вместо этого воспользоваться плагином selectmenu для группы нитей: http://filamentgroup.com/lab/jquery_ui_selectmenu_an_aria_accessible_plugin_for_styling_a_html_select/// Редактировать

... и плагин автозаполнения jquery для комбинированного списка, также поддерживающий письменный ввод: http://jqueryui.com/demos/autocomplete/

Вы можете попробовать более простой подход вместо использования цикла:

window.onload = function() {    
    var itemsContainer = document.getElementById('cities-drop');

    document.addEventListener('keyup',function(event) {
        if (event.keyCode == 40 && itemsContainer.style.display=='block') {
            event.preventDefault();

            var previousHoveredChoice = itemsContainer.querySelector('.hovered');
            previousHoveredChoice.className = '';

            var currentHoveredChoice = previousHoveredChoice.nextSibling;
            if (currentHoveredChoice) {
                currentHoveredChoice.className = 'hovered';
            }
        }
    });

    //following code is copy-pasted from the live example 
    //just to close the onload function handler in this solution
    document.addEventListener('keyup',function(event){
        if (event.keyCode == 27) {

            if (document.getElementById('cities-drop').style.display=='block'){
                document.getElementById('cities-drop').style.display='none';
            }
        }

    });
    //end of copy-pasted code
};
Другие вопросы по тегам