Переходное событие срабатывает дважды

У меня есть следующий код, и моя проблема в том, что событие transitionend запускается дважды. Я не знаю, что вызывает это. Я подозревал, что префиксы поставщика вызвали его, но они этого не делают. Даже если я оставлю только transitionend и transition, он все равно будет стрелять дважды.

CSS

transition: 1s ease-out;

JS

document.addEventListener('click', function (e) {
    var submarine = document.querySelector('.submarine');
    var submarineX = e.clientX - submarine.offsetWidth / 2;
    var submarineY = e.clientY - submarine.offsetHeight / 2;

    submarine.style.left = submarineX + "px";
    submarine.style.top = submarineY + "px";
});

document.addEventListener('transitionend', function (event) {
    console.log(event.type + " " + new Date().getTime());
});

Fiddle

document.addEventListener('transitionend', function (event) {
    console.log(event.type + " " + new Date().getTime());
});

document.addEventListener('click', function (e) {
    var submarine = document.querySelector('.submarine');
    var submarineX = e.clientX - submarine.offsetWidth / 2;
    var submarineY = e.clientY - submarine.offsetHeight / 2;

    submarine.style.left = submarineX + "px";
    submarine.style.top = submarineY + "px";
});
.submarine {
    position: absolute;
    top: 0;
    left: 0;
    width: 20px;
    height: 20px;
    background-color: red;
    border-radius: 50%;
    transition: 1s ease-out;
}
<div class="submarine"></div>

Ответ 1

transitionend срабатывает для каждого свойства, перенесенного в вашем случае top и left.

Вы можете получить доступ к свойству, связанному с событием, в event.propertyName.

Нет события "переход s end", поэтому вам, вероятно, понадобится хакерство, например, фильтрация обработки обратного вызова transitionend только для одного из перешедших свойств. Например:.

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

пс. Ни один браузер не запускает событие MSTransitionEnd. Это было в какой-то момент в документах MS, но когда-то до бета-версии IE10 он был заменен стандартным событием transitionend.

Ответ 2

Событие запускается для каждого свойства, которое было перенесено.

Способ propertyName, предложенный Fabricio, является правильным способом сделать это, однако в зависимости от обстоятельств вы также можете использовать one();, как это.

$(document).one('transitionend webkitTransitionEnd MSTransitionEnd', function() {
   ...
});

Ответ 3

Для тех, кто ищет простое одноразовое решение для копирования и вставки (я включил только необходимый css). Это не отвечает на вопрос, и он отвечает на то, что я искал, когда я приземлился здесь.

CSS

.my-elem {
    transition: height 0.5s ease-out, opacity 0.5s ease-out;
}

JavaScript:

var elem = document.querySelector(".my-elem");

var transitionCounter = 0;

var transitionProp = window.getComputedStyle(elem , null)["transition-property"] || "";

// We just need to know how many transitions there are
var numTransitionProps = transitionProp.split(",").length;

elem.addEventListener("transitionend", (event) => {
  // You could read event.propertyName to find out which transition was ended, 
  // but it not necessary if you just want to know when they are all done.
  if (transitionCounter < (numTransitionProps - 1)) {
    transitionCounter++;
  } else {
    transitionCounter = 0; // reset
    alert("I'm done!!!"); // do what you need to
  }
}, false);

Протестировано в IE11, Chrome 48 и Firefox 37.

Ответ 4

Для тех, кто все еще ищет более надежное решение, например событие "allTransitionEnd", я внедрил jQuery "специальное событие" , подробнее как доказательство концепции для чего-то, над чем я работал, но я мог бы выпустить lib на Github.

Посмотрите JSBin.

Это довольно сложно, поэтому я не буду объяснять слишком много, но это делает его очень легким, чтобы делать вещи после того, как все переходы закончились на элементе:

$(function () {

    $element.on('allTransitionEnd', function () {
        // do something after all transitions end.
    });

});

Он работает, исследуя элемент для свойств перехода, затем привязывается к нативным событиям transitionend (включая специфику поставщика), чтобы отслеживать свойства, которые завершили переход. Когда все закончили переход, он запускает любые связанные обработчики allTransitionsEnd, а затем очищает свойства перехода, в случае их изменения, а также зонды для них в следующий раз.

Это действительно полезно, когда несколько свойств переходят с переменной задержкой и/или продолжительностью, и вы хотите что-то сделать после завершения всех переходов.

Примеры использования:

  • Удалите флэш-сообщение после исчезновения и сжатия.
  • Запускать "открытые" и "закрытые" события в компоненте многократного использования, таком как меню или модальный, где потребители могут захотеть выполнить некоторую логику после завершения перехода, не вдаваясь в css-переходы.

Если вы только переходите к одному свойству или не имеете разных задержек и/или продолжительности, то простое решение прекрасно работает.

Работает в последней версии Chrome, Firefox, Safari, Mobile Safari и IE11 и IE10. Не работает в IE8, потому что переходы не поддерживаются. Привяжите к дополнительному родному событию как резерв.

Ответ 5

Это относительно старый вопрос, но я решил поделиться своим ответом:

function OnTransitionEvent() {
    var t,
        el = document.createElement('transitionElement');

    var transitions = {
        'transition'      : 'transitionend',
        'OTransition'     : 'oTransitionEnd',
        'MozTransition'   : 'transitionend',
        'WebkitTransition': 'webkitTransitionEnd'
    };

    for (t in transitions){
        if (el.style[t] !== undefined){
            return transitions[t];
        }
    }
}

var transitionEvent = OnTransitionEvent();

$(document).one(transitionEvent, function() { 
    console.log('done');
});