最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Why does the mouseout handler behave so illogically in this case? - Stack Overflow

programmeradmin4浏览0评论

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=".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>

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>

Share Improve this question edited May 23, 2017 at 12:10 CommunityBot 11 silver badge asked May 21, 2017 at 11:07 Jaroslav TavgenJaroslav Tavgen 3182 silver badges9 bronze badges 3
  • You have used $(this).on("mouseout", removeSquare); that is why it is disappearig – Nimish Commented May 21, 2017 at 11:19
  • But it shouldn't. Square is the part of $(this). – Jaroslav Tavgen Commented May 21, 2017 at 11:23
  • 1 BTW, your code has another problem, too: every time the mouseover event fires, you're adding another copy of the mouseout handler. If you keep moving the mouse in and out of the parent element, you'll eventually end up with dozens of redundant mouseout handlers on it. – Ilmari Karonen Commented May 21, 2017 at 18:41
Add a comment  | 

5 Answers 5

Reset to default 12

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>

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:

or like this:

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>

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>

You can achieve the same using CSS.

.child {
  display: none:
  
}
.parent:hover > .child {
  display: inline-block;
  background-color: red;
  width: 50px;
  height: 50px;
}
<span class="parent">Hover mouse over this text<br>
  <span class='child'></span>
</span>

It is because of event bubbling. When you enter the child span, you jQuery will fire mouseout because you've now gone to a child span. If you want to keep it going, use mouseenter and louseleave which does not fire until you leave the actual element, regardless of child elements.

<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("mouseleave", removeSquare);
}
$ ( ".parent" ).on ( "mouseenter", addSquare );
</script>
发布评论

评论列表(0)

  1. 暂无评论