【问题标题】:transitionend event fires twicetransitionend 事件触发两次
【发布时间】:2013-09-12 09:34:26
【问题描述】:

我有以下代码,我的问题是transitionend 事件被触发了两次。我不知道这是什么原因造成的。我怀疑供应商前缀导致了它,但他们没有。即使我只留下transitionendtransition 它仍然会触发两次。

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>

【问题讨论】:

    标签: javascript css


    【解决方案1】:

    transitionend 为每个转换的属性触发,在您的情况下为 topleft

    您可以在event.propertyName 访问与事件关联的属性。

    没有“transitionsend”事件,因此您可能需要一些技巧,例如过滤transitionend 回调处理仅针对其中一个已转换属性。例如:

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

    ps。没有浏览器触发MSTransitionEnd 事件。它出现在 MS 文档中,但在 IE10 测试版发布之前的某个时候,它被标准的transitionend 事件所取代。

    【讨论】:

    • 当您有一系列转换并且您想知道何时完成所有转换而没有硬编码转换属性时,这真是令人沮丧。
    • @stoutie 如果您不介意将所有过渡样式和事件处理(例如:hover)移至JavaScript,您可以使用Web Animations API,它为您提供一个干净的onfinish 回调。
    • 不确定我是否在这里遗漏了什么,但我相信要检查你需要的属性名称 event.propertyName 至少 event.originalEvent 在 Chrome 38 中未定义。MDN 文档似乎支持我对此表示赞同:developer.mozilla.org/en-US/docs/Web/Events/transitionend
    • @rich97 在我的示例代码中,eventjQuery event objectjQueryEvent.originalEvent 暴露底层native event object。如果您使用的是本机 API,则您已经可以直接访问本机事件。 =]
    【解决方案2】:

    每个已转换的属性都会触发该事件。

    Fabricio 建议的 propertyName 方法是正确的方法,但是如果您使用 jQuery,您也可以使用 one();,就像这样。

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

    【讨论】:

    • +1 也可以,但这样每次转换时都必须附加回调。
    • @FabrícioMatté 是的,我知道这就是为什么我说视情况而定。所以不管你的船是什么。
    • 我使用了这个,但transitionend webkitTransitionEnd 触发了两次,所以我在.one $(this).off('webkitTransitionEnd moztransitionend transitionend oTransitionEnd'); 中添加了这个
    • 这实际上并没有解决问题...回调函数仍然会为每个被转换的属性触发
    • 当提问者明确没有使用jQuery解决方案时,请不要提交...
    【解决方案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's 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 "special event",更多的是作为我正在研究的东西的概念证明,但我可能会在 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】:

      您可以使用target 属性过滤掉由子元素触发的事件,并使用propertyName 仅在特定属性更改时触发事件。

      const handleTransitionEnd = event => {
        if (event.target !== myDomElementRef) return;
        if (event.propertyName !== "height") return;
      
        // Do my things
      };
      

      【讨论】:

        【解决方案6】:

        这是一个相对古老的问题,但我想我会分享我的答案:

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

        【讨论】:

        • 这和问题有什么关系?
        猜你喜欢
        • 2015-12-04
        • 2016-11-08
        • 1970-01-01
        • 2014-04-07
        • 2021-04-10
        • 2013-03-28
        • 1970-01-01
        相关资源
        最近更新 更多