admin管理员组

文章数量:1406312

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 previous item and right / down arrows select the next item.

I would like to use the left / right arrows to move focus left or right on the grid (a bit like excel does). Can I disable the left / right arrows from changing the menu choice, (while keeping the functionality for the up / down arrows)?

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 previous item and right / down arrows select the next item.

I would like to use the left / right arrows to move focus left or right on the grid (a bit like excel does). Can I disable the left / right arrows from changing the menu choice, (while keeping the functionality for the up / down arrows)?

Share Improve this question asked Mar 11, 2014 at 9:33 wobbily_colwobbily_col 12k14 gold badges64 silver badges88 bronze badges 3
  • Thanks, but it doesn't seem to work in Firefox, only Chrome. – wobbily_col Commented Mar 11, 2014 at 10:31
  • 1 I have updated the fiddle to work with firefox. I stumbled upon a issue with firefox not preventing the default behavior. Im not getting it perfect for FF :( – A1rPun Commented Mar 11, 2014 at 10:57
  • It seems to be working in the version I am using. Thanks for the replies. – wobbily_col Commented Mar 11, 2014 at 12:07
Add a ment  | 

4 Answers 4

Reset to default 3

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.

Fiddle

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.

Workaround

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.

Usage

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

JSFiddle

This expands on A1rPun's JSFiddle.

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

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

本文标签: HTMLJavascriptstop left right arrow from changing dropdown menu choiceStack Overflow