transitionend

不止一次地在项目中使用到 transitionend,这个事件用于监测 transition 动画是否完成,当完成时调用回调函数。与此相似的还有 animationend 事件。

开始使用 transitionend 时觉得很难以理解,因为在大部分情况下,当我希望监测某个元素是否完成动画时,这个事件总是被触发了很多次,而且触发的时间也远远提前于动画完成时间。

子元素事件冒泡

如果子元素也有transition的话,transitionend事件会冒泡。DOM 结构如下,只包含一个父元素和一个子元素。

1
2
3
<div class="parent">
<div class="child"></div>
</div>

父元素和子元素都包含 transition 属性,当添加 active 的 class 时触发动画,但是动画的结束时间不同,以便我们更好地观察究竟是哪个元素触发了 transitionend 事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.parent {
transform: translateX(-100px);
transition: transform 3s linear;
}

.child {
transform: translateX(100px);
transition: transform 1s linear;
}

.parent.active,
.parent.active .child {
transform: translateX(0);
}

此外,我们对父元素进行了事件监测,当 transition 完成时,打印出 “end”。

1
2
3
$('.parent').on('transitionend',function(e){
console.log('end');
})

我们期望的结果当然是父元素经过 3s 的动画后,打印出 end,但结果却打印了两个。我们发现子元素中的 transitionend 事件会冒泡到父元素上,因此会监测到两个 transitionend,分别在 1s (子元素 transition 完成)和 3s (父元素 transition 完成)时。

在大部分情况下,如果我们只想监测当前元素的动画完成情况,需要在子元素中阻止冒泡。

1
2
3
$('.child').on('transitionend',function(e){
e.stopPropagation();
})

结果和我们想象的一样。现在我们实现了只监测父元素动画,3s 之后控制台打印出一个 “end”。

transition 中包含不同的属性

如果 transition 属性像下面这样,包含了不止一个 transition-property,究竟会监测到一个还是两个 transitionend 事件呢?

1
transition: opacity 1s linear, transform 3s linear;

为了考察这个问题,我们在 HTML 中去掉子元素,只保留了父元素。

1
<div class="parent"></div>

为父元素设置两个属性变动,其中 opacity 属性提前于 transform 到达最终状态,动画时间较短。

1
2
3
4
5
6
7
8
9
10
.parent {
transform: translateX(-100px);
opacity: 0;
transition: opacity 1s linear, transform 3s linear;
}

.parent.active {
transform: translateX(0);
opacity: 1;
}

结果如下:

所以 transitionend 事件除了会有子元素的冒泡,还会监听不同属性。这就很好地解释了为什么我们在平时的使用里会得到比预期多得多的 transitionend 事件!