The best way to explain my problem is these are two demos:
And that are they:
Demo 1 (works as expected in chrome, in firefox it doesn't work)
let handler = function () {
    block.classList.add("visible");
    requestAnimationFrame(() => block.classList.add("move"));
    block.addEventListener(
        "transitionend",
        () => {
            console.log("style is removed");
            this.addEventListener("click", handler, { once: true });
            block.classList.remove("move");
        },
        { once: true }
    );
};
btn.addEventListener("click", handler, { once: true });.main {
    display: none;
    width: 100px;
    height: 100px;
    background: red;
    transition: all 5.5s ease;
}
.visible {
    display: block;
}
.move {
    transform: translateX(100px);
    background: blue;
}<div id="block" class="main"></div>
<button id="btn">Animate</button>Demo 2 (works incorrect)
let handler = function () {
    block.classList.add("visible");
    block.classList.add("move");
    block.addEventListener(
        "transitionend",
        () => {
            console.log("style is removed");
            this.addEventListener("click", handler, { once: true });
            block.classList.remove("move");
        },
        { once: true }
    );
};
btn.addEventListener("click", handler, { once: true });.main {
    display: none;
    width: 100px;
    height: 100px;
    background: red;
    transition: all 5.5s ease;
}
.visible {
    display: block;
}
.move {
    transform: translateX(100px);
    background: blue;
}<div id="block" class="main"></div>
<button id="btn">Animate</button>I'll fast emphasize differences between two demos:
- first demo has requestAnimationFrame
- <div>block has class "visible" before starting a click handler in first demo
- In the click handler, it has code for adding class "visible" in the second demo
So main question is: Why second demo doesn't fire transitionend event and why I don't see transition?
Extra demo
let handler = function () {
    alert(1);
    block.classList.add("visible");
    alert(2);
    setTimeout(() => {
      alert(4);
      block.classList.add("move");
      alert(5);
    });
    block.addEventListener(
        "transitionend",
        () => {
            console.log("style is removed");
            this.addEventListener("click", handler, { once: true });
            block.classList.remove("move");
        },
        { once: true }
    );
    alert(3);
};
btn.addEventListener("click", handler, { once: true });.main {
    display: none;
    width: 100px;
    height: 100px;
    background: red;
    transition: all 5.5s ease;
}
.visible {
    display: block;
}
.move {
    transform: translateX(100px);
    background: blue;
}<div id="block" class="main"></div>
<button id="btn">Animate</button>Checkout it in chrome and firefox, seems what some part of code invokes repainting earlier, if it is not, correct me and explain why.
 
    