I am creating a web form with a grid of inputs for creating objects in Django.
It seems that when the focus is on a drop down menu, the up and left arrows select the pr
Ruud's Answer works but if you look closely or if you have a lot of code running in the event, you'll see the select value changing the next element and back to the original one.
Here's a solution that works on Firefox, and looks cleaner to me. Untested in other browsers.
The trick is simply to set the select as disabled in the keydown event, then to reenable it on the next frame with a setTimeout. The anonymous function will not run until all the code in the event handler has been processed, assuring that the select is not reenabled too early.
mySelect.addEventListener('keydown',
function (event) {
switch (event.which || event.keyCode || event.charCode) {
case 33:
this.disabled = true;
setTimeout(function (elem) {elem.disabled = false;}, 0, this);
break;
case 34:
this.disabled = true;
setTimeout(function (elem) {elem.disabled = false;}, 0, this);
break;
}
});
Here's the JSFiddle
While A1rPun's answer does what was asked, it does not work in Firefox. This is because of this Firefox bug, basically event.preventDefault
doesn't prevent ← and → on <select>
nodes.
I made a workaround for this issue that can be used to improve A1rPun's answer to support Firefox.
function ensurePreventDefault(select) {
var selectedIndex, scrollTop;
function saveState() {
selectedIndex = select.selectedIndex;
scrollTop = select.scrollTop;
}
saveState();
if (!select.multiple && !select.size) {
select.addEventListener('change', saveState);
}
// use setTimeout to wait a frame and see if the selected index was changed
setTimeout(function () {
select.removeEventListener('change', saveState);
if (select.selectedIndex !== selectedIndex) {
// Damn you, Firefox!
select.selectedIndex = selectedIndex;
select.scrollTop = scrollTop;
}
});
}
Short version: store the selectedIndex
and restore it back a frame later.
Long version: store the selectedIndex
, so that we can check later if it was changed by Firefox, using setTimeout
. You'll also want to store these values on 'change'
event to fully support a dropdown. Also store scrollTop
to prevent Firefox from scrolling inside a multi-select. These are rendered as lists, not as dropdowns, causing Firefox to scroll down or up when the selected index was changed. In case of a multi-select you do not want to listen to it's 'change'
event, this will have a reversed effect.
You can use it like this:
.addEventListener('keydown', function (event) {
switch (event.which || event.keyCode) {
case 37: // left arrow
case 39: // right arrow
event.preventDefault();
ensurePreventDefault(this);
break;
}
});
This expands on A1rPun's JSFiddle.
IN regard to the answer of Ruud Lenders: works only if you have a single select in the form. If you have multiple select dropdowns then you can use the following version:
$(document).on('keydown', 'select', function(event) {
var value = $(this).find('option:selected').val();
if ((event.which == 37 || event.which === 39)) {
setTimeout(function (obj, val) {
console.log(val)
return function() {
obj.find('option[value="' + val + '"]').prop("selected", true)
}
}($(this), value), 0);
}
});
Changing default behavior of controls is sometimes frustrating for users. But other times the user expect it works like excel like in your case :)
You can do something like this:
var selects = document.getElementsByTagName('select');
for (var i = 0; i < selects.length; i++){
selects[i].addEventListener('keydown',function(e){
var key = e.which || e.keyCode;
if(key == 37){
var previousSibling = this.previousSibling;
while(previousSibling && previousSibling.nodeType != 1) {
previousSibling = previousSibling.previousSibling
}
previousSibling.focus();
e.preventDefault();
}else if(key === 39){
var nextSibling = this.nextSibling;
while(nextSibling && nextSibling.nodeType != 1) {
nextSibling = nextSibling.nextSibling
}
nextSibling.focus();
e.preventDefault();
}
})
}
Key 37 = ← and key 39 is →.
e.preventDefault();
prevents the default behaviour of the key you pressed.