Take the 2-minute tour ×

I'm trying to create a multiselect dropdown list with checkbox and filter option. I'm trying to get the list hidden with I click outside but could not figure it out how. Appreciate your help.


share|improve this question
add comment

3 Answers

up vote 16 down vote accepted

Watch out, your solution (the Plunker provided in the question) doesn't close the popups of other boxes when opening a second popup (on a page with multiple selects).

By clicking on a box to open a new popup the click event will always be stopped. The event will never reach any other opened popup (to close them).

I solved this by removing the event.stopPropagation(); line and matching all child elements of the popup.

The popup will only be closed, if the events element doesn't match any child elements of the popup.

I changed the directive code to the following:

select.html (directive code)

link: function(scope, element, attr){

    scope.isPopupVisible = false;

    scope.toggleSelect = function(){
        scope.isPopupVisible = !scope.isPopupVisible;

    $document.bind('click', function(event){
        var isClickedElementChildOfPopup = element
            .length > 0;

        if (isClickedElementChildOfPopup)

        scope.isPopupVisible = false;

I forked your plunker and applied the changes:

Plunker: Hide popup div on click outside


Plunker Screenshot

share|improve this answer
Thank you! I'll mark it resolved –  amitava82 Jun 27 '13 at 13:51
Performance hint: If you have allot of these select boxes on your page, you should only bind click when the popup is opened and unbind click as soon as the popup is closed. –  cheneym Jun 28 '13 at 13:31
cheneym, but what about IE8? I get 'event.target.tagName' is null or not an object –  Artsiom M. Oct 8 '13 at 13:09
It seems like elementMatchesAnyInArray is just trying to see if the event.target is a child of element. Couldn't the same be achieved by checking for element.find(event.target).length > 0? –  dubilla Nov 4 '13 at 21:11
@dubilla thanks, you are right. I updated the example and the plunker. –  cheneym Dec 20 '13 at 13:45
show 2 more comments

I realized it by listening for a global click event like so:

.directive('globalEvents', ['News', function(News) {
    // Used for global events
    return function(scope, element) {
        // Listens for a mouse click
        // Need to close drop down menus
        element.bind('click', function(e) {

The event itself is then broadcasted via a News service

angular.factory('News', ['$rootScope', function($rootScope) {
    var news = {};
    news.setClick = function( target ) {
        this.clickTarget = target;

You can then listen for the broadcast anywhere you need to. Here is an example directive:

.directive('dropdown', ['News', function(News) {
  // Drop down menu für the logo button
  return {
    restrict: 'E',
    scope: {},
    link: function(scope, element) {
      var opened = true;
      // Toggles the visibility of the drop down menu
      scope.toggle = function() {
        element.removeClass(opened ? 'closed' : 'opened');
        element.addClass(opened ? 'opened' : 'closed');
      // Listens for the global click event broad-casted by the News service
      scope.$on('click', function() {
        if (element.find(News.clickTarget.tagName)[0] !== News.clickTarget) {
      // Init

I hope it helps!

share|improve this answer
add comment

OK I had to call $apply() as the event is happening outside angular world (as per doc).

    element.bind('click', function(event) {

    $document.bind('click', function(){
    scope.isVisible = false;
share|improve this answer
add comment

Your Answer


