Задать вопрос
@Faveplus

Оптимизация страницы с 50+ видео-iframe — нативного lazy loading недостаточно. Что посоветуете?

Я работаю над сайтом-каталогом, где на некоторых страницах может быть до 50 видео-iframe. Сейчас я использую стандартный подход:

<iframe src="url/ifr/4854766?muted=1&hd=0"                            
title="Video Title"                            
loading="lazy"                            
class="w-full h-full"                            
allowfullscreen></iframe>

Хотя атрибут loading="lazy" помогает, такое количество iframe всё равно создает значительную нагрузку (потребление памяти и лишние сетевые запросы по мере скролла).

Можете ли вы порекомендовать какие-то конкретные приемы с использованием Intersection Observer, чтобы отложить установку src для iframe до нужного момента?

Как вы справляетесь с таким количеством элементов, не нанося вреда SEO и UX?

Ищу любые библиотеки, паттерны JS или CSS-фишки (например, content-visibility: auto), которые помогут разгрузить основной поток (main thread) браузера.
  • Вопрос задан
  • 288 просмотров
Подписаться 2 Средний 1 комментарий
Помогут разобраться в теме Все курсы
  • Нетология
    Веб-разработчик с нуля: профессия с выбором специализации
    14 месяцев
    Далее
  • Яндекс Практикум
    Фронтенд-разработчик расширенный
    13 месяцев
    Далее
  • Skillbox
    JavaScript
    3 месяца
    Далее
Пригласить эксперта
Ответы на вопрос 2
opium
@opium
Просто люблю качественно работать
Для 50+ embed'ов хорошо работает facade: рендеришь превью-картинку с кнопкой play, а iframe создаёшь только по клику. Если часть iframe должна грузиться без клика — выноси URL в data-src и подставляй через IntersectionObserver:

const observer = new IntersectionObserver((entries) => {
  entries.forEach(e => {
    if (e.isIntersecting) {
      e.target.src = e.target.dataset.src;
      observer.unobserve(e.target);
    }
  });
}, { rootMargin: '200px' });
document.querySelectorAll('iframe[data-src]').forEach(el => observer.observe(el));


Дополнительно на обёртки можно повесить content-visibility: auto с contain-intrinsic-size — браузер будет пропускать layout/paint за пределами экрана.
Ответ написан
Комментировать
PiSaiK
@PiSaiK
IT куратор
Нативный loading="lazy" действительно слабоват — браузер всё равно создаёт iframe-контексты заранее. Вот проверенные решения:
1. Facade Pattern — главный приём
Вместо iframe сразу показываем лёгкую «заглушку» с превью. Iframe создаётся только по клику:
class VideoFacade extends HTMLElement {
  connectedCallback() {
    const videoId = this.dataset.videoId;
    const title = this.dataset.title || 'Видео';
    
    this.innerHTML = `
      <div class="video-facade" role="button" aria-label="Воспроизвести ${title}">
        <img src="/thumbs/${videoId}.jpg" 
             alt="${title}" 
             loading="lazy"
             decoding="async">
        <svg class="play-btn" viewBox="0 0 68 48">
          <path d="M66.5 7.7c-.8-2.9-2.5-5.4-5.4-6.2C55.8.1 34 0 34 0S12.2.1 6.9 1.5c-2.9.8-4.6 3.3-5.4 6.2C.1 13 0 24 0 24s.1 11 1.5 16.3c.8 2.9 2.5 5.4 5.4 6.2C12.2 47.9 34 48 34 48s21.8-.1 27.1-1.5c2.9-.8 4.6-3.3 5.4-6.2C67.9 35 68 24 68 24s-.1-11-1.5-16.3z" fill="#212121" fill-opacity=".8"/>
          <path d="M45 24L27 14v20" fill="#fff"/>
        </svg>
      </div>
    `;
    
    this.addEventListener('click', () => this.activate(), { once: true });
  }
  
  activate() {
    const iframe = document.createElement('iframe');
    iframe.src = `https://player.example.com/ifr/${this.dataset.videoId}?autoplay=1&muted=1`;
    iframe.allow = 'autoplay; fullscreen';
    iframe.allowFullscreen = true;
    this.replaceChildren(iframe);
  }
}
customElements.define('video-facade', VideoFacade);

<!-- Было: тяжёлый iframe -->
<!-- Стало: ~2KB вместо ~500KB на видео -->
<video-facade 
  data-video-id="4854766" 
  data-title="Название ролика">
</video-facade>

2. Intersection Observer + виртуализация превью
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const facade = entry.target;
      const img = facade.querySelector('img[data-src]');
      if (img) {
        img.src = img.dataset.src;
        img.removeAttribute('data-src');
      }
      observer.unobserve(facade);
    }
  });
}, {
  rootMargin: '200px 0px', // предзагрузка за 200px до viewport
  threshold: 0
});

document.querySelectorAll('video-facade').forEach(el => observer.observe(el));


3. CSS content-visibility — разгрузка рендеринга
.video-grid {
  /* Браузер пропускает layout/paint для невидимых элементов */
  content-visibility: auto;
  contain-intrinsic-size: 0 300px; /* примерная высота блока */
}

.video-facade {
  contain: layout style paint;
  aspect-ratio: 16/9;
}


4. Готовые решения
lite-youtube-embed, 1.3KB, Web Component для YouTube
lite-vimeo-embed, 1.2KB, То же для Vimeo
lazysizes + unveilhooks,3KB, Универсальный lazy-load
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

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