1

I am targeting multiple elements with a jQuery selector by classname, and would like to:

  1. Fire the event of the first item only
  2. Ignore all other selector items

Here is a humorous vertical menu which demonstrates the problem, which if moused-over fast will fire concurrent events and behave like a slinky.

(function() {
  "use strict";

  $(".child").hide();
  $(".parent").on("mouseover", function() {
    var $this = $(this);

    $this.siblings(".parent")
      .find("ul")
      .slideUp();
    $this.find("ul")
      .slideToggle();
  });
})();
#sidebar,
li {
  width: 300px;
}
ul {
  padding: 0;
  list-style-type: none;
}
li {
  background-color: lightgrey;
  font-weight: bold;
  font-size: 24px;
  text-align: center;
  margin: 3px;
  margin-left: 0;
  list-style-position: inside;
  border: 1px solid darkgray;
}
li:hover {
  cursor: pointer;
}
<div id="sidebar">
  <ul>
    <li class="parent">Menu 1
      <ul class="child">
        <li>Item 1</li>
        <li>Item 2</li>
      </ul>
    </li>
    <li class="parent">Menu 2
      <ul class="child">
        <li>Item 1</li>
        <li>Item 2</li>
        <li>Item 3</li>
      </ul>
    </li>
    <li class="parent">Menu 3
      <ul class="child">
        <li>Item 1</li>
        <li>Item 2</li>
        <li>Item 3</li>
        <li>Item 4</li>
      </ul>
    </li>
    <li class="parent">Menu 4
      <ul class="child">
        <li>Item 1</li>
        <li>Item 2</li>
        <li>Item 3</li>
        <li>Item 4</li>
        <li>Item 5</li>
      </ul>
    </li>
  </ul>
</div>

<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
Ian Campbell
  • 2,678
  • 10
  • 56
  • 104
  • Try using e.stoppropagation(); – Chandan Rai Jan 31 '17 at 03:57
  • 1
    Have you attempted a debounce? http://stackoverflow.com/questions/27787768/debounce-function-in-jquery – Jerinaw Jan 31 '17 at 04:02
  • @crai -- thank you for the advice, however I have tried `e.stopPropagation()`, and `e.stopImmediatePropagation()` with no such luck. – Ian Campbell Jan 31 '17 at 04:02
  • @Jerinaw -- have never heard of this, thanks for the information! Reading [the documentation about this plugin](http://benalman.com/code/projects/jquery-throttle-debounce/examples/debounce/ "jQuery debounce by Ben Alman"), there is a statement -- "*when called repetitively, executes the original function just once per "bunch" of calls*", which seems promising. – Ian Campbell Jan 31 '17 at 04:17

2 Answers2

1

Instead of mouseover use the mouseenter and mouseleave events and it works fine

(function() {
  "use strict";

  $(".child").hide();
  $(".parent").on("mouseenter mouseleave", function() {
    var $this = $(this);

    
    $this.find("ul")
      .slideToggle();
  });
})();
#sidebar,
li {
  width: 300px;
}
ul {
  padding: 0;
  list-style-type: none;
}
li {
  background-color: lightgrey;
  font-weight: bold;
  font-size: 24px;
  text-align: center;
  margin: 3px;
  margin-left: 0;
  list-style-position: inside;
  border: 1px solid darkgray;
}
li:hover {
  cursor: pointer;
}
<div id="sidebar">
  <ul>
    <li class="parent">Menu 1
      <ul class="child">
        <li>Item 1</li>
        <li>Item 2</li>
      </ul>
    </li>
    <li class="parent">Menu 2
      <ul class="child">
        <li>Item 1</li>
        <li>Item 2</li>
        <li>Item 3</li>
      </ul>
    </li>
    <li class="parent">Menu 3
      <ul class="child">
        <li>Item 1</li>
        <li>Item 2</li>
        <li>Item 3</li>
        <li>Item 4</li>
      </ul>
    </li>
    <li class="parent">Menu 4
      <ul class="child">
        <li>Item 1</li>
        <li>Item 2</li>
        <li>Item 3</li>
        <li>Item 4</li>
        <li>Item 5</li>
      </ul>
    </li>
  </ul>
</div>

<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
0

The mouseover/mouseout events have a particular behaviour with regard to child elements.

  • mouseover triggers when the cursor enters an element from outside, or from a child element.
  • mouseout triggers when the cursor leaves an element, either by leaving it to the outside or by entering a child element.

This is not always the behaviour you want, so jQuery provides two synthetic events :

  • mouseenter triggers only when the cursor enters an element from outside.
  • mouseleave triggers only when the cursor leaves an element to the outside.

The odd "slinky" behaviour observed in the question is caused by the mouseover/mouseout behaviour, by which the event handler fires each time the cursor enters parent elements from the outside but also when it re-enters from child elements.

The simplest change would be to replace mouseover with mouseenter, thereby responding only to cursor entry from outside the parent elements.

That will give something more like the behaviour you would expect.

(function() {
  "use strict";

  $(".child").hide();
  $(".parent").on("mouseenter", function() {
    var $this = $(this);

    $this.siblings(".parent")
      .find("ul")
      .slideUp();
    $this.find("ul")
      .slideToggle();
  });
})();
#sidebar,
li {
  width: 300px;
}
ul {
  padding: 0;
  list-style-type: none;
}
li {
  background-color: lightgrey;
  font-weight: bold;
  font-size: 24px;
  text-align: center;
  margin: 3px;
  margin-left: 0;
  list-style-position: inside;
  border: 1px solid darkgray;
}
li:hover {
  cursor: pointer;
}
<div id="sidebar">
  <ul>
    <li class="parent">Menu 1
      <ul class="child">
        <li>Item 1</li>
        <li>Item 2</li>
      </ul>
    </li>
    <li class="parent">Menu 2
      <ul class="child">
        <li>Item 1</li>
        <li>Item 2</li>
        <li>Item 3</li>
      </ul>
    </li>
    <li class="parent">Menu 3
      <ul class="child">
        <li>Item 1</li>
        <li>Item 2</li>
        <li>Item 3</li>
        <li>Item 4</li>
      </ul>
    </li>
    <li class="parent">Menu 4
      <ul class="child">
        <li>Item 1</li>
        <li>Item 2</li>
        <li>Item 3</li>
        <li>Item 4</li>
        <li>Item 5</li>
      </ul>
    </li>
  </ul>
</div>

<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

As the other answer indicates, you can also attach the handler to the mouseleave event, thereby ensuring that the menu system collapses reliably regardless of how the cursor leaves each of the parent elements.

Roamer-1888
  • 19,138
  • 5
  • 33
  • 44