0

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.

1 Answer 1

1

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

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the answer and a great article about reflow & repaint. I never run into problem like this before, and the links you provided helped a lot. I did some observations with my Chrome and learned so many interesting things.
@Nier No problem, it was a very interesting topic for me too (and, as it is definitely not a common problem, I learnt a lot too).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.