Move active element loses mouseout event in Internet Explorer
Asked Answered
M

4

13

In a library I am using I have the task of moving an element to the front of the dom when it is hovered over. (I make it bigger so I need to see it, then shrink it back when mouse out).

The library I am using has neat solution which uses appendChildren on the active element to move it to the end its parent so further towards the end of the dom and in turn on top.

The problem is I believe that because the element you are moving is the one you are hovering over the mouseout event is lost. Your mouse is still over the node but the mouseout event isn't being fired.

I have stripped the functionality down to confirm the issue. It works fine in Firefox but not in any version of IE. I'm using jQuery here for speed. Solutions can be in plain old Javascript, which would be a preference as it might need to go back up stream.

I can't use z-index here as the elements are vml, the library is Raphael and I am using the toFront call. Sample using ul/li to show issue in a simple example

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<script src="js/jquery.min.js" type="text/javascript"></script>
<style>
    li
    {
        border:1px solid black;
    }
</style>
</head>
<body>
<ul><li>Test 1</li></ul>
<ul><li>Test 2</li></ul>
<ul><li>Test 3</li></ul>
<ul><li>Test 4</li></ul>
<script>
$(function(){
    $("li").mouseover(function(){
        $(this).css("border-color","red");
        this.parentNode.appendChild(this);
    });

    $("li").mouseout(function(){
        $(this).css("border-color","black");
    });
});
</script>
</body>
</html>

Edit: Here is a link to a js paste bin to see it in action. http://jsbin.com/obesa4

**Edit 2: ** See all comments on all answers before posting as lots more info in that.

Microgamete answered 10/9, 2010 at 15:52 Comment(5)
i can't make sense of your post. you want the mouseout event to fire while your mouse is still over the element? what is your actual question?Socher
And for this.parentNode.appendChild(this);, you're trying to re-add the existing LI element? Why not just add a CSS class to it instead of adding/removing the same element with new CSS? Or does your original code involve adding an entirely new element from somewhere else inside the UL to imitate your "expanded" size effect that you mentioned in beginning?Cyna
Linkol I am trying to get the sample code to work in ie like it does in firefox. The mouseout event doesn't fire. I am using li elements as an example. As I say in my post the elements are actually VML elements. So things like z-index or adding classes won't work, unless people can prove different.Microgamete
Oh and the question/issue is the title of the post!Microgamete
I am adding a bounty to this question. I have solved it by monitoring the position of the mouse in internet explorer and resetting the hovers when the mouse moves out of the area. Basically reinventing the mouse in/out functionality. It works, but I would prefer to find a better example.Microgamete
K
15

The problem is that IE handles mouseover differently, because it behaves like mouseenter and mousemove combined on an element. In other browsers it's just mouseenter.

So even after your mouse has entered the target element and you've changed it's look and reappended it to it's parent mouseover will still fire for every movement of the mouse, the element gets reappended again, which prevents other event handlers from being called.

The solution is to emulate the correct mouseover behavior so that actions in onmouseover are executed only once.

$("li").mouseover( function() {
    // make sure these actions are executed only once
    if ( this.style.borderColor != "red" ) {
        this.style.borderColor = "red";
        this.parentNode.appendChild(this);
    }
});

Examples

  1. Extented demo of yours
  2. Example demonstrating the mouseover difference between browsers (bonus: native javascript)
Kruller answered 23/10, 2010 at 22:14 Comment(3)
Brilliant, exactly what I was hoping for when I set the bounty up. My code is performing much better now I'm not having to hack around this too.Microgamete
Here's a simple example in Raphael 2.0.2 jsfiddle.net/K2bSY/2 for people encountering the same issue using toFront(), showing an example fix using Raphael's .data() functionSecretarygeneral
Though this appears to solve the issue, I've found that if I move the mouse very fast using IE11 (probably also true for older IE) it's possible for the mouseout to not fire even with this change.Heedless
B
1

I was able to get it working with nested divs and a mouseenter event on the parent:

<div id="frame">
  <div class='box'></div>
  <div class='box'></div>
  <div class='box'></div>
  <div class='box'></div>
</div>
...
$('#frame').mouseenter(function() {
     $(".box").css("border-color", "black");
});

Here's a working version using Raphael:

http://jsfiddle.net/xDREx/

Bhakti answered 20/10, 2010 at 19:6 Comment(3)
Can' get that to work at all. Nothing happens when I mouse over the boxes.Microgamete
Did you try the link? What browser are you using? I got it to work in IE6, IE7 & IE8.Bhakti
Ooh mouseenter on the parent, that is basically what you are doing. Which fires the reset. Nice. However it doesn't solve the core problem of the events being lost. (Click event too!) Deffo worth half the 200 points though. If I could get the element to get to the top with out losing events i'd be a happy man. (JSFiddle must have been down, working great now)Microgamete
H
1

I modified @galambalazs' answer because I found that it failed if I hovered really quickly over the li elements, as some of the elements would still retain the mouseover effect.

I came up with a solution that removes the hover state on elements that failed to trigger the mouseover event by pushing these elements to a stack whenever the mouseover even is triggered. Anytime either the mouseover or mouseout event is called, I pop the elements from this array and remove the styles placed on it:

$(function(){

    // Track any hovered elements
    window.hovered = [];

    $("li").mouseover(function() {
        // make sure that these actions happen only once
        if ( $(this).css("border-color") != "red" ) {
            resetHovered ();  // Reset any previous hovered elements
            $(this).css("border-color","red");
            this.parentNode.appendChild(this);
            hovered.push(this);
        }
    });

    $("li").mouseout(function(){
        resetHovered();  // Reset any previous hovered elements
    });

    // Reset any elements on the stack
    function resetHovered () {
        while ( hovered.length > 0 ) {
            var elem = hovered.pop();
            $(elem).css("border-color","black");
        }
    }
});

I've tested this solution with IE 11. A functional example can be found here.

Heedless answered 23/5, 2014 at 21:28 Comment(0)
S
0

That's wonky, and seems to be IE-only (but so is VML). If the parent element has a height specified, you can attach the mouseout handler to the parent... but it sounds like that won't work in your situation. Your best alternative is to use mouseover on adjacent elements to hide it:

$(function()
{
    $("li").mouseover(function()
    {
        $("li").css("border-color", "black");
        $(this).css("border-color", "red");
        this.parentNode.appendChild(this);
    });
});

Or SVG. You can use z-index in SVG.

Sullage answered 11/9, 2010 at 0:53 Comment(1)
Hmm the alternative is I think the solution here. Basically I'll need to call a reset function on all the other elements on mouseover which will fire the shrink animation. Its very clunky but it might work. I'll up vote this answer for now, and see if anyone else can think of something.Microgamete

© 2022 - 2024 — McMap. All rights reserved.