javascript 使 ul 列表像选择输入一样工作

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15700834/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 01:47:01  来源:igfitidea点击:

Make ul list work like select input

javascriptjqueryhtml

提问by Menno van leeuwen

I want to be able to use a ul list as an select form element, for styling reasons.

出于样式原因,我希望能够使用 ul 列表作为选择表单元素。

I'm able to populate an hidden input with my code (not included in this jsfiddle), and so far so good.But now I'm trying to let my ul behave like the select input when the keyboard is pressed, or the mouse is used.

我能够用我的代码填充一个隐藏的输入(不包含在这个 jsfiddle 中),到目前为止一切都很好。但是现在我试图让我的 ul 在按下键盘或鼠标时表现得像选择输入用来。

In my previous question i had some problems with keyboard controls. They are now fixed. See: Autoscroll on keyboard arrow up/down

在我之前的问题中,我在键盘控制方面遇到了一些问题。它们现在已修复。请参阅:键盘上的自动滚动箭头向上/向下

The problem that remains is that the mouse is not ignored when the keyboard buttons are pressed. This is causing the "hover effect" to listen to the keyboard input first, but than immediately going to the mouse and select this li item as being selected.

仍然存在的问题是按下键盘按钮时不会忽略鼠标。这导致“悬停效果”首先听取键盘输入,然后立即使用鼠标并选择此 li 项目作为被选中。

This can be seen in my jsfiddle example: http://jsfiddle.net/JVDXT/3/

这可以在我的 jsfiddle 示例中看到:http: //jsfiddle.net/JVDXT/3/

My javascript code:

我的javascript代码:

// scrollTo plugin 
  $.fn.scrollTo = function( target, options, callback ){
  if(typeof options == 'function' && arguments.length == 2){ callback = options; options = target; }
  var settings = $.extend({
    scrollTarget  : target,
    offsetTop     : 100,
    duration      : 0,
    easing        : 'linear'
  }, options);
  return this.each(function(){
    var scrollPane = $(this);
    var scrollTarget = (typeof settings.scrollTarget == "number") ? settings.scrollTarget : $(settings.scrollTarget);
    var scrollY = (typeof scrollTarget == "number") ? scrollTarget : scrollTarget.offset().top + scrollPane.scrollTop() - parseInt(settings.offsetTop);
    scrollPane.animate({scrollTop : scrollY }, parseInt(settings.duration), settings.easing, function(){
      if (typeof callback == 'function') { callback.call(this); }
    });
  });
}


//My code
//The function that is listing the the mouse
jQuery(".btn-group .dropdown-menu li").mouseover(function() {
        console.log('mousie')
        jQuery(".btn-group .dropdown-menu li").removeClass('selected');
        jQuery(this).addClass('selected');
})  

//What to do when the keyboard is pressed
jQuery(".btn-group").keydown(function(e) {
    if (e.keyCode == 38) { // up
        console.log('keyup pressed');
        var selected = jQuery('.selected');
        jQuery(".btn-group .dropdown-menu li").removeClass('selected');
        if (selected.prev().length == 0) {
            selected.siblings().last().addClass('selected');
        } else {
            selected.prev().addClass('selected');
            jQuery('.btn-group .dropdown-menu').scrollTo('.selected');
        }
    }
    if (e.keyCode == 40) { // down
        console.log('keydown');
        var selected = jQuery('.selected');
        jQuery(".btn-group .dropdown-menu li").removeClass('selected');
        if (selected.next().length == 0) {
            selected.siblings().first().addClass('selected');
        } else {
            selected.next().addClass('selected');
            jQuery('.btn-group .dropdown-menu').scrollTo('.selected');
        }
    }
});

So could anyone teach me how to igonore the mouse when the keyboard buttons are pressed, but listing to the mouse when it's touched again by the user. Like the default select input form field.

那么谁能教我如何在按下键盘按钮时忽略鼠标,但在用户再次触摸鼠标时列出鼠标。就像默认的选择输入表单字段一样。

Update

更新

Here's a new jsfiddle.

这是一个新的jsfiddle

回答by coma

Check this out:

看一下这个:

http://jsfiddle.net/coma/9KvhL/25/

http://jsfiddle.net/coma/9KvhL/25/

