setTimeout() 中的代码在多次调用时不会被执行

我有以下功能:


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。所有条目都是动画的,并被称为销毁。


我尝试使用承诺无济于事。我怎样才能解决这个问题?


心有法竹
浏览 307回答 2
2回答

跃然一笑

正如您所指出的,如果您很快单击删除,则只有第二个被删除,原因是索引变量在超时触发之前被覆盖,这就是为什么第二个是唯一被删除的原因。您可以立即删除该项目,然后在动画完成后保留超时以重置列表。或者,为过渡已结束添加侦听器以从列表中删除项目,而不是等待超时。

慕斯王

万一有人遇到这个,这就是我最终做的事情(谢谢@Lynyrd!)// transition names (found in Modernizr)let transEndEventNames = {&nbsp; 'WebkitTransition': 'webkitTransitionEnd', // Saf 6, Android Browser&nbsp; 'MozTransition': 'transitionend', // only for FF < 15&nbsp; 'transition': 'transitionend', // IE10, Opera, Chrome, FF 15+, Saf 7+};我使用 React,所以我在 useEffect 中添加了事件侦听器,以便在再次获取 foodList 等时自动添加它们。// add & remove event listeners for transition enduseEffect(() => {&nbsp; foodList.forEach((item, index) => {&nbsp; &nbsp; if (!document.getElementById(index)) return;&nbsp; &nbsp; document&nbsp; &nbsp; &nbsp; .getElementById(index)&nbsp; &nbsp; &nbsp; .addEventListener(&nbsp; &nbsp; &nbsp; &nbsp; transEndEventNames.WebkitTransition,&nbsp; &nbsp; &nbsp; &nbsp; handleTransitionEnd&nbsp; &nbsp; &nbsp; );&nbsp; &nbsp; document&nbsp; &nbsp; &nbsp; .getElementById(index)&nbsp; &nbsp; &nbsp; .addEventListener(&nbsp; &nbsp; &nbsp; &nbsp; transEndEventNames.MozTransition,&nbsp; &nbsp; &nbsp; &nbsp; handleTransitionEnd&nbsp; &nbsp; &nbsp; );&nbsp; &nbsp; document&nbsp; &nbsp; &nbsp; .getElementById(index)&nbsp; &nbsp; &nbsp; .addEventListener(transEndEventNames.transition, handleTransitionEnd);&nbsp; });&nbsp; return () => {&nbsp; &nbsp; foodList.forEach((item, index) => {&nbsp; &nbsp; &nbsp; if (!document.getElementById(index)) return;&nbsp; &nbsp; &nbsp; document&nbsp; &nbsp; &nbsp; &nbsp; .getElementById(index)&nbsp; &nbsp; &nbsp; &nbsp; .removeEventListener(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; transEndEventNames.WebkitTransition,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; handleTransitionEnd&nbsp; &nbsp; &nbsp; &nbsp; );&nbsp; &nbsp; &nbsp; document&nbsp; &nbsp; &nbsp; &nbsp; .getElementById(index)&nbsp; &nbsp; &nbsp; &nbsp; .removeEventListener(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; transEndEventNames.MozTransition,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; handleTransitionEnd&nbsp; &nbsp; &nbsp; &nbsp; );&nbsp; &nbsp; &nbsp; document&nbsp; &nbsp; &nbsp; &nbsp; .getElementById(index)&nbsp; &nbsp; &nbsp; &nbsp; .removeEventListener(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; transEndEventNames.transition,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; handleTransitionEnd&nbsp; &nbsp; &nbsp; &nbsp; );&nbsp; &nbsp; });&nbsp; };}, [foodList]);我决定不尝试根据当前浏览器仅添加一个事件侦听器,但这也是可能的。请记住,每次转换都会触发此侦听器。结果,当有人将鼠标悬停在其中的元素上时(触发了工具提示),我的表格条目被删除。所以不要忘记过滤:function handleTransitionEnd(event) {&nbsp; // only delete element for transform transition&nbsp; if (!(event.propertyName === 'transform')) return;&nbsp; let newState = foodList.slice();&nbsp; newState.splice(event.target.id, 1);&nbsp; setFoodList(newState);}就我而言,这很容易,因为我在元素上只有一个“变换”过渡。另外,我不知道 eventListener 的数量是否会成为较大表的性能问题。现在根本不需要 setTimeout() 函数,在 deleteFood() 中,我现在只启动动画并从数据库中删除项目:function DeleteFood(index) {&nbsp; // animate&nbsp; document.getElementById(index).style.transform = 'translate(500px, 0)';&nbsp; // delete from airtable&nbsp; foodList[index].destroy();}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

JavaScript