JavaScript嘗試更新DOM之前運行的所有內嵌代碼,因爲後者是緩慢的。整個循環在頁面更新一次之前運行。
我們可以強制頁面更新:
for(var i =0; i<100000; i++){
notice.innerHTML = i;
notice.getBoundingClientRect(); // Force DOM update to get latest size
console.log(i);
}
然而,當DOM被更新它仍然徑直回到JS繼續運行循環 - 這是更新的速度比你可以看到和仍然似乎掛起。
你需要做的是暫停JS執行,以便頁面有機會更新。
我們可以異步JS功能做到這一點 - 這是結束當前JS塊但排隊的回調函數後火(在這種情況下,用戶看到後話):
var notice = document.getElementById("esperanto-notice");
var i = 0;
// Function to write the next value, increment, and queue up the next timeout
var nextFunc = function() {
console.log(i);
notice.innerHTML = i++;
if (i < 100000)
setTimeout(nextFunc, 16); // 16ms is 60FPS
else
console.log('done');
}
// Start it off
nextFunc();
<label id="esperanto-notice"></label>
現在整個JS運行並且nextFunc
執行一次。它也會在16ms後將它排隊等待再次啓動,但在此之前它會讓瀏覽器更新頁面。
每次nextFunc
都會觸發它使用setTimeout
來排隊下一次執行,然後頁面有一幀要更新(所以用戶看到它),然後它再次觸發。
現代瀏覽器提供的功能尤其是等待下一幀:requestAnimationFrame
:
var notice = document.getElementById("esperanto-notice");
var i = 0;
// Function to write the next value, increment, and queue up the next timeout
var nextFunc = function() {
console.log(i);
notice.innerHTML = i++;
if (i < 100000)
// Request the next visible frame to continue
requestAnimationFrame(nextFunc);
else
console.log('done');
}
// Start it off
nextFunc();
<label id="esperanto-notice"></label>
這是最好的辦法,除非你需要支持IE瀏覽器的舊版本(< = 9 ),因爲可以處理任何幀的持續時間(如果你有很多的麻煩,則可以有setTimeout
問題)。
最後,這是新的語言關鍵字async
和await
可以使您的代碼更容易。您可以保持循環並抽象等待DOM更新。接下來的這個片段只能運行在現代瀏覽器Chrome和FX(但可以使用巴貝爾或打字稿支持IE):
(async function() {
var notice = document.getElementById("esperanto-notice");
for (var i = 0; i < 100000; i++) {
console.log(i);
notice.innerHTML = i;
// Pass back to the DOM until the next frame
await new Promise(r => requestAnimationFrame(r));
}
console.log('done');
})();
<label id="esperanto-notice"></label>
哇。這是一個非常全面的答案。它非常適合我的需求。非常感謝你! – user3649135