【问题标题】:JS ArrowDown addEventListener for several elements in loop (demo)循环中几个元素的 JS ArrowDown addEventListener (demo)
【发布时间】:2012-02-08 20:54:32
【问题描述】:

有以下键盘监听器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 未注释,它会跳转到第二个元素并且不再跳转。

搞不懂原理,怎么办,那个听者一直在听……

编辑 live demo
也许,这是一个结束的问题,但我不确定

【问题讨论】:

  • 你想达到什么目的?自定义下拉菜单?
  • @Šime Vidas 你是绝对正确的
  • 你为什么要自己写?有提供此功能的库和框架。
  • @Šime Vidas 我知道。这是我的 JS 学习计划中的任务。我再说一遍 - 我用纯 JS 编写它。
  • 你想达到什么目的?是否要使用 ARROW_UP 和 ARROW_DOWN 键选择该列表中的项目?

标签: javascript events loops listener arrow-keys


【解决方案1】:

在查看您的代码并意识到您正在尝试做什么之后,我认为您的错误是使用 substr,而您应该使用 indexOf。这是更新的行:

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



更多细节: 您实际上在做的是使用 substrstart 索引的字符串值。不确定结果是什么,但显然不是 -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 存在。我在 IE 中遇到 JS 错误。

在你的 if 语句中,你有 itemsContainer.getAttribute('class')。首先,您需要使用itemsContainer.children[i]。其次,.getAttribute('class') 在 IE 中不适合我,所以我将其切换为 .className

最后,itemsContainer.children[i].nextSibling 对我不起作用,但它很简单,只需将其更改为 itemsContainer.children[i+1] 即可获得下一个兄弟姐妹。

【讨论】:

  • 是的,这是我犯的错误,我错误地使用了substr。无论如何这对我没有帮助,indexOf()match()javascript.info/play/pNQ9E 也没有。
  • 抱歉回复晚了。它在那个版本中对我有用,你在这里写的。从另一边,我不需要内联观察者onkeydown=move(event),所以当我尝试使用addEventListener时,我会给你反馈
  • @sergionni:请记住,IE 不支持addEventListener,它仍然是最常用的浏览器之一。您将在 Internet Explorer 中收到 Object doesn't support this property or method 的 Javascript 错误。如果必须使用此方法,则应先检查该函数是否存在。有关示例,请参见 stackoverflow.com/a/1695383
  • 是的,我知道attachEventaddEventListener。这不是问题。主要问题是我根本需要听众,而不是“内联观察者”。
  • @sergionni:我很困惑你所说的“内联观察者”是什么意思......使用内联方法附加事件仍将执行与使用 addEventListener 相同的功能。我所知道的唯一区别是,如果您尝试使用 inline 方法向同一事件添加另一个动作,前一个动作将被覆盖,而使用addEventListener,这两个动作都将执行。所以内联方法还是一个事件监听器,还是一样的。
【解决方案2】:

您可以尝试一种更简单的方法,而不是使用循环:

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

【讨论】:

    【解决方案3】:

    我发现有几件事可能是个问题。首先,你更新 itemsContainer.children[i].nextSiblingitemsContainer.children[i+1]。这就是为什么如果您跳过休息时间,它总是选择最后一个元素。 itemsComtainer[i+1] 如果有与该类匹配的项目,将始终悬停。

    第二个问题是 Travesty3 在他的回答中指出的。

    我还更改了 if 条件以检查悬停的类是否位于其中一个子级而不是容器本身。

    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/

    编辑:我要撤销这个建议,因为如果多个元素具有相同的名称,它就不起作用。如果这很重要,您应该查看 Filament Group 的选择菜单插件: http://filamentgroup.com/lab/jquery_ui_selectmenu_an_aria_accessible_plugin_for_styling_a_html_select/ //编辑

    ...以及组合框的 jquery 自动完成插件也支持书面输入: http://jqueryui.com/demos/autocomplete/

    【讨论】:

    • 您好像忘记更新 if 语句:if (itemsContainer.children[i].getAttribute('class').match('hovered')){
    猜你喜欢
    • 1970-01-01
    • 2021-12-03
    • 2021-06-22
    • 2014-07-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-21
    • 2018-03-25
    相关资源
    最近更新 更多