Как мгновенно изменить элемент DOM?

Я знаю, что это может быть глупый вопрос, но он сводит меня с ума. Я пытаюсь изменить innerHTML элемента DOM, но он не меняется до конца выполнения функции. Например:

function test(){
  let testEl = document.getElementById('testEl')
 
  for (let i = 0; i < 5; i++) 
  {
    testEl.innerHTML = 'Count: ' + i; 
    alert(i);
  }

}

Даже если я поставил алерт в цикле, текст элемента не изменится до конца выполнения функции. Как можно применить изменение мгновенно (например, я имею в виду во время цикла)?

Вам нужно сделать анимацию с таймаутами

Pauline Nemchak 21.12.2020 22:52

Потому что поток блокируется. Хотя в данном конкретном случае я не вижу, как это будет иметь значение. Код завершит 5 циклов быстрее, чем человеческий глаз сможет даже распознать 5 отличий. (Если бы дисплей мог даже отчетливо отображать все 5 из них так быстро.)

David 21.12.2020 22:53

@David В каждом повторении есть предупреждение (), которое останавливает выполнение. Таким образом, пользователь сможет увидеть изменения. Дело в том, что элемент DOM не меняется. Если вы добавите console.info('Count: ' + i), вы сможете видеть числа в консоли (одно за другим после предупреждающего сообщения), но все равно не в элементе DOM.

MyWherany 21.12.2020 23:01
youtube.com/watch?v=4xsvn6VUTwQ
Jason Owens 21.12.2020 23:02

Вам нужно немного больше узнать об одно- и многопоточной обработке. JavaScript — это язык однопоточной обработки. Но он выполняется в многопоточном клиенте (браузере). JavaScript может запросить alert, но это браузер просит операционную систему сделать это в отдельном потоке, и, поскольку оповещение является «модальным» диалогом, он блокирует выполнение всех других браузеров, поэтому вы не увидите обновление страницы во время оповещение присутствует.

Scott Marcus 21.12.2020 23:12

На самом деле предупреждение просто для примера. Что я делаю, так это сначала меняю DOM (например, помещаю текст «загрузка»), затем вызываю php-файл для получения некоторых данных, а затем снова меняю DOM на «Готово». Единственное, что я вижу, это «Готово» в конце, даже если обработка занимает 4-5 секунд. Я предполагаю, что это связано с sync/asych, поэтому ваши ответы полезны!

MyWherany 21.12.2020 23:20

Просто нет гарантии, что браузер сможет или будет обновлять пользовательский интерфейс между предупреждениями таким образом. Фаерфокс умеет, хром нет. Однако вы можете запрограммировать его так, чтобы вы могли обновлять элемент между предупреждениями для всех браузеров.

MinusFour 21.12.2020 23:22

Это потому, что процесс идет так быстро, это делается почти сразу. Ответ Маджеда ниже - это правильный способ «замедлить процесс», чтобы вы могли видеть происходящие изменения.

Scott Marcus 21.12.2020 23:26

@ СкоттМаркус Я так не думаю. Я установил таймер каждые 0,5 секунды (после загрузки страницы). У меня есть кнопка, которая запускает выполнение скрипта. Таймер зависает на некоторое время, когда скрипт выполняется. Я думаю, что это связано с открытием XMLHttpRequest, которое я делаю. Если я использую консоль для регистрации того, что хочу, она отлично работает, но это не относится к элементу DOM.

MyWherany 21.12.2020 23:35

Мне жаль, что вы так не думаете, но вы ошибаетесь. Я занимаюсь этим и учу этому с тех пор, как был изобретен JavaScript. Если ваш таймер зависает, это потому, что вы неправильно его реализуете. XMLHttpRequest по умолчанию является асинхронным, поэтому он не блокирует ваш код JavaScript. Как я уже сказал, вам нужно узнать, как работает асинхронная обработка в однопоточном JavaScript. Этот однопоток всегда имеет приоритет над любым асинхронным кодом, который он мог породить (например, таймеры). Таким образом, нельзя рассчитывать на то, что таймеры будут запускаться точно в указанное время.

Scott Marcus 21.12.2020 23:36

Я очень рад, что вы преподаете JS с 1995 года! XMLHttpRequest по умолчанию является асинхронным, но это изменится, если вы установите для асинхронности значение false (это то, что я сделал). Итак, я должен установить для async значение true и управлять продолжением скрипта из функции .onload запроса. Вот и все.

MyWherany 21.12.2020 23:47

Установка async на false сводит на нет всю цель XHR, поэтому вы можете просто выполнить встроенную функцию — вот и все. Вы не делаете то, что пытаетесь сделать правильно, и мы пытаемся рассказать вам, как это сделать, но вы, похоже, хотите просто поспорить.

Scott Marcus 21.12.2020 23:56

