3
\$\begingroup\$

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.

jsFiddle

Basic markup 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');
    });
})();

I have found this code to be unreliable so I did a little bit of digging and I found that I could change the native UI look that works in modern browsers. I have written in a fallback for

Here is the new HTML / CSS only code.

\$\endgroup\$
1
\$\begingroup\$

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.

\$\endgroup\$
  • 1
    \$\begingroup\$ $(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). \$\endgroup\$ – Bill Barry May 2 '12 at 22:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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