- From: Isiah Meadows <notifications@github.com>
- Date: Mon, 20 Jul 2020 00:46:15 -0700
- To: whatwg/dom <dom@noreply.github.com>
- Cc: Subscribed <subscribed@noreply.github.com>
- Message-ID: <whatwg/dom/issues/880@github.com>
When using `insertBefore` with transitions and the FLIP technique, I get very strange results across browsers. - [Smooth transitions using `appendChild`](https://flems.io/#0=N4Igxg9gdgzhA2BTEAucD4EMAONEBMQAaETAVwBcIAlReCTQlAM03jxOYEskZUBtUFEwBbZGgB0ACwoj4xdFAqIlqEAB4y8AARd8AXgA6pKFxGZlxgHzr4XKwAZ1Aejs23ARhdvb9gEze9r5WAMyBNs5aVgp4SGAUXNB8aA4oAGx+IAC+REKi4iASAFZ8JJBKKhRqzs7aAGKY8RAATgTaEJTacLoUAOQw2mBImK3N2gDuUhbadIhiSgMjiNqM+G2YUPjarSIQAG4EhlBH5TAUK6bmytQQEOf62vgQYGTzFBIA5ogUAKJIbwAhACeAEl8AAKYwbMwWRDGACUJyS51mIgc2ge0KuiBudwkYCkPHwADkIGsYPwHABdJGwFH-DwYi4w663d4Eomk8n8Dw0qCnelzPxMrGw3HswnwElkxAUvx8gUzf4hEWXMVs-GS6XckJ8o41drNNatfAobSUojaDyWvyW3X62oiLgADwIZu05tt2gcdstvKOR2YZH5CWg2idzvB8O0wG0BpaxraAForOGXYcoB68BQACpmRAdCjghOIZqWgCsDgciMz2iOHoNdS4zTO9cGyO03BbFHRD1RDk+3wBHU2XCgHwAwnZKrR4lGJFRsG3FV2zoy+wzBxRh8H8GPJ9OlLOi-CFxAl7WV82zsKN0KtzvR+Op1wZ4g56fF8uO6uKCq7yIIQPiOe7PoeFDHvOX7HLWBoAKrYPgsLmj6Vo2naVLaCmnqWqhIR+nyHqiqyeI4NgKj4BOWrgqifg1kRaoke8ZEUVRRI0f81ZtsROIaixmxsVKHFzCE9HMti4oSPxlHUaiHg1m2BoADKYGc2gANTaCCUAHM0FCKbUpLKGaiAjNOYwAAYUM0GwwFwoZQAAImQNkORZ4ZkGpABGyzZtoPnMC0yxYMozQGdoVk2bAgXNCIFmWi0MwHJmuytBMiDNvgUCygMzwvC22jgkUnnnNZQI9BcWx4HCsG1JM0xTNg5GwKebb9hIZxAkgC5RXZDnOa5iSZkYtZyR1FBdYgPW2fZQ0DRYQ0Ym1-x+ONk3TbAs3QPNDlLaNyprd11kzf1LkLWGDzGA4MDGMtcwDp1R29TFIhMpFtkhYgACa4IACTAL+6JJkq93Abu+4vm+H5ntgWTYM68IWXdIgeIdU3HdFLSvQ872wJ9P3-b+jLA2NXzbiBEPgZBn7nnDCNI-t96Pejz1Y29GMwPjf0A9eFDCiTK1g0+B6vke74njDdOI8jQHMxtMAvezvVc4TvMqgLIlC6BItQxLi5Swz4V1C0YDLK0SZYEChZtk8LxvBIXlkkCEgQMwzDZgAEhlHwyAGtXaAACpbyMDkMqkwEpXBnFJ+AQsYHNbccIBiWNYcwBHUfMbHkIgAnDkIsjq1pxn0erDnedDQXjOAfiWDp5HpfZ-HvWJ1XHrtXLHOKwBD0TU9J1zWdu0je3m6d6zsVMmN48D9tQ+LSPIMiKtM+Y5PAEr33LOz0588XTLaPy93S+y1v8uJztC91iAt1QFkftBiGi0ls0UYxnGjrpls2EvxmWbfHmMQhZwQRgrFWMSRtebfjpJ2XmvYl4DjJo+bWkMxbQ2gh6K83Z1xL1RkgimYFRYQXFlBc80C1K-lvEvVa+DwaEN1qQi8mCfxqyngdWhwtUHEPQWQmCDZagISQsoHC3pfRWkwthC0aFtBentLWHiklpKCQhP2MSCi+JNVYrJBkajGK8V) - [Choppy transitions when interrupted using `insertBefore`](https://flems.io/#0=N4Igxg9gdgzhA2BTEAucD4EMAONEBMQAaEAMwEskZUBtUKTAW2TQDoALAF0fmPSk6IBqEAB4ArvAAE5fAF4AOiExRyjTIKUA+UfHJaADKID0enWYCMJs7v0Ama-ttaAzI53HJWvniRhO5NDUaAYoAGx2IAC+RPRMLCCsAFbUJJACQpwixsZSAGKY-hAATgRSEOKcUnAynADkMFJgSJilxVIA7uwaUohIzAKNrYhSmPj4ZSr4UqWMEABuBApQy+kwVSpqGogAShAQVXJS+BBg4gOcrADmiJwAov2ZAEIAngCS+AAUSpvqmiAASlWQSqfUQjAMUiOv22ewOrDA7Eo+AAchAJjAaAYALrA2Cg-oWKGjVR-Xb7S6I5FojE0Cy4qBrAnguzEmGCOGUpHwVHoxCYuwMpm9fouNmk2EUhHc3m0lwM5Y5crFCalfAoKRYohSCzauza+WK3KMcgADwIGqkmv1UgMBu19OWy1I4kZAWgUhNps+AKkwCkSpKqrKAFotJ6zUsoFa8JwACpqRAVTifIOIYragCsBgMQOjUmWVqVeXIxXWhaaIKkFDLnEhRzBEOutyeFSg+HIUCuAGE9Jkdoh-D7WJwINgK8Ka+siQ3Cc3OK3XR2u73yP3BymASOxxOq1POKzZyz54v252e32BAOh1vR+P85PS+sxUfGC4T23lxe11eN8O706+ZKgAqtg+DbJqdo6nqBrYlIYbWtqUEuA6DJWuy5Lwp2eDFAuiCkCUiCfI2NqNrmFYYZyrA4NgQj4N2MrEYSeYVkqAAymDrFIADUUhvFAiy4axuRooIGqIK0fbtAABpwxQqDA5DulAAAi4jycp0meuIXEAEYjLGUj6QRpRSFggjFMJUiyfJsAmYw0naiUvSLNGcymR0iClvgUD8o0pxnGWUifEkOlVHJLy1CS0x4IgVldD03TYLRsBbhWZGsOsLxICOtmKcpakaYE0aKPmjYWJlnDZYguUKUpxWFRoxVQul-R2JV1W1bA9XQI1yktWVoodTlcl1QV6lNR6RxKAYMBKK14IGMNNWjXZJSMMSNkKeZiAAJqfAAJMA+6QiGIqLR+S7nqu643tu2BRNgpoAtJC2MBVWUjXl9mbatMA7ftR37kSZ3lZdZ4rpenDXpu92Pc9r2Dcen0rd962-XlAOHcdT4HvB52MO1NwLp+11QzD-5jvDL1ve+KNdTAP1HFtsBY0DuNiqDQ3E6eX43b+d13tTiNWXkJRgCMpQhlgLzJhWJxnBcrC6eiLysBApCkLGAASXlXFwgFFrkAAKMtvUtzScTAbHkOs1HjN8IB-T1KyAm9FWWzA1u25cYxfEozvKUoeZWiRCJYF7Nt237juB8Vwe0+HVtR77DsB3lLsJ0jTb039TME0tucZ+NRVTe7y0M-nYNF2NDUTf1pWh21Fd5+jr7tTX3Ul5NJWJ53jNtwTdNVV9te9fXzXTSA81QFEgEum6zVpsUPp+gGxqRtMCHL1GMa3AmzDJp8XpZjmIcFkBuQlrWu74tWuP1gX4N8+Tf63juD57rjM4ExVPOk5DH80M373Vvlxfch4CZExbAA78t1YYAU-nffcL4h7PzJkAim797xWVAuBQQiFbT2h1HBBCWpoJSBtIafMlEpTYXTHhEyRFyrakbC4c+tCsKwAYU8fChEmIslYaKFil8pAcS4rxfiglOBWVEogcSkk1wyTjuPUuUAtKMDCkZAytxtFMLMtsSyoiWYD2KA5Jy7RECuU9IRToXkV) The only difference is the way the DOM nodes are rearranged, which is what confuses me about this. <details> <summary>Code for each</summary> `insertBefore`: ```html <!doctype html> <ul id="animate"><li>0</li><li>1</li><li>2</li><li>3</li></ul> <script> // Factored out so it's clearer what elements are added and removed const animateRoot = document.getElementById("animate") const elem0 = animateRoot.childNodes[0] const elem1 = animateRoot.childNodes[1] const elem2 = animateRoot.childNodes[2] const elem3 = animateRoot.childNodes[3] // ordered: [0, 1, 2, 3] // mixed: [2, 0, 3, 1] function mix() { // ordered -> mixed setTimeout(order, 500) // First const first0 = elem0.getBoundingClientRect().top const first1 = elem1.getBoundingClientRect().top const first2 = elem2.getBoundingClientRect().top const first3 = elem3.getBoundingClientRect().top // Update [0, 1, 2, 3] -> [2, 0, 3, 1] animateRoot.insertBefore(elem2, elem0) animateRoot.appendChild(elem1) // Last + Invert // Note: earlier `transitionDuration` must be set before later // `transform`, or even more weirdness occurs (just try it and see // what happens). elem0.style.transitionDuration = elem1.style.transitionDuration = elem2.style.transitionDuration = elem3.style.transitionDuration = "0s" elem0.style.transform = `translateY(${first0 - elem0.getBoundingClientRect().top}px)` elem1.style.transform = `translateY(${first1 - elem1.getBoundingClientRect().top}px)` elem2.style.transform = `translateY(${first2 - elem2.getBoundingClientRect().top}px)` elem3.style.transform = `translateY(${first3 - elem3.getBoundingClientRect().top}px)` // Force re-layout document.body.offsetHeight // Play elem0.classList.add("transition") elem1.classList.add("transition") elem2.classList.add("transition") elem3.classList.add("transition") elem0.style.transform = elem0.style.transitionDuration = elem1.style.transform = elem1.style.transitionDuration = elem2.style.transform = elem2.style.transitionDuration = elem3.style.transform = elem3.style.transitionDuration = "" } function order() { // mixed -> ordered setTimeout(mix, 500) // First const first0 = elem0.getBoundingClientRect().top const first1 = elem1.getBoundingClientRect().top const first2 = elem2.getBoundingClientRect().top const first3 = elem3.getBoundingClientRect().top // Update [2, 0, 3, 1] -> [0, 1, 2, 3] animateRoot.insertBefore(elem1, elem3) animateRoot.insertBefore(elem2, elem3) // Last + Invert // Note: earlier `transitionDuration` must be set before later // `transform`, or even more weirdness occurs (just try it and see // what happens). elem0.style.transitionDuration = elem1.style.transitionDuration = elem2.style.transitionDuration = elem3.style.transitionDuration = "0s" elem0.style.transform = `translateY(${first0 - elem0.getBoundingClientRect().top}px)` elem1.style.transform = `translateY(${first1 - elem1.getBoundingClientRect().top}px)` elem2.style.transform = `translateY(${first2 - elem2.getBoundingClientRect().top}px)` elem3.style.transform = `translateY(${first3 - elem3.getBoundingClientRect().top}px)` // Force re-layout document.body.offsetHeight // Play elem0.classList.add("transition") elem1.classList.add("transition") elem2.classList.add("transition") elem3.classList.add("transition") elem0.style.transform = elem0.style.transitionDuration = elem1.style.transform = elem1.style.transitionDuration = elem2.style.transform = elem2.style.transitionDuration = elem3.style.transform = elem3.style.transitionDuration = "" } setTimeout(mix, 500) </script> ``` `appendChild`: ```html <!doctype html> <ul id="animate"><li>0</li><li>1</li><li>2</li><li>3</li></ul> <script> // Factored out so it's clearer what elements are added and removed const animateRoot = document.getElementById("animate") const elem0 = animateRoot.childNodes[0] const elem1 = animateRoot.childNodes[1] const elem2 = animateRoot.childNodes[2] const elem3 = animateRoot.childNodes[3] // ordered: [0, 1, 2, 3] // mixed: [2, 0, 3, 1] function mix() { // ordered -> mixed setTimeout(order, 500) // First const first0 = elem0.getBoundingClientRect().top const first1 = elem1.getBoundingClientRect().top const first2 = elem2.getBoundingClientRect().top const first3 = elem3.getBoundingClientRect().top // Update [0, 1, 2, 3] -> [2, 0, 3, 1] animateRoot.appendChild(elem2) animateRoot.appendChild(elem0) animateRoot.appendChild(elem3) animateRoot.appendChild(elem1) // Last + Invert // Note: earlier `transitionDuration` must be set before later // `transform`, or even more weirdness occurs (just try it and see // what happens). elem0.style.transitionDuration = elem1.style.transitionDuration = elem2.style.transitionDuration = elem3.style.transitionDuration = "0s" elem0.style.transform = `translateY(${first0 - elem0.getBoundingClientRect().top}px)` elem1.style.transform = `translateY(${first1 - elem1.getBoundingClientRect().top}px)` elem2.style.transform = `translateY(${first2 - elem2.getBoundingClientRect().top}px)` elem3.style.transform = `translateY(${first3 - elem3.getBoundingClientRect().top}px)` // Force re-layout document.body.offsetHeight // Play elem0.classList.add("transition") elem1.classList.add("transition") elem2.classList.add("transition") elem3.classList.add("transition") elem0.style.transform = elem0.style.transitionDuration = elem1.style.transform = elem1.style.transitionDuration = elem2.style.transform = elem2.style.transitionDuration = elem3.style.transform = elem3.style.transitionDuration = "" } function order() { // mixed -> ordered setTimeout(mix, 500) // First const first0 = elem0.getBoundingClientRect().top const first1 = elem1.getBoundingClientRect().top const first2 = elem2.getBoundingClientRect().top const first3 = elem3.getBoundingClientRect().top // Update [2, 0, 3, 1] -> [0, 1, 2, 3] animateRoot.appendChild(elem0) animateRoot.appendChild(elem1) animateRoot.appendChild(elem2) animateRoot.appendChild(elem3) // Last + Invert // Note: earlier `transitionDuration` must be set before later // `transform`, or even more weirdness occurs (just try it and see // what happens). elem0.style.transitionDuration = elem1.style.transitionDuration = elem2.style.transitionDuration = elem3.style.transitionDuration = "0s" elem0.style.transform = `translateY(${first0 - elem0.getBoundingClientRect().top}px)` elem1.style.transform = `translateY(${first1 - elem1.getBoundingClientRect().top}px)` elem2.style.transform = `translateY(${first2 - elem2.getBoundingClientRect().top}px)` elem3.style.transform = `translateY(${first3 - elem3.getBoundingClientRect().top}px)` // Force re-layout document.body.offsetHeight // Play elem0.classList.add("transition") elem1.classList.add("transition") elem2.classList.add("transition") elem3.classList.add("transition") elem0.style.transform = elem0.style.transitionDuration = elem1.style.transform = elem1.style.transitionDuration = elem2.style.transform = elem2.style.transitionDuration = elem3.style.transform = elem3.style.transitionDuration = "" } setTimeout(mix, 500) </script> ``` </details> I've reproduced this behavior explained above on each of the following platforms: - Chrome 83.0.4103.116 on Windows 10 64-bit - Safari 13.1 on iOS 13 - Firefox 78.0.2 on Windows 10 64-bit Relevant framework bugs I've filed, before I narrowed it down further to this: - https://github.com/MithrilJS/mithril.js/issues/2612 - https://github.com/facebook/react/issues/19406 - https://github.com/preactjs/preact/issues/2637 - https://github.com/infernojs/inferno/issues/1519 ----- 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. -- You are receiving this because you are subscribed to this thread. Reply to this email directly or view it on GitHub: https://github.com/whatwg/dom/issues/880
Received on Monday, 20 July 2020 07:46:29 UTC