@ScottMarcus «Это потому, что процесс идет так быстро, что он выполняется почти сразу. Ответ Маджеда ниже — это правильный способ «замедлить процесс», чтобы вы могли видеть происходящие изменения». Это был ваш ответ, и я отвечаю: «Я так не думаю», потому что это не было проблемой.

MyWherany 22.12.2020 00:21
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
13
83
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вы можете обновлять число каждый период времени, используя setInterval:

function test(){
  let testEl = document.getElementById('testEl');
  let i = 0;
  const interval = setInterval(function(){ 
    testEl.innerHTML = `Count: ${i++}`;
    if (i === 5)
      clearInterval(interval);
  }, 1000);
}

test();
<p id = "testEl"></p>

Решение Interval не помогает в моей ситуации. В вашем первом решении просто поместите предупреждение в цикл, чтобы увидеть, что именно я имею в виду: `function test(){ let testEl = document.getElementById('testEl') for (let i = 0; i < 5; i++) { testEl .innerHTML += Count: ${i}<br>; предупреждение('ПАУЗА'); // testEL не изменился и не изменится до конца функции } } test(); `

MyWherany 21.12.2020 23:08

@MyWherany the for-loop работает быстро, и это нормально видеть только конечный результат. Вот почему я предложил добавить содержимое, чтобы показать все итерации, или использовать интервал, чтобы показать медленное изменение переменной.

Majed Badawi 21.12.2020 23:12
Ответ принят как подходящий

JavaScript работает в однопоточной среде. Это означает, что в любой момент времени может выполняться только один контекст выполнения. Асинхронный код выполняется за пределами среды выполнения JavaScript (в данном случае за счет собственной обработки браузера), и только когда поток JavaScript бездействует, могут быть выполнены результаты асинхронного запроса (т. е. обратные вызовы).

Ниже приведен пример, который обновляет элемент DOM примерно каждую секунду, создавая часы. Однако, если вы нажмете кнопку, он попросит браузер отобразить alert, который обрабатывается вне среды выполнения JavaScript и является блокирующим элементом пользовательского интерфейса, поэтому часы остановятся. Как только вы очистите предупреждение, вы увидите, что временной скачок будет примерно текущим.

Как вы увидите, асинхронный вызов API window.setInterval() позволяет функции выполняться многократно, время от времени и, следовательно, не непрерывно. Это заменяет необходимость в цикле, который выполняется полностью каждый раз, когда к нему обращаются. Из-за этого вы можете видеть обновления на веб-странице вместо последнего значения вашего цикла.

Подробности смотрите в комментариях:

const clock = document.querySelector("span");

// setInterval is not JavaScript. It's a call to a browser
// API asking the JS runtime to run the supplied function every
// 900 milliseconds, but that's just a request. After 900 
// milliseconds, the browser will place the function on the
// JavaScript event queue and only when the JavaScript thread
// is idle will anything on the queue be executed. This is why
// the 900 milliseconds is not a guarantee - - it's just the 
// minimum amount of time you'll have to wait for the function
// to run, but it could be longer if what's already running
// on the JavaScript thread takes longer than 900 milliseconds
// to complete.
window.setInterval(function(){
  // Update the DOM
  clock.textContent = new Date().toLocaleTimeString();
}, 900);

document.querySelector("button").addEventListener("click", function(){
  // An alert is also not JavaScript, but another browser API that is executed
  // by the browser, not JavaScript. However, it is a blocking (modal) UI element.
  // The rest of the browser interface (including the web page) cannot update
  // while the alert is present. As soon as the alert is cleared, the UI will update.
  window.alert("I'm a UI blocking construct rendered by the browser, not JavaScript");
});
<div>Current time is: <span></span></div>
<button>Click for alert</button>

Другой способ добиться этого - использовать async - Promise вот так

async function test() {
    let testEl = document.getElementById('testEl')

    for (let i = 0; i < 5; ++i) {
        testEl.innerHTML = 'Count: ' + i;

        await new Promise((resolve, _) => {
            setTimeout(function() {
                resolve("");
            }, 1000 /* your preferred delay here */);
        });
    }
}

Другие вопросы по теме

Похожие вопросы

Обратная гравитация/антигравитация? Какие элементы алгоритма гравитационной силы мне нужно изменить, чтобы обратить его вспять?
Возможно ли, что веб-приложение Google Script App может заполнить текстовое поле моей веб-страницы?
Использование Jquery для ограничения выбора флажка с помощью родительских селекторов html
Сбросьте раскрывающийся список, выбрав отключенный параметр по умолчанию в React
Файл JavaScript со строкой запроса для динамического создания холста в виде изображения png в img src
Вызов индикатора прогресса
Разбивка на страницы в Firestore с помощью react-redux
Состояние реакции useState обновляется, когда используется свойство ключа, но в противном случае для обновления требуется useEffect или аналогичный метод
Сохранение согласованных отношений parentId/id с помощью функции flatten
Максимально увеличьте высоту таблицы и включите полосу прокрутки при достижении в JavaScript/React.js