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
Make ul list work like select input
提问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 mousemove
as this will ensure that the right list item is selected as soon as the mouse starts moving again, with mouseover
it 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 mousemoved
indicating if the mouse has moved over the document and set it to false
, on mousemove
over the document, set it to true
and attach the mousemove
function to the mousemove
event 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 mousemove
event on the list items if it is present, and set mousemoved
to false
to ensure the mousemove
event doesn't get attached again until the mouse is moved again.
作为一个按键(在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
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 中的一个问题]
The issue is because of the automatic scroll
which is triggered on keydown
that again triggers mouseenter
messes 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,
下面是更改描述列表和一个小演示来解释它是如何修复的,
- Prevented auto scroll that is triggered on pressing up arrow/down arrow using
e.preventDefault()
http://jsfiddle.net/TRkAb/[press up/down on theul li
], Now try the same on http://jsfiddle.net/TRkAb/1/[No more scroll] - Added a flag on
keydown
to suppress the mouseevents on keydown, this flag is resetonmousemove
- Added
tabindex
to li which would allow you to set focus using.focus
function. [More info: https://stackoverflow.com/a/6809236/297641] - Calling
.focus
would automatically scroll to the desired location. (no need forscrollTo
plugin) http://jsfiddle.net/39h3J/- [Check how it scrolls to li that is on focus]
- 使用
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]
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 filterType
to ''
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 mousemove
event 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
mouseover
event if the coordinates have not changed by a specified distance, e.g. 10 pixels - ignoring
mouseover
if 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 mouseover
event 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 keydown
handler 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_event
variable separate for each widget instead of being a global.
将last_key_event
每个小部件的变量分开而不是全局变量可能是有意义的。