简体   繁体   中英

CSS3 one-way transition controlled by JavaScript doesn't work when add/remove class sequentially

I want to do a CSS3 transition in one-way. For example, I want to highlight some element by setting it's background-color: yellow; immediately, and set it back to background-color: white; with one-way transition.

I tried to use JavaScript to achieve this like this:

 const highlightBtn = document.getElementById("highlightBtn"); const highlightToggleBtn = document.getElementById("highlightToggleBtn"); const highlightTimeoutBtn = document.getElementById("highlightTimeoutBtn"); const bodyClassList = document.getElementsByTagName("body")[0].classList; highlightBtn.addEventListener("click", () => { bodyClassList.add("highlight"); bodyClassList.remove("highlight"); /* This doesn't work, either. bodyClassList.toggle("highlight"); bodyClassList.toggle("highlight"); */ }); highlightToggleBtn.addEventListener("click", () => { bodyClassList.toggle("highlight"); }); highlightTimeoutBtn.addEventListener("click", () => { bodyClassList.add("highlight"); setTimeout(() => {bodyClassList.remove("highlight");}); /* This works, too. bodyClassList.toggle("highlight"); setTimeout(() => {bodyClassList.toggle("highlight");}); */ }); 
 body { transition: background-color 1s ease; background-color: white; } body.highlight { transition: background-color 0s ease; background-color: yellow; } 
 <button id="highlightBtn"> Highlight </button> <button id="highlightToggleBtn"> Highlight Toggle </button> <button id="highlightTimeoutBtn"> Highlight Timeout </button> 

The problem is, if I toggle the class once at a time, transition works perfectly.

// This works fine.
highlightToggleBtn.addEventListener("click", () => {
    bodyClassList.toggle("highlight");
});

However, for the original goal, I want to highlight the element, so I put add/remove to the same element just want to see the one-way transition, it failed.

highlightBtn.addEventListener("click", () => {
    bodyClassList.add("highlight");
    bodyClassList.remove("highlight");
    /* 
    This doesn't work, either.
    bodyClassList.toggle("highlight");
    bodyClassList.toggle("highlight");
    */
});

But, if I use setTimeout with delay is 0 ms, the result is ideal.

highlightTimeoutBtn.addEventListener("click", () => {
    bodyClassList.add("highlight");
    setTimeout(() => {bodyClassList.remove("highlight");});
    /*
    This works, too.
    bodyClassList.toggle("highlight");
    setTimeout(() => {bodyClassList.toggle("highlight");});
    */
});

Why the second method doesn't work? Is putting the removeClass in setTimeout the best solution? I also tried put a delay in my body's transition CSS like: transition: background-color 1s ease 1ms; , but it doesn't work, either.

You need to reflow/repaint after changing classes as otherwise the browser will optimise and skip straight to the end state (basically, the browser won't update the UI until all the functions have finished running). You can use setTimeout , but if you feel it's too bloated (and setTimeout is actually not always reliable), you can trigger/force a reflow by accessing the .offsetHeight property of the element:

element.classList.add('highlight')
element.offsetHeight
element.classList.remove('highlight')

I suggest you read this article, as it will help you understand much more and avoid pitfalls in the future: http://www.phpied.com/rendering-repaint-reflowrelayout-restyle/

There are also other ways of forcing a reflow: https://gist.github.com/paulirish/5d52fb081b3570c81e3a

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM