w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.43k stars 656 forks source link

[css-transforms-1] [css-transitions-1] Strange behavior with `insertBefore` vs `appendChild` and transitions #5334

Open dead-claudia opened 4 years ago

dead-claudia commented 4 years ago

Originally filed in whatwg/html#5742, but it was recommended I post it here.

Relevant specs, though I'm not for certain which sections apply:

When using insertBefore with transitions and the FLIP technique, I get very strange results across browsers.

The only difference is the way the DOM nodes are rearranged, which is what confuses me about this.

Code for each `insertBefore`: ```html
  • 0
  • 1
  • 2
  • 3
``` `appendChild`: ```html
  • 0
  • 1
  • 2
  • 3
```

I've reproduced this behavior explained above on each of the following platforms:

Relevant framework bugs I've filed, before I narrowed it down further to this:


I suspect it's a bug in all three of those listed browsers, but I'm filing an issue here in case it's actually a spec issue or if a spec note is necessary for this.

Loirooriol commented 4 years ago

See #3309. When you use appendChild or insertBefore, the node is temporarily taken out of the tree, and thus the transition is cancelled. Thus in you 1st code you are cancelling the transitions for all the elements, while in the 2nd one only for elem1 and elem2. elem0 and elem3 are choppy, I guess that's the reason, but I'm not an expert.

annevk commented 4 years ago

Thanks, that's probably it. Does CSS transitions detail this somehow?

emilio commented 4 years ago

Yeah, that's it, and it is specified:

If an element is no longer in the document, implementations must cancel any running transitions on it and remove transitions on it from the completed transitions.

And also:

If an element is not in the document during that style change event or was not in the document during the previous style change event, then transitions are not started for that element in that style change event.

annevk commented 4 years ago

The first should maybe be reworded to "when an element is removed from the document" since the element might be in the document again when CSS next gets to look at it.

dead-claudia commented 4 years ago

In case it was missed, the code snippets are literally just following the FLIP pattern minus the last step of removing the transition class after the transition completes. Here's some pseudocode for what each element is doing:

const initial = elem.getBoundingClientRect().top
moveElement(elem)
const final = elem.getBoundingClientRect().top
elem.style.setProperty("transition-duration", "0s")
elem.style.setProperty("transform", `translateY(${dx}px)`)
forceBrowserStyleRecalc()
elem.classList.add("transition") // contains `transition: 2s transform`
elem.style.removeProperty("transition-duration")
elem.style.removeProperty("transform")

The glitches seem to involve the starting point for the transform, as if dx isn't accurate somehow. And the only thing that's different between the two code snippets is how moveElement is performed.

dead-claudia commented 4 years ago

Update: Found a simpler reproduction.

Movement primitives would not help fix this one, as the former does not add nodes with existing parents.

Edit: simplify it further.

dead-claudia commented 4 years ago

And here's it simplified and deconstructed using two elements, one static and alternating remove/add, the other animated:

The only ones smooth are removing all nodes and re-adding the ones desired - no implicit removal is necessary. And it only affects nodes that are not removed. I've confirmed this in both Chrome and Firefox on Windows.

dead-claudia commented 3 years ago

/cc @annevk in case you missed it

annevk commented 3 years ago

@isiahmeadows what would be interesting to know for this issue is if any of the cases you tested contradicts with the specification somehow.

dead-claudia commented 3 years ago

That I'm not sure of, but I'm not nearly familiar enough with the relevant CSS specs to fully discern it.

Edit: My suspicion is that it's probably consistent with the spec, but my confidence level is like maybe at most 20%.