nnattawat / flip

A lightweight jQuery plugin to make 3D card flipping animation
Other
626 stars 312 forks source link

IE CSS hover problem #65

Open MFry opened 8 years ago

MFry commented 8 years ago

Flip hover behavior works incorrectly on IE when a css sheet has hover styling.

Example:

...
<body>
    <title>jQuery plugin</title>
    <style type="text/css">
      body {
        margin: 0 100px;
      }
      .card {
        width: 100px;
        height: 100px;
        margin: 20px;
        display: inline-block;
      }

      .front, .back {
        border: 2px gray solid;
        padding: 10px;
      }
      .front {
        background-color: #ccc;
      }
      .back {
        background-color: red;
      }
   <!-- Problem CSS. Although, anything with a:hover or similar will cause the behavior on IE-->
    .content a:hover { 
    color: #0066CC;  
    text-decoration: none; }

    </style>
  </head>
  <body>
    <div class="card">
      <div class="front">
          Front
      </div>
      <div class="back">
          Back
      </div> 
    </div>
</body>

</html>
$(document).ready(function(){

    $(".card").flip({
        axis: 'x',
        trigger: 'hover'
    });

});
Download commented 8 years ago

Can you make a JSFiddle demonstrating the issue?

MFry commented 8 years ago

JSFiddle: https://jsfiddle.net/8rzeqqn2/1/ On browsers such as IE 11.0.9600.18097 an issue occurs where hovering over a flip object with hover will cause it to flip and revert back even when your mouse stays over said flip object.

Download commented 8 years ago

Yes, I can confirm this issue in Microsoft Edge as well. Thank you for making a reproduction.

This is interesting. The relevant code in Flip is here

As you can see, Flip already takes great care in preventing a 'shaking/flipping effect' that can happen during the flip (as the mouse leaves and re-enters the object quickly) by detaching the mouseLeave listener before performing the flip and re-attaching it once the flip has completed. But it then checks for hover using if (!$dom.is(":hover")) and unflips if the mouse left during the flip. It seems that $dom.is(":hover") is returning false here.... Weird.

I think you stumbled onto a bug in IE/Edge...

We may be able to workaround this. I think the best thing for you to do would be to fork this project and include your local flip.js, then set breakpoint in the relevant code and see if there is a way to detect that the mouse is actually still hovering... If you can find a workaround I'll be happy to help you create a PR and merge it into Flip, but I don't have time myself in the short term to investigate this.

MFry commented 8 years ago

I will do this and see if I come up with anything.

Download commented 8 years ago

Cool! I'd be glad to help where needed. I just can't take the lead in this.

Good luck!

MFry commented 8 years ago

The problem seems to be in the setTimeout function on line 200.

              setTimeout(function() {
                $dom.bind('mouseout', performUnflip);
                if (!$dom.is(":hover")) {
                   //unflip($dom);
                }
              }, (settings.speed + 150));
            };

I haven't done all the necessary testing but just doing that seems to get it to work on IE.

Download commented 8 years ago

Yes. I think actually the problem is in the line above, that indicates that the dom object is not being hovered, even though it is.

Question is, is there another way to find out that it is still being hovered? If we just uncomment the unflip than Flip will no longer detect it if you move your mouse off the element during the timeout.

MFry commented 8 years ago

You are correct and the insight is much appreciated. I have taken a look into mouseout and :hover and I thought that possibly the mouse over/mouse out might have something to do with this. I forked flip and jsfiddle'd and switched a part of the code to see what would happen.

              setTimeout(function() {
                $dom.bind('mouseleave', performUnflip);
                $dom.mouseleave(function(){
            unflip($dom);
        });
Download commented 8 years ago

Mmmm yes. This looks to me like you are attaching the listener twice... The code should be doing effectively the same as just uncommenting the hover detect right?

Still it does look like you are on to something... I quickly tested in Chrome, IE11 and Edge and it seems to behave ok in all these browsers.

Would you care to test this a bit more thoroughly, including checking what happens if you just completely get rid of the if with the hover check i.s.o. replacing with the call to $dom.mouseleave?

If you can confirm this change makes it well-behaved cross-browser, I'd be happy to merge in a pull request and release it.

MFry commented 8 years ago

I have went through the code and tried to understand the section below and play with it

else if (settings.trigger.toLowerCase() == "hover") {
            var performFlip = function() {
              //allows for the completion of a flip if the mouse leaves before the animation is complete
              $dom.unbind('mouseleave', performUnflip);

              flip($dom);
              //Complete the flip behavior and read the event handler to prepare and/or perform another flip 
              setTimeout(function() {
                $dom.bind('mouseleave', performUnflip);
                if (!$dom.is(":hover")) {
                  unflip($dom);
                }
              }, (settings.speed + 50));
            };

            var performUnflip = function() {
              unflip($dom);
            };

            $dom.mouseenter(performFlip);
            $dom.mouseleave(performUnflip);
          }

As far as I can tell the code makes sense and I don't see why IE is making a fuss. The unbinding $dom.unbind('mouseleave', performUnflip); makes sense so that you can complete the flip animation even if the mouse leaves early, but I think it could be done differently so that the flip completes regardless and we don't need a timeout unbind/bind&check hover. Now I am just trying to figure out how.

Edit: Its too bad it would be too much rewriting to get animate to solve this.

akmil commented 8 years ago

problem is in backface-visibility: hidden; . Check this example, in IE-11 works correct : http://jsfiddle.net/Tinclon/2ega7yLt/8/