I have written a custom drop down menu handler using jQuery because I felt that the native drop down didn't fit my UI.

I have taking into consideration the native way of going through a drop down box using arrows and tabs to move to next input.

I was wondering if anyone can see any mistakes or errors or anyway I could improve this.

http://jsfiddle.net/tFcQ8/1/

Basic mark up for a drop down box:

<label>
    <a class="dropdown button drop white"><span>Title</span><i></i></a>
    <ul class="dropmenu">
        <li>Mr</li>
        <li>Mrs</li>
        <li>Ms</li>
        <li>Miss</li>
    </ul>
    <input type="text" class="dropFocuser" />
</label>

jQuery:

(function() {
    var listItems
      , direction
      , index=0
      , shift='off'
      , next
      , method = false;

    var changeInput = function(drop, next, enter) {
        var input = drop.parent().siblings('a').find('span');
        var newinput = drop.html();
        $(input).html(newinput);
        if(next){
            if(enter) {
                drop.parent().removeClass('show');
                drop.parent().parent().next().focus();
            } else {
                method = true;
                console.log(drop.parent().removeClass('show'));
                console.log(drop.parent().parent().next().focus());

            }
        }
    }

    $('input.dropFocuser').focus(function() {
        if(method == false) {
            index = 0;
            $(this).siblings('ul').addClass('show');
            listItems = $(this).siblings('ul').find('li');
        } else {
            method = false;
        }

    });

    $('ul.dropmenu').find('li').mouseover(function(e){
        var that = this;
        $('li').removeClass('focusList');
        $(this).addClass('focusList');
        $(listItems).each(function(i) {
            if($(listItems)[i] == that){
                index = i;
                return false;
            }
        });
    });

    $('ul.dropmenu').find('li').click(function(e) {
        changeInput($(this), next=true);
    })
    /**
     * Keep track of the shift keys so that we weither to focus next or prev input element.
     */
    $(document).keydown(function(e){
        //if shift key has been pressed down set it to on
        if(e.keyCode == 16) {
            shift = 'on';
        }
    });
    $(document).keyup(function(e){
        //if shift key has been released set it to off
        if(e.keyCode == 16) {
            shift = 'off';
        }
    })
    $('input.dropFocuser').keydown(function(e) {
        $('li').removeClass('focusList');

        switch (e.keyCode){
            case 9:
                //this checks to see if the shift key is pressed or not and
                //from there takes appropriate action.
                if(shift == 'off'){
                    $(this).siblings('ul').removeClass('show');
                    console.log($(this).parent().next().focus());
                } else {
                    $(this).siblings('ul').removeClass('show');
                    console.log($(this).parent().prev().focus());
                }
                return false;
            break;
            case 13:
                var current = listItems[index-1];
                changeInput($(current), next=true, enter=true);
                $(current).addClass('focusList');
                return false;
            break;
            case 40:
                if(index == listItems.length){
                    index = 1;
                    $(listItems[0]).addClass('focusList');
                    changeInput($(listItems[0]), next=false);
                } else{
                    index++
                }
                direction = 'down';
            break;
            case 38:
                if(direction == 'down') {
                    $(listItems[index-2]).addClass('focusList');
                    changeInput($(listItems[index-2]), next=false);
                    index -= 1;
                    if(index == 0) {
                        $(listItems[listItems.length-1]).addClass('focusList');
                        changeInput($(listItems[listItems.length-1]));
                        index = listItems.length;

                    }
                    return false;
                 }  
                direction = 'up';
            break;
            default : return false;
        }
        changeInput($(listItems[index-1]), next=false);
        $(listItems[index-1]).addClass('focusList');
    });
})();

EDIT: I have found this code to be unreiable so I did a little bit of digging and I found that I could change the native UI look that works in modern browsers and I have written in a fall back for

Here is the new HTML / CSS only code: http://jsfiddle.net/tFcQ8/3/

link|improve this question
feedback

1 Answer

Just two small notes from a non-JavaScript/jQuery developer:

  1. Named constants would be better instead of the magic numbers/strings.

    e.keyCode == 16
    shift = 'on';
    shift = 'off';
    case 13:
    case 9:
    case 13:
    case 40:
    case 38:
    
  2. I'd separate the API calls and logging:

    var focusResult = $(this).parent().next().focus();
    console.log(focusResult);
    

    It's easy to lost the $(this).parent().next().focus() call when somebody accidentally comments out the whole console.log line because he or she does not want the log any more.

link|improve this answer
1  
$(this).parent().next().focus() will return the $(this) object. The variable is poorly named, but the pattern there is method chaining (the log appears to be leftover debug code). – Bill Barry May 2 at 22:09
feedback

Your Answer

 
or
required, but never shown

Not the answer you're looking for? Browse other questions tagged or ask your own question.