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
};