简体   繁体   中英

CSS :hover rerender after covering element height 0

I have an element which I call back covered by another element which I call front .

Back is set in CSS to change color on hover. Front is set, when hovered, to disappear after a 1s delay and the mouse pointer which was on Front element now is on back element .

Front disappears by setting height: 0 in JavaScript. After Front disappears and the mouse ointer is hovering on Back the browser doesn't rerender the hover effect on Back which results in its color not being changed as it should.

When I try the same example by naturally removing Front from the DOM it works fine. My app, though, requires I do this by setting height: 0 .

You can try it for yourselves in the following examples. You will see a red box and a blue box. Red is Front and blue is Back . When you move the mouse pointer to the red box, the red box's color changes to green . When you move your mouse onto the blue box, after one second, it will disappear.

The point of the example is to show that after the blue box disappears, the mouse pointer now hovers on the red box and it should therefore become green .

In this example , the blue box is removed from the DOM completely and it works as well as expected.

In this example , though, the blue box is to be removed by setting height: 0 . After disappearing, the red element won't become green until after I move the mouse.

Is there any way to force browser to rerender?

The problem persists in Google Chrome and Microsoft Edge. Firefox works good.

Instead of trying to force the browser to rerender , you can just add a class to foo that will make it green when bar disappears and then, it returns to normal on mouseleave .

CSS :

#foo.hover, /* <- I just added this */
#foo:hover {
    background-color: green;
}

JavaScript :

var
  foo = document.getElementById("foo"),
  bar = document.getElementById("bar");

/* Set the 'mouseenter' event for bar to set the height to 0 and & the 'hover' class. */
bar.onmouseenter = function() {
  setTimeout(function() {
    bar.style.height = 0;
    foo.classList.add("hover");
  }, 1000);
}

/* Set the 'mouseleave' event for bar to remove the 'hover' class. */
bar.onmouseleave = function() {
  foo.classList.remove("hover");
}

Check out the following snippet for a visual representation.

Snippet:

 /* --- JavaScript --- */ var foo = document.getElementById("foo"), bar = document.getElementById("bar"); /* Set the 'mouseenter' event for bar. */ bar.onmouseenter = function() { setTimeout(function() { /* Set the height to 0. */ bar.style.height = 0; /* Add the 'hover' class. */ foo.classList.add("hover"); }, 1000); } /* Set the 'mouseleave' event for bar. */ bar.onmouseleave = function() { /* Remove the 'hover' class. */ foo.classList.remove("hover"); } 
 /* --- CSS --- */ #foo { width: 200px; height: 200px; background-color: red; position: absolute; top: 10px; left: 15px; } #foo.hover, #foo:hover { background-color: green; } #bar { width: 200px; height: 200px; background-color: blue; position: absolute; top: 100px; left: 100px; } 
 <!-- HTML --> <div id = "foo"></div> <div id = "bar"></div> 

This really seems like a browser issue, there are some ways on how to force webkit to redraw . But I really wouldn't recommend that for you. You can simply use bar.style.display = 'none'; instead of setting the height to zero. Has the same effect and everything works as expected. See this fiddle: https://jsfiddle.net/zkhorh38/4/ .

It's possible to do it in CSS just by playing with the transition property.

 #foo { width: 200px; height: 200px; background-color:red; position: absolute; top:10px; left:15px; z-index: 10; } #foo:hover { background-color: green; } #bar { width: 200px; height: 200px; background-color:blue; position: absolute; top:100px; left:100px; z-index: 20; transition: all 1000000s; } #bar:hover { transition: all 0.01s; opacity: 0; z-index: 1; } 
 <div id="foo"></div> <div id="bar"></div> 

This might be a browser issue, but there are some ways on how to force webkit to redraw. But I wouldn't recommend that for you. You can simply use bar.style.display = 'none'; instead of setting the height to zero.

Another solution to force a redraw/repaint that works just fine:

sel.style.display='none';
sel.offsetHeight; // no need to store this anywhere, the reference is enough
sel.style.display='';

**

To avoid flickering you may try 'inline-block' , 'table' or 'run-in' instead of 'none' , but this may have side-effects. Also, a timeout of 0 triggers a reflow just like querying offsetHeight does: sel.style.display = 'run-in'; setTimeout(function () { sel.style.display = 'block'; }, 0); sel.style.display = 'run-in'; setTimeout(function () { sel.style.display = 'block'; }, 0);

Try this it really works !

If back is completely covered by front , you could set front.style['pointer-events'] = 'none' in the onmouseenter callback first to let the mouse activate back 's :hover rule before front disappears.

See modified working snippet .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM