我需要一个特殊的transitionend
- 类似事件,在所有转换完成后触发一次,或者如果CSS中没有定义转换则立即触发。
这是我到目前为止所得到的:
(function($){
$.event.special.transitionsComplete = {
setup: function(data, namespaces, eventHandle){
var queue = [],
style = window.getComputedStyle(this, null),
computedProps = style.getPropertyValue('transition-property').split(', '),
computedDurations = style.getPropertyValue('transition-duration').split(', '),
$node = $(this);
// only count properties with duration higher than 0s
for(var i = 0; i < computedDurations.length; i++)
if(computedDurations[i] !== '0s')
queue.push(computedProps[i]);
// there are transitions
if(queue.length > 0){
$node.on('webkitTransitionEnd.x transitionend.x', function(e){
queue.splice(queue.indexOf(e.originalEvent.propertyName));
if(queue.length < 1)
$node.trigger('transitionsComplete');
});
// no transitions, fire (almost) immediately
}else{
setTimeout(function(){
$node.trigger('transitionsComplete');
}, 5);
}
},
teardown: function(namespaces){
$(this).off('.x');
}
};
})(jQuery);
我做了一个实例here。
唯一的问题是它只有在元素本身具有过渡属性时才有效,忽略了来自子元素的过渡。如果我将transitionsComplete
切换到transitionend
,则在子转换完成后运行父事件处理程序和子事件处理程序。是否有某种方式,或者更好的方法来确定一个元素是否发生过它或它的孩子的转变?如果可能的话,我想避免手动通过孩子并检查他们的过渡属性。 (无论如何这都不可靠,因为即使有些孩子有过渡,也不意味着他们会在那时活跃)
答案 0 :(得分:3)
我使用treeWalker api遍历原始节点(root)和所有子节点(仅元素),过滤掉没有过渡的元素,并将过渡属性收集到queue
( fiddle)。正如您所看到的,我已经解决了complete-div
和complete-p
之间的时差,并且它们现在同时发射(几乎是几毫秒)。
有两个警告,我没有解决方法:
.visible
添加到div
来触发的
其他人通过添加.invisible
,将全部添加到queue
。事件永远不会激发,因为queue
永远不会消失 - 我不知道如何解决这个问题。padding
为
例如),可能会使用transitionend
触发多个transition-property
事件,例如padding-top
,padding-right
等...
会导致数组很快耗尽splice(-1, 1)
从数组末尾删除项目。我有workaround,但是
这可能会导致问题,因为它可能会删除其中的其他属性
queue
。最好的解决方法是不要在快捷方式上进行转换
属性。treeWalker的代码基于Ban Nadel&#39; s Finding HTML Comment Nodes In The DOM Using TreeWalker。
最后的代码:
(function ($) {
$.event.special.transitionsComplete = {
setup: function (data, namespaces, eventHandle) {
var TRANSITION_PROPERTY = 'transition-property';
var TRANSITION_DURATION = 'transition-duration';
var root = this;
var queue = [];
var $node = $(this);
function filter(node) { // filter for treeWalker
/*** filters transitions which are a string with one '0s'. If more then '0s' is defined it will be catched when creating the queue ***/
var computedDuration = window.getComputedStyle(node, null)
.getPropertyValue(TRANSITION_DURATION);
return computedDuration === '0s' ? NodeFilter.FILTER_SKIP : NodeFilter.FILTER_ACCEPT;
}
filter.acceptNode = filter; // for webkit and firefox
/** create the treeWalker to traverse only elements **/
var treeWalker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, filter, false);
/** traverse all elements using treeWalker.nextNode(). First node is the root **/
do {
var style = window.getComputedStyle(treeWalker.currentNode, null);
var computedProps = style.getPropertyValue(TRANSITION_PROPERTY).split(', ');
var computedDurations = style.getPropertyValue(TRANSITION_DURATION).split(', ');
/** push all props with duration which is not 0s **/
computedDurations.forEach(function (duration, index) {
duration !== '0s' && queue.push(computedProps[index]);
});
} while (treeWalker.nextNode()); // iterate until no next node
// no transitions, fire (almost) immediately
if (queue.length === 0) {
setTimeout(function () {
$node.trigger('transitionsComplete');
}, 5);
return; // return out of the function to skip the transitions block
}
// there are transitions
$node.on('webkitTransitionEnd.x transitionend.x', function (e) {
var propertyName = e.originalEvent.propertyName;
var indexOfProp = queue.indexOf(propertyName);
queue.splice(indexOfProp, 1);
if (queue.length < 1) {
console.log('Transitions Complete');
$node.trigger('transitionsComplete');
}
});
},
teardown: function (namespaces) {
$(this).off('.x');
}
};
})(jQuery);
答案 1 :(得分:3)
if($('div').one('webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend')) {
$('div').one('webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend', function(e) {
console.log("Fire after transitions");
});
} else {
console.log("Fire immediately if there are no transitions");
}
我确信有人会解释为什么这样的实现不起作用,但也许会提供一些灵感/讨论。
答案 2 :(得分:2)
所以,你去,确实我检查了孩子们: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);
//});
答案 3 :(得分:1)
$(function() {
var div = $("div"),
p = $("p"),
start = new Date().getTime();
console.log("-- start --");
div.addClass("visible");
var n = 0
, transitions = [];
div.on({
"transitionend": function(e) {
++n;
transitions.push({
"element": e.originalEvent.srcElement,
"property": e.originalEvent.propertyName,
"duration": e.originalEvent.elapsedTime
});
var container = $(this).css("transition").split(","),
elems = $(p, this).css("transition").split(",");
if (container.length === 1 && n === elems.length) {
$(this).trigger("transitionComplete", [transitions])
}
},
"transitionComplete": function(e, tx) {
console.log(e.type, this, (new Date().getTime() - start) / 1000, tx);
alert(e.type);
}
});
});
p {
opacity: 0;
transition: opacity 10s, transform 5s;
background: red;
width: 50px;
height: 50px;
margin: 100px;
}
div.visible p {
opacity: 1;
transform: scale(1.5);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div>
<p></p>
</div>
jsfiddle http://jsfiddle.net/nf8gvbuo/1/