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:
Using an outside variable.
var fired = false;
jQuery("#main").one('webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend', function(e) {
if ( ! fired ) {
fired = true;
console.log("POUM!");
}
});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
Html:Draw Table Using Innerhtml
How to Animate the Drawing of Text on a Web Page
Using JavaScript to Increment Top/Left/Bottom/Right Values
Bootstrap Collapsed Menu Links Not Working on Mobile Devices
How to Copy Text to the Client's Clipboard Using Jquery
How to Initialize an Array's Length in JavaScript
Get Index of Element as Child Relative to Parent
$(Document).Click() Not Working Correctly on Iphone. Jquery
Official Information on 'Arguments' in Es6 Arrow Functions
How to Reach the Element Itself Inside Jquery's 'Val'
How to Retain the Scroll Position of a Scrollable Area When Pressing Back Button
How to Optimize Website for Touch Devices
Jquery Select Pseudo-Element :After
Change CSS Rule in Class Using Jquery
Sorting HTML Table with JavaScript