0

I know that CSS transitions do not work with auto keyword, so if I want to apply transition: left 2s, right 2s; have to vary both right and left from (let's say) 0 to 100.

I have a script which might change an object's position either towards left or right, but I cannot set right: 100 if left is set to 0 (it has to be auto).

So I came out with this solution.

function moveObject(direction) {
    direction ? myobject.style.right = 0 : myobject.style.left = 0;    //same position as auto but can be transitioned
    direction ? myobject.style.right = "50vw" : myobject.style.left = "50vw";    //actual transition
}

function resetObject() {
    myObject.style.left = myObject.style.right = "auto";   //now I can move to either right or left again
    //I don't need an animation on reset
}
#myobject {
    position: absolute;
    left: auto;
    right: auto;
    
    /*width, height...*/
}
<div id="myObject"></div>

This didn't work (the assign to 0 was completely ignored).

So I came out with this solution instead (same as above but using setTimeout(..., 0);

function moveObject(direction) {
    direction ? myobject.style.right = 0 : myobject.style.left = 0;

    setTimeout(function() {
        direction ? myobject.style.right = "50vw" : myobject.style.left = "50vw";    //this is executed async
    }, 0);
}

function resetObject() {
    myObject.style.left = myObject.style.right = "auto";
}
#myobject {
    position: absolute;
    left: auto;
    right: auto;
    
    /*width, height...*/
}
<div id="myObject"></div>

This improved the result, but sometimes (it seems to be completely random, but more frequently on small screens), the transition still does not occur.

I supposed that this was because of the fact that sometimes the asynchronous function is executed too soon, so I tried to increase the delay to 10ms, but the problem still occurs sometimes (less frequently).

Now, I could increase the value furthermore, but:

  • I can never be sure that the error will not occur anyway sooner or later, maybe on a faster device (unless I set the timeout to a very large number)
  • I cannot set the timeout to a very large number, since I want it to be imperceptible

So, is there a minimum number that can assure a successful output?

If not, how can accomplish the same result?

Simo Pelle
  • 141
  • 1
  • 11
  • 1
    You need to force trigger the css changes https://stackoverflow.com/questions/21664940/force-browser-to-trigger-reflow-while-changing-css/21665117 – Akxe Jan 31 '21 at 02:42
  • @Akxe7 thank you very much! I can't test it now but it seems to be exactly what I need... I'll let you know as soon as I try it. – Simo Pelle Jan 31 '21 at 10:40

1 Answers1

0

As @Akxe suggested, the solution was reflowing the page. In other words, browsers flush multiple changes of the DOM all together. This speeds up the page by a bit but leads to some problems like the one I described (further information here)

As suggested here, the solution was a function like this one:

function reflow() {    //this will be called between the two elements
    void(document.documentElement.offsetWidth);
}

function moveObject(direction) {
    direction ? myobject.style.right = 0 : myobject.style.left = 0;
    reflow();   //the line above is now flushed
    direction ? myobject.style.right = "50vw" : myobject.style.left = "50vw";
}

function resetObject() {
    myObject.style.left = myObject.style.right = "auto";
}
#myobject {
    position: absolute;
    left: auto;
    right: auto;
    
    /*width, height...*/
}
<div id="myObject"></div>

The function actually worked for me even without using void, but I preferred keeping it since in the referenced question someone pointed out that it didn't work for him, and also for more clarity.

This could also be useful: it is an (unofficial) list of everything that causes page reflow.

Simo Pelle
  • 141
  • 1
  • 11