Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the mouseout handler behave so illogically in this case?

Red square is the part of a container with class "parent". If I hover mouse over that red square it disappears. But why? I expected that it shouldn't.

Expected behaviour: it does not disappear since red square is a part of ".parent" container and I have clearly stated, that the mouseout event occurs on that container.

There was a suggestion, that this question is a duplicate of

JavaScript mouseover/mouseout issue with child element

In some way - yes, but I think that this question provides value, because it not only provides the solution ("you can try this"), but also explains WHY you should use that and WHY the initial solution is not working as it is supposed to.

<span class="parent">Hover mouse over this text<br></span>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script>
    function removeSquare()
    {
        $(this).find(".kvadrat").remove();
    }

    function addSquare()
    {
        $(this).append("<span style='display:inline-block;width: 50px;height: 50px;background-color:red' class='kvadrat'></span>");
        $(this).on("mouseout", removeSquare);
    }
    $(".parent").on("mouseover", addSquare);
</script>
like image 977
Jaroslav Tavgen Avatar asked May 21 '17 11:05

Jaroslav Tavgen


People also ask

What is the difference between Mouseleave and Mouseout?

This means that mouseleave is fired when the pointer has exited the element and all of its descendants, whereas mouseout is fired when the pointer leaves the element or leaves one of the element's descendants (even if the pointer is still within the element).

What is Mouseout event?

The mouseout event is fired at an Element when a pointing device (usually a mouse) is used to move the cursor so that it is no longer contained within the element or one of its children.

In what situation will the event Mouseleave will take effect trigger?

The mouse leave event will be triggered whenever the mouse cursor leaves from the selected element and after the occurrence of the event, it implements a mouse leave event that has been attached to an event handler function to run.

What is Mouseout and mouseover?

The mouseover event occurs when a mouse pointer comes over an element, and mouseout – when it leaves. These events are special, because they have property relatedTarget . This property complements target . When a mouse leaves one element for another, one of them becomes target , and the other one – relatedTarget .


3 Answers

It's normal behaviour of .mouseout() event.

Show the number of times mouseout and mouseleave events are triggered. mouseout fires when the pointer moves out of the child element as well, while mouseleave fires only when the pointer moves out of the bound element.

You should use .mouseenter() and .mouseleave() events,

function removeSquare()
{
$(this).find(".kvadrat").remove();
}

function addSquare()
{
    $(this).append ( "<span style='display:inline-block;width: 50px;height: 50px;background-color:red' class='kvadrat'></span>" );
    
}
$ ( ".parent" ).on ( "mouseenter", addSquare );
$(".parent").on("mouseleave", removeSquare);
.parent {
  display: inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span class="parent">Hover mouse over this text<br></span>
like image 94
Okan Kocyigit Avatar answered Oct 13 '22 11:10

Okan Kocyigit


As other people have noted, your original problem is that mouseover and mouseout events also fire for child elements. The solution to that issue is either to use jQuery's mouseenter and mouseleave events, or simply to replace the JS code with the CSS :hover pseudo-class.

However, the reason why the other JS and CSS solutions posted here sometimes behave erratically (causing the square to disappear if you move the mouse over it slowly, but not if you move it fast, and not on all browsers even if you move it slowly) is because, depending on your browser and font settings, there may or may not be a small gap between the top line of text and the square below it. If the gap exists, and your mouse cursor hits it while moving from the text to the square, the browser will consider the mouse to have left the parent element, and will thus hide the square.

Setting a (light blue) background color on the parent element shows the issue clearly; depending on what font and line height the browser chooses, the parent element and the box can look like this:

Screenshot (small line height, no gaps between lines)

or like this:

Screenshot (large line height with gaps between lines)

Manually setting a particularly large line height makes the problem easily reproducible (CSS example based on Thomas van Broekhoven's answer):

.kvadrat {
  display: none;
}
.parent:hover > .kvadrat {
  display: inline-block;
  background-color: red;
  width: 50px; height: 50px;
}
.parent {
  line-height: 2.0;
  background: lightblue;
}
<span class="parent">Hover mouse over this text!<br>
Here's another line of text.<br>
<span class='kvadrat'></span></span>

There are two general ways to fix this issue. The simplest option, where practical, is to make the parent element a block, thereby eliminating the gaps between the lines. You may also wish to add position: absolute to the square's style, so that it won't expand its parent element when it appears:

.kvadrat {
  display: none;
}
.parent:hover > .kvadrat {
  display: inline-block;
  position: absolute;
  background-color: red;
  width: 50px; height: 50px;
}
.parent {
  display: block;
  line-height: 2.0;
  background: lightblue;
}
<span class="parent">Hover mouse over this text!<br>
Here's another line of text.<br>
<span class='kvadrat'></span></span>

Alternatively, if you really want to stick with an inline parent element (e.g. because you want it to be able to wrap across several lines of text), you can set a negative top margin on the square to make sure it overlaps the line of text above it. If you don't want the square to visibly overlap the text, you can further move all the visible content of the square into an inner element and set a corresponding positive top margin on it, like this:

.kvadrat {
  display: none;
}
.parent:hover > .kvadrat {
  display: inline-block;
  position: absolute;
  margin-top: -1em;
  border: 1px dashed gray; /* to show the extent of this otherwise invisible element */
}
.kvadrat > .inner {
  display: block;
  margin-top: 1em;
  background-color: red;
  width: 50px; height: 50px;
}
.parent {
  line-height: 2.0;
  background: lightblue;
}
<span class="parent">Hover mouse over this text!<br>
Here's another line of text.<br>
<span class='kvadrat'><span class='inner'></span></span></span>
like image 27
Ilmari Karonen Avatar answered Oct 13 '22 11:10

Ilmari Karonen


I know this is not directly answering your JavaScript question, but I would like to open your eyes if you're not bounded to JavaScript. You can easily achieve this with CSS.

.kvadrat {
  display: none:

}
.parent:hover > .kvadrat {
  display: inline-block;
  background-color: red;
  width: 50px;height: 50px;
}
<span class="parent">Hover mouse over this text<br>
<span class='kvadrat'></span></span>
like image 5
Thomas van Broekhoven Avatar answered Oct 13 '22 11:10

Thomas van Broekhoven