Transitionend Event Fires Twice

transitionend event fires twice

transitionend fires for each property transitioned, in your case top and left.

You can access the property associated with the event at event.propertyName.

There's no "transitionsend" event, so you will probably need some hackiness such as filtering the transitionend callback handling for only one of the transitioned properties. E.g.:

function (event) {
if (event.propertyName == 'top') {
//put your code here
}
});

ps. No browser fires the MSTransitionEnd event. It was at some point in the MS docs, but sometime before the IE10 beta release it was replaced by the standard transitionend event.

Why does the event 'transitionend' fires twice?

Your transactionend event has associated with transition style of "key" class.
Which reacts by adding one more class "playing".
It fired when you add playing class and again when you remove it.
Also you use "All" in your transition style which call this event multiple of style attribute change.

Solution : I think it is not feasible with transactionend event. Because it fires ones when it done transition and once more when back to original. Try some different logic or clarify.

Hint : It is better to use "setTimeout" function to remove "playing" class after transition time elapse.

Toggle function fires twice on transitionend

There are two transitionend events firing: one from your #my-toggle <span>, one from your ::before pseudo element. You need to differentiate those two events. You’d need the event argument e for that.

The only difference is then found in e.originalEvent.propertyName (for the corresponding CSS property) and e.originalEvent.pseudoElement.

So let’s check for that last property in the if condition.

Also put another && e.target == this in there, since the transitionend event is also firing for children because the event bubbles up. Since you’re only listening to the event on the parent <span>, you can’t simply call e.stopPropagation which would only be useful to prevent the parent(s) from firing the event.