(function($, undefined) {

    $.fn.dropdown = function() {

        var widget = $(this);
        var label = widget.find('span.valueOfButton');
        var list = widget.children('ul');
        var selected;
        var highlighted;

        var select = function(i) {

            selected = $(i);
            label.text(selected.text());

        };

        var highlight = function(i) {

            highlighted = $(i);

            highlighted
            .addClass('selected')
            .siblings('.selected')
            .removeClass('selected');
        };

        var scroll = function(event) {

            list.scrollTo('.selected');

        };

        var hover = function(event) {

            highlight(this);

        };

        var rebind = function(event) {

            bind();

        };

        var bind = function() {

            list.on('mouseover', 'li', hover);
            widget.off('mousemove', rebind);

        };

        var unbind = function() {

            list.off('mouseover', 'li', hover);
            widget.on('mousemove', rebind);

        };

        list.on('click', 'li', function(event) {

            select(this);

        });

        widget.keydown(function(event) {

            unbind();

            switch(event.keyCode) {

                case 38:
                    highlight((highlighted && highlighted.prev().length > 0) ? highlighted.prev() : list.children().last());

                    scroll();
                    break;

                case 40:
                    highlight((highlighted && highlighted.next().length > 0) ? highlighted.next() : list.children().first());

                    scroll();
                    break;

                case 13:
                    if(highlighted) {

                        select(highlighted);

                    }
                    break;

            }

        });

        bind();

    };

    $.fn.scrollTo = function(target, options, callback) {

        if(typeof options === 'function' && arguments.length === 2) {

            callback = options;
            options = target;
        }

        var settings = $.extend({
            scrollTarget  : target,
            offsetTop     : 185,
            duration      : 0,
            easing        : 'linear'
        }, options);

        return this.each(function(i) {

            var scrollPane = $(this);
            var scrollTarget = (typeof settings.scrollTarget === 'number') ? settings.scrollTarget : $(settings.scrollTarget);
            var scrollY = (typeof scrollTarget === 'number') ? scrollTarget : scrollTarget.offset().top + scrollPane.scrollTop() - parseInt(settings.offsetTop, 10);

            scrollPane.animate({scrollTop: scrollY}, parseInt(settings.duration, 10), settings.easing, function() {

                if (typeof callback === 'function') {

                    callback.call(this);
                }

            });

        });

    };

})(jQuery);

$('div.btn-group').dropdown();

The key is to unbind the mouseover and rebind when mouse moves.

关键是解除鼠标悬停,鼠标移动时重新绑定。

I refactored it a little by using a closure function, adding the logic to a jQuery method called dropdownso you can reuse it, using switch instead of a bunch of if's and more things.

我通过使用闭包函数对其进行了一些重构,将逻辑添加到名为dropdown的 jQuery 方法中,以便您可以重用它,使用 switch 而不是一堆 if 等等。

Well, there are bazillions of plugins to transform a select to a list:

嗯,有无数的插件可以将选择转换为列表:

http://ivaynberg.github.io/select2/

http://ivaynberg.github.io/select2/

http://harvesthq.github.io/chosen/

http://harvesthq.github.io/chosen/

http://meetselva.github.io/combobox/

http://meetselva.github.io/combobox/

