How do I get my transitionend event to fire?

I’m learning about the <progress> element and trying to create a CSS transition to make it look smooth as it’s value increases. I want to execute JS after the transitionend event fires but it won’t.

What am I doing wrong?

$("button").on("click", function () {
    $("progress").val(100);
});

$("progress[value]").on(
    "transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd",
    function () {
        $("progress").val(0);
        alert("Completed");
    }
);
body {
    margin: 0;
    padding: 0;
    text-align: center;
}

button {
    margin: 10px;
    text-transform: uppercase;
    font-size: 1em;
}

.loadPageProgress progress {
    display: block;
    width: 100%;
    height: 5px;
    border: none;
    border-radius: 0;
    background-color: green;
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
}

.loadPageProgress progress[value] {
    background-color: green;
    transition: all 1000ms ease-in-out;
    -webkit-transition: all 1000ms ease-in-out;
    -moz-transition: all 1000ms ease-in-out;
    -o-transition: all 1000ms ease-in-out;
}

.loadPageProgress progress[value]::-webkit-progress-bar {
    width: 100%;
    height: 5px;
    border: none;
    border-radius: 0;
    background-color: grey;
}

.loadPageProgress progress[value]::-webkit-progress-value {
    background-color: green;
    transition: all 1000ms ease-in-out;
    -webkit-transition: all 1000ms ease-in-out;
    -moz-transition: all 1000ms ease-in-out;
    -o-transition: all 1000ms ease-in-out;
}

.loadPageProgress progress[value]::-moz-progress-bar {
    width: 100%;
    height: 5px;
    border: none;
    border-radius: 0;
    background-color: grey;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="loadPageProgress">
    <progress value="0" max="100"></progress>
</div>

<button>start transition</button>

Answer

.val(x) does not actually fire a transition on the progress element. It updates instantly. Your transition you are seeing is on the pseudo element ::-webkit-progress-value, which to my knowledge seems to be impossible to target with jQuery because it’s not part of the DOM.

You can see this if I remove the transitions from your pseudo-element’s CSS. It no longer has a transition and just updates the progress instantly.

Instead, you might use .animate or something similar to update your progress and get completion or even step callbacks. See code snippet #2 for an implementation of that.

Snippet 1: Psuedo Transitions Removed.

$("button").on("click", function () {
    $("progress").val(100);
});

$("progress[value]").on(
    "transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd",
    function () {
        $("progress").val(0);
        alert("Completed");
    }
);
body {
    margin: 0;
    padding: 0;
    text-align: center;
}

button {
    margin: 10px;
    text-transform: uppercase;
    font-size: 1em;
}

.loadPageProgress progress {
    display: block;
    width: 100%;
    height: 5px;
    border: none;
    border-radius: 0;
    background-color: green;
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
}

.loadPageProgress progress[value] {
    background-color: green;
    transition: all 1000ms ease-in-out;
    -webkit-transition: all 1000ms ease-in-out;
    -moz-transition: all 1000ms ease-in-out;
    -o-transition: all 1000ms ease-in-out;
}

.loadPageProgress progress[value]::-webkit-progress-bar {
    width: 100%;
    height: 5px;
    border: none;
    border-radius: 0;
    background-color: grey;
}

.loadPageProgress progress[value]::-webkit-progress-value {
    background-color: green;
}

.loadPageProgress progress[value]::-moz-progress-bar {
    width: 100%;
    height: 5px;
    border: none;
    border-radius: 0;
    background-color: grey;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="loadPageProgress">
    <progress value="0" max="100"></progress>
</div>

<button>start transition</button>

Snippet 2: Animate

$("button").on("click", function () {
    $("progress").animate({value:100},{
      duration:2000,
      step: function(x){
        
      },
      complete: function(){
        console.log("Done!");
      }
    });
});
body {
    margin: 0;
    padding: 0;
    text-align: center;
}

button {
    margin: 10px;
    text-transform: uppercase;
    font-size: 1em;
}

.loadPageProgress progress {
    display: block;
    width: 100%;
    height: 5px;
    border: none;
    border-radius: 0;
    background-color: green;
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
}

.loadPageProgress progress[value] {
    background-color: green;
    transition: all 1000ms ease-in-out;
    -webkit-transition: all 1000ms ease-in-out;
    -moz-transition: all 1000ms ease-in-out;
    -o-transition: all 1000ms ease-in-out;
}

.loadPageProgress progress[value]::-webkit-progress-bar {
    width: 100%;
    height: 5px;
    border: none;
    border-radius: 0;
    background-color: grey;
}

.loadPageProgress progress[value]::-webkit-progress-value {
    background-color: green;
}

.loadPageProgress progress[value]::-moz-progress-bar {
    width: 100%;
    height: 5px;
    border: none;
    border-radius: 0;
    background-color: grey;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="loadPageProgress">
    <progress value="0" max="100"></progress>
</div>

<button>start transition</button>