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