and I have mine too!(ready for touch devices using the same trick as http://uniformjs.com)

我也有我的!(准备好使用与http://uniformjs.com相同的技巧的触摸设备)

https://github.com/coma/jquery.select

https://github.com/coma/jquery.select

But this question is about taking that HTML and make it behave like a select avoiding the hover issue right?

但是这个问题是关于采用该 HTML 并使其表现得像一个避免悬停问题的选择,对吗?

回答by Mathijs Flietstra

Here's a solution, I'm using mousemoveas this will ensure that the right list item is selected as soon as the mouse starts moving again, with mouseoverit only starts to select a list item upon entering a new list item:

这是一个解决方案,我正在使用它,mousemove因为这将确保在鼠标再次开始移动时立即选择正确的列表项,mouseover它仅在输入新列表项时才开始选择列表项:

Take the anonymous function and give it a name:

取匿名函数并为其命名:

function mousemove() {
  console.log('mousie')
  jQuery(".btn-group .dropdown-menu li").removeClass('selected');
  jQuery(this).addClass('selected');
}

Declare a global variable mousemovedindicating if the mouse has moved over the document and set it to false, on mousemoveover the document, set it to trueand attach the mousemovefunction to the mousemoveevent on the list items.

声明一个全局变量,mousemoved指示鼠标是否移过文档并将其设置为false,在mousemove文档上方,将其设置为true并将mousemove函数附加到mousemove列表项上的事件。

var mousemoved = false;

jQuery(document).mousemove(function() {
  if(!mousemoved) {
    $('.btn-group .dropdown-menu li').mousemove(mousemove);  
    mousemoved = true;    
  }
})  

As soon as a key is pressed (at the start of the keydown event), use jQuery's .off()method to remove the mousemoveevent on the list items if it is present, and set mousemovedto falseto ensure the mousemoveevent doesn't get attached again until the mouse is moved again.

作为一个按键(在keydown事件的开始),便使用jQuery的.off()方法来删除mousemove的列表项的事件(如果存在),并设置mousemovedfalse确保该mousemove事件确实,直到鼠标是不会被再次安装又动了。

jQuery(".btn-group").keydown(function(e) {

  $('.btn-group .dropdown-menu li').off('mousemove');
  mousemoved = false; 
  ... // Some more of your code

Here's a jsFiddle.

这是一个jsFiddle.

回答by Selvakumar Arumugam

I tried to solve your issue by prevent autoscroll, adding tabindex on the li, setting the focus on active, and using a flag to suppress mouse.

我试图通过防止自动滚动、在 li 上添加 tabindex、将焦点设置为活动以及使用标志来抑制鼠标来解决您的问题。

Fixed fiddle:http://jsfiddle.net/8nKJT/[fixed an issue in Chrome ]

固定小提琴:http : //jsfiddle.net/8nKJT/[修复了 Chrome 中的一个问题]

http://jsfiddle.net/RDSEt/

http://jsfiddle.net/RDSEt/

The issue is because of the automatic scrollwhich is triggered on keydownthat again triggers mouseentermesses the selection of the li.

问题是自动的,因为scroll这是在触发keydown该再次触发mouseenter混乱的选择li

Note:The differences with the other approaches(answers here) I noticed is it scrolls on every keypressinstead of scrolling only after reaching the top or bottom(normal behavior). You will feel the difference when you check the demo side-by-side.

注意:我注意到与其他方法(此处的答案)的不同之处在于它在每次按键时滚动,而不是仅在到达顶部或底部后滚动(正常行为)。当您并排查看演示时,您会感觉到不同。

Below is the list of change description and a small demo to explain how it was fixed,

下面是更改描述列表和一个小演示来解释它是如何修复的,

Check out the demo and code changes too (added few improvements) and let me know.

查看演示和代码更改(添加了一些改进)并告诉我。

Also thanks to your question, I noticed this issue and bunch of other issue in one of the plugin I wrote.

也感谢您的问题,我在我编写的一个插件中注意到了这个问题和其他一些问题。

I wrote a plugin few months back to filter options and also act exactly like a drop down.

几个月前我写了一个插件来过滤选项,并且也完全像一个下拉菜单。

DEMO: http://jsfiddle.net/nxmBQ/[change filterTypeto ''to turnoff the filtering ]

The original plugin page is http://meetselva.github.io/combobox/

.. more

DEMO:http://jsfiddle.net/nxmBQ/[变化filterType''到截止滤波]

原来的插件页面是http://meetselva.github.io/combobox/

.. 更多的

回答by Joseph Myers

The problem showing up is that when the mouse is left over a part of the expanded list, then selecting using the keys is nullified because the selection made by the keyboard immediately reverts to the item that happens to be under the mouse.

出现的问题是,当鼠标悬停在扩展列表的一部分上时,使用按键进行的选择无效,因为键盘所做的选择会立即恢复到恰好位于鼠标下方的项目。

You can solve this problem and retain all functionality without doing any complicated conditional behavior or any removing of event handlers.

您可以解决此问题并保留所有功能,而无需执行任何复杂的条件行为或删除任何事件处理程序。

Just change your mouseover event handler to be a mousemove event handler. This way any keyboard navigation and selection is listened to and the mouse position is ignored anytime that the user is using the keyboard to select. And anytime the mouse is being used to select, then the mouse is listened to.

只需将鼠标悬停事件处理程序更改为 mousemove 事件处理程序即可。这样,任何键盘导航和选择都会被监听,并且鼠标位置在用户使用键盘进行选择的任何时候都会被忽略。并且无论何时使用鼠标进行选择,都会监听鼠标。

This sounds trivial but it seems to make your JS Fiddle behave perfectly and without any conflicting behavior between mouse and keyboard. Like this:

这听起来微不足道,但它似乎使您的 JS Fiddle 表现完美,并且鼠标和键盘之间没有任何冲突行为。像这样:

//The function that is listening to the mouse
jQuery(".btn-group .dropdown-menu li").mousemove...

(your code continues unchanged, only replacing mouseover with mousemove)

(您的代码保持不变,仅用 mousemove 替换 mouseover)

回答by lostsource

You could try this solution. It ignores the mousemoveevent if the coordinates have not changed (since the last mousemove event)

你可以试试这个解决方案。mousemove如果坐标没有改变(自上次 mousemove 事件以来),它会忽略该事件

//The function that is listing the the mouse
var lastOffsets = "";

jQuery(".btn-group .dropdown-menu li").mouseover(function(e) {
        var curOffsets = e.clientX+":"+e.clientY;
        if(curOffsets == lastOffsets) {
           // mouse did not really move
            return false;
        }

        lastOffsets = curOffsets;

        ///// rest of your code
}

Updated fiddle to verify if this is what you were after: http://jsfiddle.net/pdW75/1/

更新小提琴以验证这是否是您所追求的:http: //jsfiddle.net/pdW75/1/

回答by likeitlikeit

ApproachA reasonable solution should imitate the behavior of other UI elements that serve a similar purpose. On all checked systems (Windows, Linux, major browsers), drop-down boxes behave as follows:

方法一个合理的解决方案应该模仿其他具有类似目的的 UI 元素的行为。在所有选中的系统(Windows、Linux、主要浏览器)上,下拉框的行为如下:

Mousing over an item highlights it. Pressing arrow keys change the selected element, and scroll accoringly. Moving the mouse selects the element underneath. If the selection is empty, pressing downselects the first element. Pressing upselects the last element.

将鼠标悬停在项目上会突出显示它。按箭头键更改所选元素,并相应地滚动。移动鼠标选择下面的元素。如果选择为空,则按down选择第一个元素。按up选择最后一个元素。

SolutionThis codeillustrates my approach to imitating the described behavior. It's kinda cool, try it...

解决方案代码说明了我模仿所描述行为的方法。挺不错的,试试看

Additional ConsiderationsThere would be a number of other options to suppress unwanted mouse movement to change the selected element. These include:

其他注意事项还有许多其他选项可以抑制不需要的鼠标移动以更改所选元素。这些包括:

  • Keeping a state of last input method. If last selection was using the keyboard, hovering over an element will not select it, only clicking will
  • ignoring the mouseoverevent if the coordinates have not changed by a specified distance, e.g. 10 pixels
  • ignoring mouseoverif the user has ever used the keyboard
  • 保持上次输入法的状态。如果最后一次选择是使用键盘,将鼠标悬停在一个元素上不会选择它,只有点击才会
  • mouseover如果坐标没有改变指定的距离,例如 10 像素,则忽略该事件
  • 忽略mouseover用户是否曾经使用过键盘

However, at least for an application accessible to the public, it's always best to stick with established UI patterns.

但是,至少对于公众可以访问的应用程序,最好始终坚持使用既定的 UI 模式。

回答by 6502

You could use a global to ignore the mouseoverevent if a keydown was pressed recently on the widget. For example:

mouseover如果最近在小部件上按下了按键,您可以使用全局来忽略该事件。例如:

var last_key_event = 0;

jQuery(".btn-group .dropdown-menu li").mouseover(function() {
    if ((new Date).getTime() > last_key_event + 1000) {
        console.log('mousie')
        jQuery(".btn-group .dropdown-menu li").removeClass('selected');
        jQuery(this).addClass('selected');
    }
});

Then the keydownhandler can set when it was handled to avoid interaction with the mouse:

然后keydown处理程序可以设置何时处理以避免与鼠标交互:

//What to do when the keyboard is pressed
jQuery(".btn-group").keydown(function(e) {
    last_key_event = (new Date).getTime();
    ...
});

May be it could make sense to have the last_key_eventvariable separate for each widget instead of being a global.

last_key_event每个小部件的变量分开而不是全局变量可能是有意义的。