2015-09-09 2 views
18

Мне нужно специальное событие 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

Можете ли вы отредактировать CSS для дочерних элементов, которые переходят? –

+0

Но переход уже на ребенка в примере –

+0

Если вы можете редактировать сам CSS, я думаю, что могу помочь вам придумать более чистый способ. Дайте мне знать –

ответ

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

Я использовал treeWalker api для перемещения исходного узла (root) и всех дочерних узлов (только элементов), отфильтровывает элементы без переходов и собирает свойства переходов до queue (fiddle). Как вы можете видеть, я решил разницу во времени между complete-div и complete-p, и теперь они стреляют (почти - пара мс) одновременно.

Есть два предостережения, для которых у меня нет обходных путей:

  1. Если есть переходы, которые запускаются различными способами, например, один срабатывают при добавлении .visible к div, а другого добавив .invisible, все они будут добавлены в queue. Событие никогда не будет срабатывать, поскольку queue никогда не будет пустым - я не знаю, как это решить.
  2. Если есть переходы контекстных свойств (padding для примера), множественные transitionend события могут быть уволены с transition-property, такие как padding-top, padding-right и т.д ... Это заставит массив очень быстро, как splice(-1, 1) опорожнить удаляет элемент из конца массива. У меня был workaround, но , который может вызвать проблемы, так как он может удалить другие свойства в queue. Лучшим обходным решением является переход на ярлык .

Код для woodWalker основан на Ban Nadel'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

$(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/

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"); 
} 

Я уверен, что кто-то объяснить, почему реализация, как это не будет работа, но, возможно, это обеспечит некоторую вдохновительницу/обсуждение.

https://jsfiddle.net/nf8gvbuo/16/

+0

Все в порядке. Единственная причина, по которой я пошел на специальное мероприятие, - это то, что он используется во многих местах и ​​хочет избежать кода обмана –

Смежные вопросы