I need to detect if a CSS transition is completed before allowing a function to repeat again, to prevent messing up the margins.
So how cam I have something like
You can create a method which will keep in mind when the transition end has been called the last time, and thus only trigger the callback once.
function transitionEndsOnce($dom, callback) {
var tick = Date.now();
$dom.on('transitionend webkitTransitionEnd oTransitionEnd otransitionend MSTransitionEnd', function(e) {
var diff = Date.now() - tick;
tick = Date.now();
if (diff > 20) { // this number can be changed, but normally all the event trigger are done in the same time
return callback && callback(e);
}
});
}
and then simply use it this way
transitionEndsOnce($('...'), function(e){
console.log('transition ends once');
});
I think this link might be helpful to you.
There is a single event that is fired when transitions complete. In Firefox, the event is transitionend, in Opera, OTransitionEnd, and in WebKit it is webkitTransitionEnd.
el.addEventListener("transitionend", updateTransition, true);
Use jQuery data to attach stateful data to the element. Use a boolean value to "block" events from happening and flip the boolean variable once transitionend completes. Use xram's code to hook up all cross-browser transitionend events at the same time.
So for your example...
I noticed such similar questions like "how do I catch touchend events?" or "mouseup events?" etc.
They are all similar for these points of view
plural: handlers are fired many times even when we are expecting to be fired by a single event
depth: events are bubbling deeper in the tree before our handler catches them.
Examples:
a touchstart
can be followed by a mousedown
and vice versa in a device that has both a mouse and a touch screen,
a touchend
can be followed by a mouseup
for the similar reasons,
a animationstart
can be followed by many other similar events depending on how you wrote the css,
a animationend
can also be followed by many similar events for the above reasons as well.
If we need to fire our handler once per a set of similar events, i.e. events that produced by a single action like a person that presses sth. we need to introduce an event lock and use 2 handlers one at the beginning of an event and one handler at the end even if we don't need or want a handler of the side event.
The lock can be a property at the parent node higer in the tree as you guessed.
For the infamous event couple: animationstart
- animationend
such a function can be:
var oneTransition = (function(){
var $parent,
type,
callback,
unlockCallback,
newCallback,
start = 'webkitTransitionStart otransitionstart oTransitionStart msTransitionStart transitionstart',
end = 'webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend';
unlockCallback = function(){
$parent.data('oneTransitionLock', false);
};
newCallback = function(e){
if($parent.data('oneTransitionLock'))
return;
else
$parent.data('oneTransitionLock', true);
callback(e);
};
return function(){
var args = Array.prototype.slice.call(arguments, 0);
$parent = $(args[0]); // 1st arg
type = args[1]; // 2nd arg
callback = args[2]; // 3rd arg
if((args.length < 3) || ((type != 'start') && (type != 'end')) || (typeof(callback) != 'function'))
return;
$parent.off(start).off(end);
if(type == 'start'){
$parent.data('oneTransitionLock', false);
$parent.on(start, newCallback);
$parent.on(end, unlockCallback);
}else if(type == 'end'){
$parent.on(start, unlockCallback);
$parent.on(end, newCallback);
}
}
})();
and you can call it like:
oneTransition(node, 'start' or 'end', funcion(){...});
The interesting part is that it can runs for either the start or the end of animation:
1st arg. a node reference,
2nd arg. a string representing the event for our callback and
3rd arg. our actual callback.
jsFiddle
The code below will trigger on the transitionend event for whatever element(s) you have in the $element variable. There are four different event names as I believe these cover all of the cross-browser inconsistencies. Replace the '// your event handler' comment with whatever code you wish to run when the event is triggered.
$element.on('transitionend webkitTransitionEnd oTransitionEnd', function () {
// your event handler
});