2

I'm in the process of teaching myself how to write a jQuery plugin. I am using the jquery-hover-dropdown-box as a base example. It's not just copy/paste though, I've made a number of changes trying to get a better understanding of it all. For example I'm not incorporating the hover event, I added a filter, and currently not using any defaults to name a few. Clicking on a div's scroll bar fires the blur event in I.E is the only post I've found with what looks like a good resolution to this and I tried implementing something similar but was unsuccessful.

Complete Example: jsFiddle

Issue:
I click in the input and the dropdown opens but the first time I click on the scroll bar, the dropdown closes. When I open the dropdown a second time and click on the scroll bar, it does not close (as I would expect). From what I can tell, my issue is in the blur on the input. I understand that when I click in the scroll bar, the input has lost focus. I tried to implement something similar to this post on Scrollbars not working on dropdown in IE8 but was unable to get it working.

Steps to Reproduce:

  1. Click in the input to open the dropdown
  2. Click anywhere in the scroll bar and the dropdown closes (should stay open and scroll)
  3. Click in the input a second time and the dropdown opens
  4. Click anywhere in the scroll bar and the dropdown stays open (as it should)

Question:
What am I doing wrong that is causing the dropdown to close only the first time I click on the scroll bar?

What I've Tried:
When I'm appending the ul to the div (currently commented out around line 68 in the jsFiddle), I added the code below. I figured that if I stopped the action from being triggered with a mousedown on the ul it would fix my issue. Although it did fix the issue in Chrome, it persists in IE8.

Update: I changed the code below from $list.mousedown... to $container.mousedown... since $list is the ul and $container is the div that contains it. My thought was that it extend the area. The result was the same though.

...
$container.append($list);
$list.mousedown(function(e) {
    e.preventDefault();
});
...

Since this seemed to be close, I tried taking a similar approach in the blur event. The issue explained above happens when I use this code. In Chrome, clicking the scroll bar does not fire the blur event but in IE8, it does. The first time the dropdown is opened and you click in the scroll bar, it logs "hiding". Open the dropdown again and click the scroll bar and it logs "bind mousedown". Click anywhere outside the dropdown and it closes (as it should) and logs "hiding" (as it should). To me it seems backwards, but obviously I'm not understanding it correctly. (The code below is around line 134 in the jsFiddle)

Code edit: Updated with Goran.it suggestion to prevent multiple bindings from happening.

...
// where $dom is the 'div' containing the 'ul'
$dom.unbind('mousedown.auto_dropdown_box_ul')
    .bind('mousedown.auto_dropdown_box_ul', function(e) {
        console.log('bind mousedown');
        e.preventDefault();
});
setTimeout(function() {
    console.log('hiding');
    $dom.addClass('auto_dropdown_hide').hide();
}, 100);
...

I've also tried removing the blur event. I know this would prevent the dropdown from closing if you tabbed out of the input but figured it was worth a try. In Chrome it works exactly how I expected, clicking outside the input closes the dropdown, clicking the scroll bar does not close it and tabbing out does not close it. In IE8, clicking outside the dropdown does not close it though, nor does it close when you tab out, but clicking in the scroll bar does work. This is the code I added after removing blur (it's not included in the jsFiddle).

// below where the 'blur' event was
$(document).click(function(e) {
    if (e.target == dropdownArray[0].input[0] || e.target == dropdownArray[0].dom[0]) {
        console.log('matches');
        e.preventDefault();
    } else {
        console.log('does not match');
        dropdownArray[0].dom.addClass('auto_dropdown_box_hide').hide();
    }
});

Again, this is my first attempt, I'm still learning. I'm sure there are multiple things that I'm probably doing wrong, that I can improve, etc. Before I tackle those, I would just like to understand what I'm doing wrong here and what I need to do to correct it. After reading the plugin concepts, I know there is much for me to learn.

Community
  • 1
  • 1
Brian
  • 1,184
  • 2
  • 21
  • 38

2 Answers2

1

I found few issues on a first look, you should change the :

$dom.bind('mousedown.auto_dropdown_box_ul'

to:

$dom.unbind('mousedown.auto_dropdown_box_ul').bind('mousedown.auto_dropdown_box_ul'

To prevent multiple events binding to the dom node, you can also use .one event handling of jQuery. In the same event handling you should also put:

console.log('bind mousedown');
e.preventDefault();
return false;

To be sure event is not firing.

Hope this helps (I'm not having IE8 for a long time now)

Goran.it
  • 5,991
  • 2
  • 23
  • 25
  • Thanks for the suggestion. Preventing the multiple events binding was something else I was trying to figure out, and your suggestion did fix that. I tried the `.one` event and the change you suggested but unfortunately the issue (of closing the first time I click in the scroll bar) persists. Please note though, I do really appreciate you explaining how to resolve the multiple events binding though! – Brian Feb 11 '14 at 17:15
0

I believe I finally figured this one out. After multiple tries I thought I'd change up the format to one that seemed, at least to me, a little more straight forward.

Here is the complete jsFiddle

The underlying fix was correctly setting/adjusting which element has focus and when. Since mousedown executes before click, I stuck with that event on the dropdown. In the mousedown event, I set isVisible = true and set focus back on the input (although the latter is not completely necessary). In the blur event, I'm checking isVisible. If it's true, that means that a click happened in the scroll bar so don't close the dropdown. If it's false, close the dropdown. Throughout events, I'm keeping track of isVisible so I know it's state when blur executes. Again, I changed up the format so the two fiddles do look different. I'm sure I could go back and implement something similar to the original fiddle and get it working but I just liked this way more. Here is a snippet of the relevant changes:

{
    // some code above
    // where $list is the 'ul'
    $list.bind('mousedown', methods.onDropdownMousedown);
    // where $obj is the 'input'
    $obj.bind('blur', methods.doOnBlur);
},
onDropdownMousedown: function(e) {
    $input.focus(); // not really needed, just in case
    isVisible = true;
},
doOnBlur: function(e) {
    if (isVisible) {
        $input.focus();
        isVisible = false;
    } else {
        // where $container is the 'div' containing the list
        $container.addClass('auto_dropdown_box_hide').hide();
        isVisible = false;
    }
    isVisible = false;
}
Brian
  • 1,184
  • 2
  • 21
  • 38