首页 > 解决方案 > 使用超时淡出多个元素仅淡出列表中的最后一个

问题描述

我试图在几秒钟后淡出消息容器。通常只有一个,而且效果很好,但是当有 2 个或更多时,似乎只有最后一个被处理。

代码:

function fadeOut(elementToFade) {
    var element = document.getElementById(elementToFade);
    console.log(`fo element = ${element}, ${element.id}`)

    element.style.opacity = (parseFloat(element.style.opacity) - 0.1).toString();
    if (parseFloat(element.style.opacity) <= 0.0) {
        element.style.display = "none";
    } else {
        setTimeout("fadeOut(\"" + elementToFade + "\")", 150);
        // setTimeout("fadeOut(\"" + element.id + "\")", 150);
    }
};

var message_elements_nodelist = document.querySelectorAll("div[id^='message_container']");
var pauseBeforeFadeout = 5000;

for (var i = 0; i < message_elements_nodelist.length; i++) {
    var el = document.getElementById(message_elements_nodelist[i].id)
    console.log(`element = ${el}, ${el.id}`)
    setTimeout(function () {
        el.style.opacity = "1.0";
        fadeOut(el.id);
    }, pauseBeforeFadeout);
};

控制台日志显示:

element = [object HTMLDivElement], message_container-0
element = [object HTMLDivElement], message_container-1
2fo element = [object HTMLDivElement], message_container-1
10fo element = [object HTMLDivElement], message_container-1

所以代码找到了这两个元素,但是超时似乎只应用于列表中的最后一个元素,我无法弄清楚为什么会这样。

编辑

放入Promise现在看起来像这样:

const message_elements_nodelist = document.querySelectorAll("div[id^='message_container']")
const pauseBeforeFadeout = 5000

async function fadeOut(elementToFade) {
    const element = document.getElementById(elementToFade);
    console.log(`fo element = ${element}, ${element.id}`)

    element.style.opacity = (parseFloat(element.style.opacity) - 0.1).toString()
    if (parseFloat(element.style.opacity) <= 0.0) {
        element.style.display = "none"
    } else {
        setTimeout("fadeOut(\"" + elementToFade + "\")", 150)
    }
}

async function fadeOutAllMessages() {
    for (let i = 0; i < message_elements_nodelist.length; i++) {
        const el = document.getElementById(message_elements_nodelist[i].id)
        console.log(`element = ${el}, ${el.id}`)
        await new Promise(resolve => setTimeout(() => {
            el.style.opacity = "1.0"
            fadeOut(el.id)    
        }, pauseBeforeFadeout))
    }
}

fadeOutAllMessages()

不幸的是,现在第一条消息被清除了,但第二条没有,console.log消息看起来像这样:

element = [object HTMLDivElement], message_container-0
fo element = [object HTMLDivElement], message_container-0
9 fo element = [object HTMLDivElement], message_container-0

最后 9 条消息在第一条消息后 5 秒出现,所以一个await正在工作,然后它似乎只将它们与第一个循环实例捆绑在一起。

标签: javascriptdomsettimeout

解决方案


这是因为在 for 循环中使用了异步方法(在这种情况下为 setTimeout)。在异步方法执行之前,变量i已经递增(循环不会等待内部的异步代码),因此 setTimeout 将被执行多次,并且变量i始终为所有回调的最后一个值。

考虑async/await在循环内使用。

async function yourFunction() {
    for (let i = 0; i < 10; i++) {
        // wait for the promise before incrementing i and advancing the loop
        await new Promise(resolve => resolve(setTimeout(() => console.log(i), 
        1000)))
    }
}

另一种解决方案是调用在循环之外创建的函数。

const setDelay = (i) => {
    setTimeout(() => {
      console.log(i);
    }, 1000);
  }

for (let i = 0; i < 5; i++) {
    setDelay(i);
}

您可以在本主题中找到更多示例: javascript for 循环中的异步进程


推荐阅读