Open dead-claudia opened 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.
Thanks, that's probably it. Does CSS transitions detail this somehow?
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.
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.
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.
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.
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.
/cc @annevk in case you missed it
@isiahmeadows what would be interesting to know for this issue is if any of the cases you tested contradicts with the specification somehow.
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%.
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.appendChild
insertBefore
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.