1

With the help of this post I was able to put together a menu that closes either by toggling a link or clicking outside of it (via mouseup). The problem is that because this mouseup event handler is bound to the document object this is constantly being fired regardless of whether the menu is open or not.

I was wondering how could I conditionally set this handler up only when the menu is visible? I don't necessarily want to invoke: $(document).off("mouseup"); outright in that this toggle is ever fired to initiate the event listener inside $toggleMenu.on("click", function() {...}) via $(document).on("mouseup")

$(function() {
  var $toggleMenu = $(".toggle-menu"),
      $menu = $(".menu");

  $toggleMenu.on("click", function(e) {
    e.preventDefault();

    toggleUserMenu();
  });

  $toggleMenu.on("mouseup", function(e) {
    e.stopPropagation();
  });

  $(document).on("mouseup", function (e) {
    console.log("Event is still firing");

    if (!$menu.is(e.target) && $menu.has(e.target).length === 0) {
      $menu.hide();
    }
  });

  function toggleUserMenu() {
    var menuIsVisible = $menu.is(":visible");
    
    if (menuIsVisible) {
      $menu.hide();
    } else {
      $menu.show();
    }
  }
});
.toggle-menu {
  color: #444;
  display: inline-block;
  margin-bottom: 15px;
  text-decoration: none;
}

.menu {
  border: 1px solid black;
  display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<a href="" class="toggle-menu">Toggle Menu</a>

<div class="menu">
  <a href="#" class="menu-item">Menu Item 1</a>
  <a href="#" class="menu-item">Menu Item 2</a>
  <a href="#" class="menu-item">Menu Item 3</a>
</div>
Community
  • 1
  • 1
Carl Edwards
  • 13,826
  • 11
  • 57
  • 119

2 Answers2

0

I would move your $(document).on("mouseup") code to the toggleUserMenu like this:

  function toggleUserMenu() {
    var menuIsVisible = $menu.is(":visible");

    if (menuIsVisible) {
      $menu.hide();
      $(document).off("mouseup.my-menu");
    } else {
      $menu.show();
      $(document).on("mouseup.my-menu", function (e) {... });
    }

Note, I am using events with namespaces there to avoid cases when $(document).off("mouseup"); will unsubscribe all mouseup handlers.

c-smile
  • 26,734
  • 7
  • 59
  • 86
0

You could attach the event handler only when the menu is displayed and unattach the event handler when the menu is closed.

So, every time you show the menu, you attach the mouseup handler to allow closing the menu by clicking off menu.

When the menu is closed (by click off menu or by clicking the toggle link), hideMenu hides the menu and unsets the event handler so it won't be called upon further mouseup events.

Note that the mouseup handler is factored out of the .on() code so that .off() is able to reference and remove only that handler--that is to say, if you have other mouseup handlers present, they will remain intact.

http://jsfiddle.net/qmLucq9r/

$(function() {
  var $toggleMenu = $(".toggle-menu"),
      $menu = $(".menu");

  $toggleMenu.on("click", function(e) {
    e.preventDefault();
    toggleUserMenu();
  });

  $toggleMenu.on("mouseup", function(e) {
    e.stopPropagation();
  });

  var hideMenu = function() {
      $menu.hide();
      $(document).off("mouseup", mouseupHandler);
  };

  var mouseupHandler = function (e) {
      console.log("Event is still firing");
      if (!$menu.is(e.target) && $menu.has(e.target).length === 0) {
        hideMenu();
      }
  };

  function toggleUserMenu() {
    var menuIsVisible = $menu.is(":visible");
    if (menuIsVisible) {
      hideMenu();
    } else {
      $menu.show();
      $(document).on("mouseup", mouseupHandler);
    }
  }
});
arcyqwerty
  • 10,325
  • 4
  • 47
  • 84