#javascript #html #css #optimization #layout
Вопрос:
Я хочу, чтобы страница загружалась быстро, но при этом пользователь не видел задержки перед отображением содержимого при прокрутке страницы вниз.
У меня есть большая html — страница со множеством <dl>
элементов.
Чтобы ускорить загрузку страницы, я установил content-visibility: auto
css. Видишь https://web.dev/content-visibility/
dl {
content-visibility: auto;
contain-intrinsic-size: 1000px;
}
Из-за сложности содержимого <dl>
s возникает заметная задержка, когда пользователь прокручивает во время <dl>
отображения s по мере их появления в окне просмотра.
Таким образом, вскоре после загрузки страницы я хочу <dl>
сразу же отобразить все закадровые изображения (до того, как пользователь перейдет к ним), но таким образом, чтобы это не блокировало основной поток, а прокрутка оставалась отзывчивой.
Итак, я хочу установить content-visibility: visible
значение <dl>
s, начиная с верхнего, и не блокировать основной поток (скажем, более 50 мс). Итак, возможно, разрешить взаимодействие с пользователем после рендеринга каждого <dl>
из них .
Итак, мне нужна версия ниже, которая не блокирует основной поток:
document.querySelectorAll('dl').forEach(function(dlElement, currentIndex, listObj) { dlElement.style['content-visibility'] = 'visible' });
Мой вариант использования: Моя страница состоит из заметок по математике, которые я хочу разместить на одной странице, чтобы уменьшить трения. Я использую katex, который (на данный момент, прежде чем мы сможем использовать mathml в chrome) создает очень большой и сложный html, который даже на стороне сервера по-прежнему требует много времени для верстки и визуализации в браузере.
Комментарии:
1. Похоже, что видимость контента не поддерживается в Firefox или Safari-это имеет значение или вам нужно поддерживать только Edge/Chrome?
2. На данный момент я рад нацелиться на Edge/Chrome. При необходимости я могу передать mathml в Firefox или Safari.
3. Я обнаружил, что когда
dlElement.clientHeight != 1000px
«что-то» явно произошло, но я не уверен, что это «что-то» — стиль, макет или краска. Тем не менее, я рад попробовать использовать это в качестве условия, прежде чем переходить к установке следующего<dl>
в видимое. Как мне это сделать? Я пробовал различные комбинации setTimeout и requestAnimationFrame, но, похоже, я все еще блокирую основной поток.4. Я собираюсь попробовать это, но это может занять у меня некоторое время: 75% на странице: Использование requestIdleCallback для внесения изменений в DOM developers.google.com/web/updates/2015/08/…
Ответ №1:
Вместо того, чтобы оставлять это без ответа, позвольте мне вставить мой (незараженный) код, который я тестировал в течение последних нескольких недель.
// --------- Shim requestIdleCallback if not supported in browser ----------------------------------
window.requestIdleCallback =
window.requestIdleCallback ||
function (cb) {
var start = Date.now();
return setTimeout(function () {
cb({
didTimeout: false,
timeRemaining: function () {
return Math.max(0, 50 - (Date.now() - start));
}
});
}, 1);
}
window.cancelIdleCallback =
window.cancelIdleCallback ||
function (id) {
clearTimeout(id);
}
// Global
let isRequestIdleCallbackScheduled = false;
let nodeToRemove = null;
let isVisualUpdateScheduled = false;
let totalDlElementsLeftToShow = 0;
function startIfNeeded() {
totalDlElementsLeftToShow = document.querySelectorAll('dl:not([style*="content-visibility: visible;"]), .ra:not([style*="content-visibility: visible;"]').length;
if (totalDlElementsLeftToShow > 0) {
console.log('Not a mobile - Let's make things visible when we are next idle');
scheduleVisibilityChanges();
}
else {
console.log('Apparently, all visible');
}
}
function scheduleVisibilityChanges() {
// Only schedule the rIC if one has not already been set.
if (isRequestIdleCallbackScheduled) {
//console.log('returning because idle callback scheduled');
startIfNeeded();
}
isRequestIdleCallbackScheduled = true;
//console.log('scheduling visibility changes when next idle or in 30 seconds at the latest');
requestIdleCallback(processHiddenElements, { timeout: 30000 });
}
function processHiddenElements(deadline) {
// Since our operation takes a while, we only want to go ahead if we have at least 50ms.
while (deadline.timeRemaining() > 49 amp;amp; totalDlElementsLeftToShow > 0) {
// console.log('time remaining is ', deadline.timeRemaining(), '- scheduling next visual update');
// Don't set content-visibility immediately wait for the next
// requestAnimationFrame callback.
scheduleVisualUpdateIfNeeded();
}
// console.log('Deadline reached, will check again the next time the user is idle if there are more events still to send');
if (totalDlElementsLeftToShow > 0) {
requestIdleCallback(processHiddenElements, { timeout: 30000 });
}
}
function scheduleVisualUpdateIfNeeded() {
if (isVisualUpdateScheduled) {
// console.log('returning - visual update already scheduled')
return;
};
isVisualUpdateScheduled = true;
// console.log('requesting animation frame');
requestAnimationFrame(setContentToVisible);
}
function setContentToVisible() {
// console.log('changing visibility of element ');
let completeHiddenNodeList = document.querySelectorAll('dl:not([style*="content-visibility: visible;"]), .ra:not([style*="content-visibility: visible;"]');
// We chunk the layout changes
let i;
let numberToChunk = 20;
if (completeHiddenNodeList.length < 20) {
numberToChunk = completeHiddenNodeList.length
}
for (i = 0; i < numberToChunk; i) {
completeHiddenNodeList[i].style.contentVisibility = 'visible';
}
isVisualUpdateScheduled = false;
isRequestIdleCallbackScheduled = false;
totalDlElementsLeftToShow = totalDlElementsLeftToShow - numberToChunk;
}
if (!navigator.userAgentData.mobile) {
startIfNeeded();
}