Итерация Dom, вызывающая половину рендеринга webview

#javascript #android #dom #webview

#javascript #Android #dom #веб-просмотр

Вопрос:

я использую Android для открытия локального веб-файла, а затем повторяю dom и применяю некоторые изменения, НО во время итерации webview останавливается, чтобы отобразить страницу в какой-то случайной части, посмотрите gif:

то, что я уже сделал

 var elements = document.querySelectorAll("P");
for(var i = 0; i < elements.length;i  ) {
//just iterating at the gif im chaging 
//but i tried without changing, just iterating and the result still the same 
} 
 

и теперь, чтобы избежать проблем с рендерингом при каждом изменении dom, я создал DocumentFragment и изменил dom в нем (или просто повторил), а затем вернул его html в основной документ

 var x = document.getElementById('contentRoot'); //getting the element root from the DOCUMENT (not the fragment)
      var frag = document.createDocumentFragment(); //creating fragment
      var contentRoot = document.createElement("DIV"); // creating a new contentRoot html element
      contentRoot.id = 'contentRoot';
      contentRoot.innerHTML = x.innerHTML;
      frag.appendChild( contentRoot );
    var elements = frag.querySelectorAll("P");
    for(var i = 0; i < elements.length;i  ) {
    //just iterating at the gif im chaging 
    //but i tried without changing, just iterating and the result still the same 
    } 

document.body.innerHTML = frag.getElementById('contentRoot').innerHTML; //returning the edited html from the fragment so the webview will render/reflow just once everything not for each change
 

есть ли способ сохранить нормальный рендеринг, не «обрезая его»?

PS: я использую функцию для итерации window.onload, поэтому она запускается только ПОСЛЕ того, как весь dom загружен в браузер, почему он продолжает его сокращать? и просто рендерить снова в конце итерации?

введите описание изображения здесь

Ответ №1:

Проблема с JavaScript заключается в том, что во время работы JavaScript вся страница замораживается и ничего не отображает. Если у вас запущен очень большой цикл for, это объясняет вашу проблему.

Большинство браузеров для настольных компьютеров будут отображать прокрутку одновременно с отображением страницы (держу пари, что если вы запустите эту страницу в браузере для настольных компьютеров, она даже не позволит вам прокручивать).

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

Бьюсь об заклад, что если бы вы попытались увеличить масштаб страницы во время выполнения функции, это выглядело бы очень размытым, потому что рендеринг не выполняется.

Один из способов исправить эту проблему — использовать таймеры или API requestAnimationFrame.

requestAnimationFrame поддерживается практически всеми современными браузерами. Что он делает, так это откладывает код до тех пор, пока не будет отрисован следующий кадр, и обычно используется для создания плавной анимации.

Используя этот API, я создал следующий код…

 var elements = document.querySelectorAll("P");
var i = 0;
var j = elements.length;
function iterate(){
    // this acts as the body of your for loop
    // do stuff with the current selected DOM element here...
    // select dom element with elements[i]

    i  ;
    if(i < j){
        requestAnimationFrame(iterate);
    }
}
iterate();
 

Теперь приведенный выше код запускает итерацию вашего цикла for для каждого кадра анимации. Предполагая, что ваше устройство работает со скоростью 60 кадров в секунду, это составляет максимум 60 итераций в секунду.

Чтобы бороться с этим и сделать его еще быстрее, мы можем requestAnimationFrame(iterate) заменить его альтернативным кодом window.setTimeout(iterate, 0) , который, по сути, сообщает браузеру выполнять итерацию каждую возможную миллисекунду, сохраняя при этом возможность рендеринга страницы. Однако этот метод может снизить частоту кадров. (однако для мобильного браузера, который обрабатывает разделенную прокрутку, как у вас, частота кадров не должна быть проблемой)

Редактировать:

Когда я запустил довольно простой, но длинный цикл в своем настольном браузере, используя описанный выше метод, я достиг скорости 60 кадров в секунду, измеряемой JavaScript, с частотой около 150-200 итераций в секунду. Если вы используете мобильные устройства, ваши результаты, вероятно, будут медленнее.

Альтернативное предложение:

Если вы хотите использовать готовый API для такого рода вещей, есть один, который выглядит довольно круто. Операционная система нашла один под названием Turboid, который позволяет вам легче управлять такого рода циклами. http://turboid.net/artikel/real-loops-in-javascript.php

Комментарии:

1. Привет, хороший ответ MineAndCraft, можете ли вы добавить это в свой ответ, чтобы я мог пометить его как правильный -> turboid.net/artikel/real-loops-in-javascript.php я использую этот turboid, и он решил проблему очень хорошо, и он быстрее, чем requestAnimationFrame (не знает, как на самом деле работает turboid).. таким образом, правильный ответ будет содержать эту «вики», которую вы опубликовали, и то, что я использую, и кто-то тоже может использовать

2. Похоже, что способ работы Turboid — это очень простой способ настройки таймера с некоторыми дополнительными функциями для доступности… Что касается его loop.turbo(x) функции, то лучше всего, возможно, сделать ставку на то, что он выполняет x итерации для каждого интервального интервала… Сам не уверен, но, похоже, этот парень знает, как эти циклы работают довольно хорошо. Я добавлю это туда, чтобы другие, ищущие такие вещи, тоже могли их найти.

Ответ №2:

Возможно, попробуйте дождаться следующего фрейма javascript, это может быть полезно при работе с большим количеством манипуляций с dom :

 setTimeout(function(){
    document.body.innerHTML = frag.getElementById('contentRoot').innerHTML;
}, 1 );
 

Комментарии:

1. попробовал это, немного улучшил, но после большого количества doms это заморозило рендеринг webview