setTimeout() 中的代码在多次调用时不会执行
code in setTimeout() does not get executed when called multiple times
我有以下功能:
function deleteFood(index) {
// animate
document.getElementById(foodList[index].id).style.transform = 'translate(500px, 0)';
// delete from airtable
foodList[index].destroy();
window.setTimeout(() => {
// delete from state
let newState = foodList.slice();
newState.splice(index, 1);
setFoodList(newState);
}, 500);
}
基本上有一个列表,用户可以在每个列表条目上单击删除。如果您在一个条目上单击删除,它会动画化,然后在删除时消失。到目前为止,一切正常。
但是如果我非常快地点击删除两个条目,只有第二个被删除。
假设我们有 4 个条目 (0,1,2,3),我在 1 和 2 上单击删除,那么列表仍然有 0,1,3。所有条目都是动画的,并被称为销毁。
我尝试使用 promises 无济于事。我该如何解决这个问题?
如您所述,如果您非常快速地单击删除,则只有第二个被删除,原因是索引变量在超时触发之前被覆盖,这就是为什么第二个是唯一一个被删除的原因删除。
您可以立即删除该项目,然后保留超时,以便在动画完成后仅重置列表。或者,为过渡添加一个侦听器已结束以从列表中删除该项目,而不是等待超时。如果你想听过渡结束,而许多答案有点旧,你可能想回顾
How do I normalize CSS3 Transition functions across browsers?
万一有人遇到这个问题,这就是我最后做的事情(谢谢,@Lynyrd!)
// transition names (found in Modernizr)
let transEndEventNames = {
'WebkitTransition': 'webkitTransitionEnd', // Saf 6, Android Browser
'MozTransition': 'transitionend', // only for FF < 15
'transition': 'transitionend', // IE10, Opera, Chrome, FF 15+, Saf 7+
};
我使用 React,所以我在 useEffect 中添加了事件侦听器,以便在再次获取 foodList 等时自动添加它们。
// add & remove event listeners for transition end
useEffect(() => {
foodList.forEach((item, index) => {
if (!document.getElementById(index)) return;
document
.getElementById(index)
.addEventListener(
transEndEventNames.WebkitTransition,
handleTransitionEnd
);
document
.getElementById(index)
.addEventListener(
transEndEventNames.MozTransition,
handleTransitionEnd
);
document
.getElementById(index)
.addEventListener(transEndEventNames.transition, handleTransitionEnd);
});
return () => {
foodList.forEach((item, index) => {
if (!document.getElementById(index)) return;
document
.getElementById(index)
.removeEventListener(
transEndEventNames.WebkitTransition,
handleTransitionEnd
);
document
.getElementById(index)
.removeEventListener(
transEndEventNames.MozTransition,
handleTransitionEnd
);
document
.getElementById(index)
.removeEventListener(
transEndEventNames.transition,
handleTransitionEnd
);
});
};
}, [foodList]);
我决定不尝试根据当前浏览器只添加一个事件侦听器,但这也是可能的。
请记住,每次转换都会触发此侦听器。结果,当有人将鼠标悬停在其中的一个元素上时(这触发了工具提示),我的 table 条目被删除了。所以不要忘记过滤:
function handleTransitionEnd(event) {
// only delete element for transform transition
if (!(event.propertyName === 'transform')) return;
let newState = foodList.slice();
newState.splice(event.target.id, 1);
setFoodList(newState);
}
在我的例子中,这很简单,因为我在元素上只有一个 "transform" 过渡。另外,我不知道 eventListeners 的数量是否会成为较大 tables 的性能问题。
现在根本不需要 setTimeout() 函数,在 deleteFood() 中,我现在只启动动画并从我的数据库中删除该项目:
function DeleteFood(index) {
// animate
document.getElementById(index).style.transform = 'translate(500px, 0)';
// delete from airtable
foodList[index].destroy();
}
我有以下功能:
function deleteFood(index) {
// animate
document.getElementById(foodList[index].id).style.transform = 'translate(500px, 0)';
// delete from airtable
foodList[index].destroy();
window.setTimeout(() => {
// delete from state
let newState = foodList.slice();
newState.splice(index, 1);
setFoodList(newState);
}, 500);
}
基本上有一个列表,用户可以在每个列表条目上单击删除。如果您在一个条目上单击删除,它会动画化,然后在删除时消失。到目前为止,一切正常。 但是如果我非常快地点击删除两个条目,只有第二个被删除。
假设我们有 4 个条目 (0,1,2,3),我在 1 和 2 上单击删除,那么列表仍然有 0,1,3。所有条目都是动画的,并被称为销毁。
我尝试使用 promises 无济于事。我该如何解决这个问题?
如您所述,如果您非常快速地单击删除,则只有第二个被删除,原因是索引变量在超时触发之前被覆盖,这就是为什么第二个是唯一一个被删除的原因删除。
您可以立即删除该项目,然后保留超时,以便在动画完成后仅重置列表。或者,为过渡添加一个侦听器已结束以从列表中删除该项目,而不是等待超时。如果你想听过渡结束,而许多答案有点旧,你可能想回顾
How do I normalize CSS3 Transition functions across browsers?
万一有人遇到这个问题,这就是我最后做的事情(谢谢,@Lynyrd!)
// transition names (found in Modernizr)
let transEndEventNames = {
'WebkitTransition': 'webkitTransitionEnd', // Saf 6, Android Browser
'MozTransition': 'transitionend', // only for FF < 15
'transition': 'transitionend', // IE10, Opera, Chrome, FF 15+, Saf 7+
};
我使用 React,所以我在 useEffect 中添加了事件侦听器,以便在再次获取 foodList 等时自动添加它们。
// add & remove event listeners for transition end
useEffect(() => {
foodList.forEach((item, index) => {
if (!document.getElementById(index)) return;
document
.getElementById(index)
.addEventListener(
transEndEventNames.WebkitTransition,
handleTransitionEnd
);
document
.getElementById(index)
.addEventListener(
transEndEventNames.MozTransition,
handleTransitionEnd
);
document
.getElementById(index)
.addEventListener(transEndEventNames.transition, handleTransitionEnd);
});
return () => {
foodList.forEach((item, index) => {
if (!document.getElementById(index)) return;
document
.getElementById(index)
.removeEventListener(
transEndEventNames.WebkitTransition,
handleTransitionEnd
);
document
.getElementById(index)
.removeEventListener(
transEndEventNames.MozTransition,
handleTransitionEnd
);
document
.getElementById(index)
.removeEventListener(
transEndEventNames.transition,
handleTransitionEnd
);
});
};
}, [foodList]);
我决定不尝试根据当前浏览器只添加一个事件侦听器,但这也是可能的。 请记住,每次转换都会触发此侦听器。结果,当有人将鼠标悬停在其中的一个元素上时(这触发了工具提示),我的 table 条目被删除了。所以不要忘记过滤:
function handleTransitionEnd(event) {
// only delete element for transform transition
if (!(event.propertyName === 'transform')) return;
let newState = foodList.slice();
newState.splice(event.target.id, 1);
setFoodList(newState);
}
在我的例子中,这很简单,因为我在元素上只有一个 "transform" 过渡。另外,我不知道 eventListeners 的数量是否会成为较大 tables 的性能问题。 现在根本不需要 setTimeout() 函数,在 deleteFood() 中,我现在只启动动画并从我的数据库中删除该项目:
function DeleteFood(index) {
// animate
document.getElementById(index).style.transform = 'translate(500px, 0)';
// delete from airtable
foodList[index].destroy();
}