Faire fonctionner la liste ul comme select input
je veux pouvoir utiliser une liste ul comme élément de forme select, pour des raisons de style.
je suis capable de peupler une entrée cachée avec mon code (non inclus dans ce jsfiddle), et jusqu'ici tout va bien.Mais maintenant j'essaie de laisser mon ul se comporter comme l'entrée select lorsque le clavier est pressé ou que la souris est utilisée.
dans ma question précédente, j'ai eu quelques problèmes avec les commandes de clavier. Ils sont maintenant réparés. Voir:défilement Automatique sur la flèche du clavier haut/bas
Le problème qui demeure est que la souris n'est pas ignorée lorsque les touches du clavier sont enfoncées. Cela provoque l'effet "hover" d'écouter d'abord l'entrée du clavier, mais de passer immédiatement à la souris et de sélectionner cet élément li comme étant sélectionné.
ceci peut être vu dans mon exemple de jsfiddle: http://jsfiddle.net/JVDXT/3/
mon 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');
}
}
});
est-ce que quelqu'un pourrait m'apprendre à igonore la souris quand les touches du clavier sont pressées, mais la liste à la souris quand il est touché à nouveau par l'utilisateur. Comme le champ de formulaire d'entrée par défaut select.
mise à Jour
Voici un nouveau jsfiddle.
7 réponses
Check this out:
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();
la clé est de déballer la mouseover et de rebind lorsque la souris bouge.
j'ai refait un peu en utilisant une fonction de clôture, l'ajout de la logique à une méthode jQuery liste déroulante donc vous pouvez le réutiliser, en utilisant switch au lieu d'un tas de si et plus de choses.
Eh bien, il y a des millions de plugins pour transformer un select en un liste:
http://ivaynberg.github.io/select2/
http://harvesthq.github.io/chosen/
http://meetselva.github.io/combobox/
et j'ai le mien aussi! (prêt pour touch en utilisant la même astuce que http://uniformjs.com)
https://github.com/coma/jquery.select
mais cette question Est de prendre cela HTML et le faire se comporter comme un select évitant la question du hover n'est-ce pas?
Voici une solution, j'utilise mousemove
car cela permet de s'assurer que l'élément de la liste de droite est sélectionné dès que la souris recommence à bouger, avec mouseover
il ne commence à sélectionner un élément de la liste à l'entrée d'un nouvel élément de liste:
prendre la fonction anonymous et lui donner un nom:
function mousemove() {
console.log('mousie')
jQuery(".btn-group .dropdown-menu li").removeClass('selected');
jQuery(this).addClass('selected');
}
Déclarer une variable globale mousemoved
indiquant si la souris est déplacée sur le document et à false
mousemove
sur le document, l'ensemble true
et joindre mousemove
fonction <à la!--3--> événement sur les éléments de la liste.
var mousemoved = false;
jQuery(document).mousemove(function() {
if(!mousemoved) {
$('.btn-group .dropdown-menu li').mousemove(mousemove);
mousemoved = true;
}
})
dès qu'une touche est pressée (au début de l'événement keydown), utilisez .off()
méthode pour supprimer le mousemove
événement sur les éléments de la liste si elle est présente, et set mousemoved
false
mousemove
événement n'attachez pas à nouveau jusqu'à ce que la souris est déplacée de nouveau.
jQuery(".btn-group").keydown(function(e) {
$('.btn-group .dropdown-menu li').off('mousemove');
mousemoved = false;
... // Some more of your code
Voici un jsFiddle.
j'ai essayé de résoudre votre problème en empêchant l'autoscroll, en ajoutant tabindex sur le li, en mettant l'accent sur active, et en utilisant un drapeau pour supprimer la souris.
Fixe violon: http://jsfiddle.net/8nKJT/ [correction d'un problème dans Chrome ]
le problème est dû à l'automatic scroll
qui est déclenché sur keydown
qui déclenche une nouvelle fois mouseenter
les dégâts de la sélection de l' li
.
Remarque: Les différences avec les autres approches(les réponses ici), j'ai remarqué est-il rouleaux sur chaque pression de touche au lieu de défiler seulement après avoir atteint le haut ou le bas(comportement normal). Vous sentirez la différence en regardant la démo côte à côte.
ci-dessous se trouve la liste de la description du changement et une petite démo pour expliquer comment il a été corrigé,
- Empêché de défilement automatique déclenché en appuyant sur la flèche vers le haut/la flèche vers le bas à l'aide de
e.preventDefault()
http://jsfiddle.net/TRkAb/ [appuyez vers le haut/vers le bas sur leul li
], Maintenant essayer la même chose sur http://jsfiddle.net/TRkAb/1/ [Pas plus de défilement] - ajout D'un drapeau sur
keydown
pour supprimer le mouseevents sur keydown, ce drapeau est réinitialiséonmousemove
- Ajouté
tabindex
à li ce qui vous permettrait de mettre la mise au point en utilisant.focus
fonction. [Plus d'infos: https://stackoverflow.com/a/6809236/297641] - appel
.focus
se déroulerait automatiquement à l'endroit désiré. (pas besoin descrollTo
plugin) http://jsfiddle.net/39h3J/ - [comment ça défile à li qui est sur focus]
consultez la démo et les changements de code (quelques améliorations ajoutées) et faites-moi savoir.
aussi grâce à votre question, j'ai remarqué ce numéro et un tas d'autres numéros dans l'un des plugin J'ai écrit.
j'ai écrit un plugin il y a quelques mois pour filtrer les options et agir aussi exactement comme une goutte vers le bas.
DÉMO: http://jsfiddle.net/nxmBQ/ [modifier filterType
''
à la bretelle de sortie de l'filtrage ]
la page du plugin original est http://meetselva.github.io/combobox/
.. plus d'
vous pouvez utiliser un global pour ignorer le mouseover
événement si un keydown a été appuyé récemment sur le widget. Par exemple:
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');
}
});
Puis keydown
handler peut définir quand il a été manipulé pour éviter toute interaction avec la souris:
//What to do when the keyboard is pressed
jQuery(".btn-group").keydown(function(e) {
last_key_event = (new Date).getTime();
...
});
peut - être serait-il logique d'avoir le last_key_event
variable séparée pour chaque widget au lieu d'être un global.
Vous pouvez essayer cette solution. Il ignore l' mousemove
événement si les coordonnées n'ont pas changé (depuis le dernier événement 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
}
mise à jour du fiddle pour vérifier si c'est ce que vous cherchiez: http://jsfiddle.net/pdW75/1/
Approche une solution raisonnable devrait imiter le comportement d'autres éléments D'UI qui servent un but similaire. Sur tous les systèmes cochés (Windows, Linux, les principaux navigateurs), les boîtes déroulantes se comportent comme suit:
mousser sur un objet le met en valeur. En appuyant sur les touches fléchées, vous modifiez l'élément sélectionné et vous faites défiler en conséquence. Le déplacement de la souris sélectionne l'élément en dessous. Si la sélection est vide, appuyer sur bas sélectionne le premier élément. En appuyant sur sélectionne le dernier élément.
Solution code illustre mon approche pour imiter le comportement décrit. C'est plutôt cool, essayer...
Autres Considérations Il y aurait un certain nombre d'autres options pour supprimer les mouvements de la souris pour modifier l'élément sélectionné. Elles comprennent:
- conserver un État de la dernière méthode d'entrée. Si la dernière sélection à l'aide du clavier, passez la souris sur un élément ne permet pas de sélectionner seulement clic
- ignorer
mouseover
événement si les coordonnées n'ont pas changé d'une distance spécifiée, par exemple 10 pixels - ignorer
mouseover
si l'utilisateur n'a jamais utilisé le clavier
cependant, au moins pour une application accessible au public, il est toujours préférable de s'en tenir aux modèles D'assurance-chômage établis.
le problème qui apparaît est que lorsque la souris est laissée sur une partie de la liste étendue, la sélection à l'aide des touches est annulée parce que la sélection faite par le clavier revient immédiatement à l'élément qui se trouve être sous la souris.
Vous pouvez résoudre ce problème et conserver toutes les fonctionnalités sans faire de comportement conditionnel compliqué ou une suppression des gestionnaires d'événements.
il suffit de changer votre gestionnaire d'événement mouseover pour être un événement mousemove manipulateur. De cette façon, toute navigation et sélection de clavier est écoutée et la position de la souris est ignorée chaque fois que l'utilisateur utilise le clavier pour sélectionner. Et chaque fois que la souris est utilisée pour sélectionner, puis la souris est écouté.
cela semble banal mais il semble faire votre violon JS se comporter parfaitement et sans aucun comportement conflictuel entre la souris et le clavier. Comme ceci:
//The function that is listening to the mouse
jQuery(".btn-group .dropdown-menu li").mousemove...
(votre code continue inchangé, remplaçant seulement mouseover par mousemove)