Why does reflow need to be triggered for CSS transitions?

空扰寡人 提交于 2019-11-27 15:48:08

(Effectively: "Why can't I easily use transitions with the display property")

Short Answer:

CSS Transitions rely on starting or static properties of an element. When an element is set to display: none; the document (DOM) is rendered as though the element doesn't exist. This means when it's set to display: block; - There are no starting values for it to transition.

Longer Answer:

  1. Reflow needs to be triggered because elements set to display: none; are not drawn in the document yet. This prevents transitions from having a starting value/initial state. Setting an element to display: none; makes the document render as if the element isn't there at all.
  2. He suggest reflowing because it's generally accepted to hide and show elements with display: none; and display: block; - typically after the element has been requested by an action (tab or button click, callback function, timeout function, etc.). Transitions are a huge bonus to UX, so reflowing is a relatively simple way to allow these transitions to occur. It doesn't have an enormous impact when you use simple transitions on simple sites, so for general purposes you can trigger a reflow, even if technically you shouldn't. Think of the guy's example like using unminified JavaScript files in a production site. Can you? Sure! Should you? Probably not, but for most cases, it won't make a hugely noticeable difference.
  3. There are different options available that prevent reflowing, or are generally easier to use than the method in the link you provided. Take the following snippet for a few examples:

A: This element is set to height: 0; and overflow: hidden;. When shown, it's set to height: auto;. We apply the animation to only the opacity. This gives us a similar effect, but we can transition it without a reflow because it's already rendered in the document and gives the transitions initial values to work with.

B: This element is the same as A, but sets the height to a defined size.

A and B work well enough for fading in elements, but because we set the height from auto/100px to 0 instantly, they appear to collapse on "fade out"

C: This element is hidden and we attempt to transition the child. You can see that this doesn't work either and requires a reflow to be triggered.

D: This element is hidden and we animate the child. Since the animation keyframes give a defined starting and ending value, this works much better. However note that the black box snaps into view because it's still attached to the parent.

E: This works similarly to D but we run everything off the child, which doesn't solve our "black box" issue we had with D.

F: This is probably the best of both worlds solution. We move the styling off the parent onto the child. We can trigger the animation off of the parent, and we can control the display property of the child and animate the transition as we want. The downside to this being you need use animation keyframes instead of transitions.

G: While I don't know if this triggers a reflow inside the function as I haven't parsed it myself, you can just simply use jQuery's .fadeToggle() function to accomplish all of this with a single line of JavaScript, and is used so often (or similar JS/jQuery fadeIn/fadeOut methods) that the subject of reflowing doesn't come up all that often.

Examples:

Here's a CodePen: https://codepen.io/xhynk/pen/gerPKq

Here's a Snippet:

jQuery(document).ready(function($){
  $('button:not(#g)').click(function(){
  	$(this).next('div').toggleClass('show');
  });

  $('#g').click(function(){
  	$(this).next('div').stop().fadeToggle(2000);
  });
});
* { box-sizing: border-box; }

button {
	text-align: center;
	width: 400px;
}

div {
	margin-top: 20px;
	background: #000;
	color: #fff;
}

.a,
.b {
	overflow: hidden;
	height: 0;
	opacity: 0;
	transition: opacity 3s;
}

.a.show {
	height: auto;
	opacity: 1;
}
.b.show {
	height: 100px;
	opacity: 1;
}

.c,
.d {
	display: none;
}

.c.show,
.d.show {
 display: block;	
}

.c div {
	opacity: 0;
	transition: 3s all;
}

.c.show div {
	opacity: 1;
}

.d div {
	opacity: 0;
}

.d.show div {
	animation: fade 3s;
}

@keyframes fade {
    from { opacity: 0; }
      to { opacity: 1; }
}

.e div {
	display: none;
}

.e.show div {
	display: block;
	animation: fade 3s;
}

.f {
	background: transparent;
}

.f div {
	background: #000;
	display: none;
}

.f.show div {
	display: block;
	animation: fade 3s;
}


.g {
	display: none;
}
<button id="a">A: Box Height: Auto</button>
<div class="a">This<br/>Has<br/>Some Strange<br/><br/>Content<br>But<br>That doesn't really<br>Matter<br/>Because shown,<br/>I'll be<br/>AUTO</div>
<button id="b">B: Box Height: 100px</button>
<div class="b">Content For 2</div>
<button id="c">C: Hidden - Child Transitions (bad)</button>
<div class="c"><div>Content<br/>For<br/>3<br/></div></div>
<div style="clear: both;"></div>
<button id="d">D: Hidden - Child Animates (Better)</button>
<div class="d"><div>Content<br/>For<br/>4<br/></div></div>
<div style="clear: both;"></div>
<button id="e">E: Hidden - Child Hidden & Animates</button>
<div class="e"><div>Content<br/>For<br/>5<br/></div></div>
<button id="f">F: Child Has BG & Animates (Works)</button>
<div class="f"><div>Content<br/>For<br/>5<br/></div></div>
<div style="clear: both;"></div>
<button id="g">G: This uses fadeToggle to avoid this</button>
<div class="g">I animate with<br/>JavaScript</div>
<footer>I'm just the footer to show the bottom of the document.</footer>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!