$("#my-toggle").bind("transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd", function(e) {  if (document.getElementById("my-checkbox").checked && !e.originalEvent.pseudoElement && e.target == this) {    console.log("do this!");  }});
.switch {  position: absolute;  top: 15%;  display: inline-block;  width: 60px;  height: 34px;}
.switch input {display:none;}
.slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; -webkit-transition: .4s; transition: .4s;}
.slider:before,.slider .number { position: absolute; content: ""; height: 26px; width: 26px; left: 4px; bottom: 4px; background-color: white; -webkit-transition: .4s; transition: .4s;}.slider .number { background: none; font-size: 14px; left: 9px; top: 9px;}
input:checked + .slider { background-color: #2196F3;}
input:focus + .slider { box-shadow: 0 0 1px #2196F3;}
input:checked + .slider:before,input:checked + .slider .number { -webkit-transform: translateX(26px); -ms-transform: translateX(26px); transform: translateX(26px);}
/* Rounded sliders */.slider.round { border-radius: 34px;}
.slider.round:before { border-radius: 50%;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><label class="switch">  <input type="checkbox" id = "my-checkbox">  <span class="slider round" id = "my-toggle">     <span class="number">00</span>  </span></label>

A transitionend event that always fires, and once only

So here you go, indeed i inspect the children: http://jsfiddle.net/cegejk59/2/

(function($){

$.event.special.transitionsComplete = {

setup: function( data, namespaces, eventHandle ) {

var allTransitions = [];
w = window,
TRANSITION_PROPERTY_KEY = 'transition-property',
TRANSITION_DURATION_KEY = 'transition-duration',
$node = $( this );

function collectTransitionsRecursively( node ) {

var style = w.getComputedStyle( node ),
nodeComputedProperties = style.getPropertyValue( TRANSITION_PROPERTY_KEY ).split( ', ' ),
nodeComputedDurations = style.getPropertyValue( TRANSITION_DURATION_KEY ).split( ', ' );

for( var i = 0; i < nodeComputedDurations.length; i++ )
if( nodeComputedDurations[ i ] !== '0s' )
allTransitions.push( nodeComputedProperties[ i ] );

for( var childIndex = 0; childIndex < node.children.length; childIndex++ )
collectTransitionsRecursively( node.children[ childIndex ] );
}

function triggerTransitionsComplete( $onNode ) {

console.log( "No transitions (left)." );

$onNode.trigger('transitionsComplete');
}

function onNoTransitionsFound() {

setTimeout( function() {

triggerTransitionsComplete( $node );
});
}

collectTransitionsRecursively( this );

if( allTransitions.length == 0 )
return onNoTransitionsFound();
else
console.log( 'remaining', allTransitions );

$node.on('webkitTransitionEnd.x transitionend.x', function( e ){

allTransitions.splice(allTransitions.indexOf(e.originalEvent.propertyName));

if( allTransitions.length == 0 )
triggerTransitionsComplete( $node );
else
console.log('remaining', allTransitions);
});
},

teardown: function( namespaces ) {

$( this ).off( '.x' );
}
};
})(jQuery);

var div = $('div'), p = $('p'), start = new Date().getTime();
console.log('-- start --');
div.addClass('visible');

div.one('transitionsComplete', function(e){
console.log('complete-div', (new Date().getTime() - start) / 1000);
});

//p.one('transitionsComplete', function(e){
// console.log('complete-p', (new Date().getTime() - start) / 1000);
//});

Enabling vendor prefixes in CSS transitions make callback fires twice

You're not doing anything wrong. Chrome just uses both the prefixed and un-prefixed versions.

There are a couple options:

  1. Using an outside variable.

    var fired = false;
    jQuery("#main").one('webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend', function(e) {
    if ( ! fired ) {
    fired = true;
    console.log("POUM!");
    }
    });
  2. Using some kind of detection to get a single variable for transitionend (the below uses Modernizr, and is taken from their documentation):

    var transitionend = (function(transition) {
    var transEndEventNames = {
    'WebkitTransition' : 'webkitTransitionEnd',// Saf 6, Android Browser
    'MozTransition' : 'transitionend', // only for FF < 15
    'transition' : 'transitionend' // IE10, Opera, Chrome, FF 15+, Saf 7+
    };

    return transEndEventNames[transition];
    })(Modernizr.prefixed('transition'));

    // then
    jQuery("#main").one(transitionend, function(e) {
    console.log("POUM!");
    });

NOTE:

Safari 6 seems to trigger onload for anything that is set in the CSS. So, if you have (assuming all prefixes)

#main {
width: 40px;
height: 40px;
transition: all 200ms;
}

Safari will trigger the transitionend with width and height on load. There are a couple ways to get around this:

  • Use more specific transition-property (but if you set that in the CSS, it will still trigger)
  • Do the following in javascript (it's not the prettiest thing, but it should take care of that edge case and it still works in Chrome) fiddle

    var transitionProperty = 'background-color',
    startColor = jQuery("#main").on(transitionend, function(e) {
    var el = $(this);
    if ( transitionProperty === e.originalEvent.propertyName && el.css(transitionProperty) !== startColor ) {
    console.log("POUM!");
    // This is to make it only happen once.
    $(this).off(transitionend);
    }
    }).css(transitionProperty);

Multiple firings of event listener even after element has been removed?

Why would detaching an element from the DOM remove all event handlers? The element still exists, just not in the DOM. Imagine trying to move an element from one parent to another

element.parentElement.removeChild(element)
newParent.appendChild(element)

Do you really think detaching all event handlers would be a good idea?

That being said, you can solve your issue in two ways.

Check if the element has a parent

elem.addEventListener('transitionend', function(e) {
if (this.parentNode) {
this.parentNode.removeChild(this);
}
}, false);

or

Detach the event listener

var onTransitionEnded = function (e) {
this.parentNode.removeChild(this);
this.removeEventListener('transitionend', onTransitionEnded);
}
elem.addEventListener('transitionend', onTransitionEnded)

transitionEnd event with multiple transitions, detect last transition

transitionEnd returns a property called propertyName in the event object.
Source

Therefore you can test for the property you want and use simple logic to filter the callbacks:

document.querySelector('a').addEventListener('transitionend', function(event){
if(event.propertyName !== 'width') return;
console.log('transitionEnd - width!');
});


Related Topics



Leave a reply



Submit