2
\$\begingroup\$

Here is a simple plugin I have written for a jQuery dropdown effect. Simple show / hide (although open classes may be better).

When you click the HTML the event searches the whole DOM for a specific class and then hides it (or removes the open class), even if the dropdown is already close.

Is there a way to only apply the events to open dropdowns somehow?

$.fn.dropdown = function () {

        return this.each(function () {

            // Initial varaibles
            var $this = $(this),
                $toggle = $this.find('.dropdown-toggle'),
                $dropdown = $this.find('.dropdown-menu');

            // When clicking a dropdown toggle
            $toggle.click( function(e){

                // Stop the default behaviour
                e.preventDefault();

                // Clear any open dropdowns
                clearDrops();

                // Stop the event from bubbling up the DOM tree
                e.stopPropagation();

                // Toggle the dropdown
                $dropdown.toggle();

            });

            // Clear the dropdowns
            var clearDrops = function () {
                // Search the entire DOM for dropdowns & hide them
                // THERE MUST BE A BETTER WAY
                $('.dropdown-menu').hide();
            };

            // Trigger the closing of dropdowns when clicking on the HTML
            $('html').bind('click', clearDrops);

        });

    };

$('.dropdown').dropdown();
\$\endgroup\$
2
  • \$\begingroup\$ Why e.stopPropagation()? Do you really want this not to prograte up the dom tree? \$\endgroup\$ Mar 15, 2012 at 21:04
  • \$\begingroup\$ The reason is so I can have an event listener on the $('html') and close the dropdown when clicking anywhere other than the toggle (which stops the click event from bubbling). \$\endgroup\$
    – Craig
    Mar 16, 2012 at 10:55

1 Answer 1

1
\$\begingroup\$

Comments:

  • Don't use the name 'dropdown' that has a very high probability for naming collisions with other libraries.
  • I get that you might want to do stuff as a learning experience but you are recreating something that already exists (as a matter of fact a lot of versions of this widget exist). You might want to check out how something like chosen works.
  • In a similar vein, this ain't gonna work with keyboard input. Maybe you don't need it, but people would expect a dropdown widget to work with keyboard input
  • This seems to require that a certain html structure exists. Someone using this without that knowledge will get confusing and silent failures. The way that jquery ui widgets usually handle this is to hide the initial element and then use jquery.after() to create the expected elements right after it
  • Actually you really should use the jquery ui widget factory that is a better fit for this then just a bare jquery plugin. (Jquery plugins generally are better for adding behaviors, JqueryUi Widget Factory is the preferred way to create enhanced controls)
  • Are you sure you want to only close dropdowns on a click event? That seems far too simplistic. What if someone opens your dropdown and uses the tab key to tab to other fields? Usually something like this will close when focus is lost (I believe that is the blur event)
  • I'm not sure if everything is a child of the html element - detached pop-up dialogs made via jquery ui for example. (not sure about this one)
  • You do not need to stop propogation nor clearMenus when the toggle is clicked. The click will propagate up to the html element and close it then.
  • You will bind the clearDrops function to the html element's click event as many times as there are controls on the page - you only need it once. Do a check or unbind.
  • Is scanning the whole DOM really a problem? The scan only happens when the user interacts with the page, she will have to click on things very quickly indeed for this to be a real preformance problem.

A direct answer to your question: Just remember what dropdown controls have been initialized and hide those.

( function($) {
    var initializedDropdowns = $();

    $.fn.dropdown = function () {

            return this.each(function () {

                var $this = $(this),

                ...
                initializedDropdowns.add($this)
                ...
                var clearDrops = function () {
                    initializedDropdowns.hide();
                };
                ...
            });

        };
} )(jQuery)
\$\endgroup\$
1
  • \$\begingroup\$ Wow, quite the answer. Thanks. Yes I am aware this has been done many times but I couldn't find one that did exactly what I required. Also a lot of the existing plugins (like chosen) seem to offer way more functionality than I actually require. I hadn't considered the tab effect or keyboard navigation so thank you for that (not even Twitter Bootstrap does this). A very well formed answer, not only answering my question but educating me in the process. This is why I love this community. \$\endgroup\$
    – Craig
    Mar 16, 2012 at 10:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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