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

Make ul list work like select input

本文关键字:一样 工作 输入 ul 列表 选择      更新时间:2023-09-26

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

我可以用我的代码填充一个隐藏的输入(不包括在本文中),到目前为止一切顺利。但现在我试图让我的ul表现得像按下键盘或使用鼠标时的选择输入。

在我之前的问题中,我有一些键盘控制的问题。它们现在是固定的。参见:自动滚动键盘上的向上/向下箭头

仍然存在的问题是,当键盘按钮被按下时,鼠标并没有被忽略。这将导致"悬停效果"首先听取键盘输入,而不是立即转到鼠标并选择被选中的li项。

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

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

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

更新

这是一个新的jsfiddle。

看看这个:

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();

关键是在鼠标移动时解除绑定并重新绑定。

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

嗯,有无数的插件可以把一个select转换成一个list:

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

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

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

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

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

但这个问题是关于采取的HTML,使其行为像一个选择避免悬停的问题,对吗?

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

取匿名函数并给它一个名字:

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

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

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

一旦一个键被按下(在keydown事件开始时),使用jQuery的.off()方法删除列表项上的mousemove事件,如果它存在,并将mousemoved设置为false,以确保mousemove事件不会再次附加,直到鼠标再次移动。

jQuery(".btn-group").keydown(function(e) {
  $('.btn-group .dropdown-menu li').off('mousemove');
  mousemoved = false; 
  ... // Some more of your code

这里有一个jsFiddle

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

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

<年代> http://jsfiddle.net/RDSEt/

问题是由于在keydown上触发的自动scroll再次触发mouseenter,导致li的选择混乱。

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

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

  • 防止自动滚动是触发在按下向上箭头/向下箭头使用e.preventDefault() http://jsfiddle.net/TRkAb/[按上/下ul li],现在尝试相同的http://jsfiddle.net/TRkAb/1/[不再滚动]
  • keydown上增加了一个标志来抑制按键时的鼠标事件,该标志被重置onmousemove
  • 添加tabindex到li,允许您使用.focus功能设置焦点。[更多信息:https://stackoverflow.com/a/6809236/297641]
  • 调用.focus将自动滚动到所需位置。(不需要scrollTo插件)http://jsfiddle.net/39h3J/-[检查它如何滚动到li是焦点]

检查演示和代码更改(添加了一些改进),让我知道。

也感谢你的问题,我注意到这个问题和一堆其他问题在我写的一个插件。

我在几个月前写了一个插件来过滤选项,它的行为也完全像一个下拉菜单。

DEMO: http://jsfiddle.net/nxmBQ/[将filterType更改为''关闭过滤]

原插件页面为http://meetselva.github.io/combobox/

. .

如果最近在小部件上按下了一个键,则可以使用全局变量来忽略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');
    }
});

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

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

可能是有意义的last_key_event变量单独为每个小部件,而不是作为一个全局。

您可以试试这个解决方案。如果坐标没有改变(自上次鼠标移动事件以来),则忽略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
}

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

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

将鼠标悬停在一个项目上,将突出显示该项目。按箭头键改变选定的元素,并相应地滚动。移动鼠标选择下面的元素。如果选择项为空,按选择第一个元素。向上按选择最后一个元素。

解决方案这个代码说明了我模仿所描述行为的方法。这挺酷的,试试吧…

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

  • 保存上次输入法的状态。如果上次选择是使用键盘,将鼠标悬停在一个元素上不会选择它,只有点击
  • 忽略mouseover事件,如果坐标没有改变指定的距离,例如10像素
  • 忽略mouseover如果用户曾经使用过键盘

然而,至少对于公众可访问的应用程序来说,坚持使用已建立的UI模式总是最好的。

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

你可以解决这个问题并保留所有的功能,而不需要做任何复杂的条件行为或删除任何事件处理程序。

只需将鼠标悬停事件处理程序更改为鼠标移动事件处理程序。这样,任何键盘导航和选择都会被监听,而当用户使用键盘进行选择时,鼠标位置会被忽略。每当鼠标被用来选择时,鼠标就会被监听。

这听起来微不足道,但它似乎使你的JS小提琴行为完美,没有鼠标和键盘之间的任何冲突行为。这样的:

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

(代码保持不变,只是用mousemove替换了mouseover)