Keď umiestnite niekoľko automaticky prehrávaných videí do animovaného Elementor karuselu (najmä rozloženia ako dvojstĺpcové vertikálne karusely s opačnými smermi posúvania), mobilné zariadenia a tablety často nedokážu zvládnuť takúto záťaž.
Typické príznaky zahŕňajú:
- zamrznutie alebo pád mobilného prehliadača
- výrazné poklesy FPS
- oneskorená reakcia na scroll alebo dotyk
- súčasné načítanie všetkých videí, ktoré zahltí pamäť/CPU
- nekonzistentné správanie autoplay
Dôvodom je, že každý element <video> v karuseli sa pokúša:
- načítať celý zdrojový súbor
- dekódovať súbor
- spustiť autoplay hneď, ako sa objaví v DOM
Na stránke so 6 + 6 videami v neustálom pohybe sa to stáva výkonnostným úzkym hrdlom, najmä na iOS a strednej triede Android zariadení.
Na vyriešenie tohto problému potrebujeme inteligentnejší systém.
Inteligentnejší prístup: Lazy-loading + automatické pozastavenie pomocou Intersection Observer
Nasledujúci skript rieši všetky hlavné problémy tým, že:
- Načítava videá len vtedy, keď sú skutočne viditeľné (50 % vo viewport)
- Pozastavuje a odstraňuje videá, keď opustia viewport
- Znižuje spotrebu RAM odstránením
src, keď nie je potrebný - Predchádza súčasnému dekódovaniu viacerých videí
- Spúšťa sa iba na mobiloch a tabletoch (≤ 1024px)
- Funguje globálne pre všetky Elementor videá (
video.elementor-video)
Výsledkom je:
- plynulé scrollovanie
- žiadne pády prehliadača
- stabilný výkon na mobiloch
- autoplay videí len vtedy, keď je to vhodné
- plynulý chod karuselov aj s mnohými videami
Skript
Nižšie je presný optimalizovaný skript, ktorý môžete použiť:
<script>
document.addEventListener("DOMContentLoaded", function() {
// Run only on Elementor mobile + tablet (<= 1024px)
if (window.innerWidth > 1024) {
return;
}
/* ---------------------------------------
Lazy-load + pause videos outside viewport
(GLOBAL - applies to all Elementor videos)
--------------------------------------- */
var allVideos = document.querySelectorAll("video.elementor-video");
allVideos.forEach(function(video) {
if (!video.getAttribute("data-src")) {
video.setAttribute("data-src", video.getAttribute("src"));
}
video.removeAttribute("src");
video.setAttribute("preload", "none");
video.isIntersecting = false;
});
var loadTimeouts = new Map();
var unloadTimeouts = new Map();
var observerOptions = { root: null, threshold: 0.5 };
var observer = new IntersectionObserver(function(entries) {
entries.forEach(function(entry) {
var video = entry.target;
video.isIntersecting = entry.isIntersecting;
if (entry.isIntersecting) {
// Cancel unload
if (unloadTimeouts.has(video)) {
clearTimeout(unloadTimeouts.get(video));
unloadTimeouts.delete(video);
}
// Schedule load and play
if (!loadTimeouts.has(video)) {
var timer = setTimeout(function() {
if (video.isIntersecting) {
if (!video.getAttribute("src")) {
video.setAttribute("src", video.getAttribute("data-src"));
video.load();
}
video.play().catch(function(error) {
console.error("Error auto-playing video:", error);
});
}
loadTimeouts.delete(video);
}, 300);
loadTimeouts.set(video, timer);
}
} else {
// Cancel load
if (loadTimeouts.has(video)) {
clearTimeout(loadTimeouts.get(video));
loadTimeouts.delete(video);
}
// Schedule pause + unload src
if (!unloadTimeouts.has(video)) {
var timer2 = setTimeout(function() {
if (!video.isIntersecting) {
video.pause();
video.removeAttribute("src");
}
unloadTimeouts.delete(video);
}, 50);
unloadTimeouts.set(video, timer2);
}
}
});
}, observerOptions);
// Observe all videos
allVideos.forEach(function(video) {
observer.observe(video);
});
});
</script>
Rozbor kódu: Ako skript funguje
Prejdime si kľúčové časti skriptu, aby ste ho mohli s istotou upraviť alebo použiť v iných projektoch.
1. Predčasné ukončenie na desktope
if (window.innerWidth > 1024) {
return;
}
Túto optimalizáciu potrebujeme len na mobiloch a tabletoch. Desktopové zariadenia zvyčajne zvládajú viacero videí lepšie a možno budete chcieť, aby sa tam prehrávali automaticky. Čokoľvek širšie ako 1024px jednoducho preskočí celú logiku.
2. Príprava všetkých Elementor videí na lazy-loading
var allVideos = document.querySelectorAll("video.elementor-video");
allVideos.forEach(function(video) {
if (!video.getAttribute("data-src")) {
video.setAttribute("data-src", video.getAttribute("src"));
}
video.removeAttribute("src");
video.setAttribute("preload", "none");
video.isIntersecting = false;
});
Tu robíme nasledovné:
- vyberieme každé
video.elementor-videona stránke - presunieme pôvodný
srcdodata-src - vymažeme
src, aby prehliadač súbor ešte nenačítal - nastavíme
preload="none", aby prehliadač nič nepredbufferoval
Od tohto momentu sú videá efektívne len zástupné prvky, kým nevstúpia do viewport.
3. Sledovanie časovačov načítania/odstránenia pre každé video
var loadTimeouts = new Map();
var unloadTimeouts = new Map();
var observerOptions = { root: null, threshold: 0.5 };
Používame dva objekty Map na uloženie ID setTimeout pre každé video:
loadTimeouts→ oneskorené načítanie a prehrávanie, keď sa video stane viditeľnýmunloadTimeouts→ oneskorené pozastavenie a odstránenie, keď opustí viewport
threshold: 0.5 znamená, že callback sa spustí, keď je 50 % videa viditeľných.
4. IntersectionObserver: rozhodovanie o načítaní alebo odstránení
var observer = new IntersectionObserver(function(entries) {
entries.forEach(function(entry) {
var video = entry.target;
video.isIntersecting = entry.isIntersecting;
if (entry.isIntersecting) {
// ... handle load logic ...
} else {
// ... handle unload logic ...
}
});
}, observerOptions);
IntersectionObserver sleduje každé video a informuje nás, keď:
- vstúpi do viewport (≥ 50 % viditeľné)
- opustí viewport
Namiesto neustáleho manuálneho kontrolovania pozície scrollu nás prehliadač efektívne upozorní.
5. Keď sa video stane viditeľným
if (entry.isIntersecting) {
// Cancel unload
if (unloadTimeouts.has(video)) {
clearTimeout(unloadTimeouts.get(video));
unloadTimeouts.delete(video);
}
// Schedule load and play
if (!loadTimeouts.has(video)) {
var timer = setTimeout(function() {
if (video.isIntersecting) {
if (!video.getAttribute("src")) {
video.setAttribute("src", video.getAttribute("data-src"));
video.load();
}
video.play().catch(function(error) {
console.error("Error auto-playing video:", error);
});
}
loadTimeouts.delete(video);
}, 300);
loadTimeouts.set(video, timer);
}
}
Čo sa tu deje:
- zrušíme akékoľvek čakajúce odstránenie (pre prípad, že používateľ rýchlo scrollne späť)
- naplánujeme oneskorené načítanie a prehrávanie (300ms)
- znova skontrolujeme, či je video stále v zornom poli pred načítaním (vyhneme sa zbytočnej práci)
- obnovíme
srczdata-src, zavoláme.load()a potom.play()
Oneskorenie zabraňuje načítaniu príliš veľkého počtu videí naraz počas rýchleho scrollovania.
6. Keď video opustí viewport
// Cancel load
if (loadTimeouts.has(video)) {
clearTimeout(loadTimeouts.get(video));
loadTimeouts.delete(video);
}
// Schedule pause + unload src
if (!unloadTimeouts.has(video)) {
var timer2 = setTimeout(function() {
if (!video.isIntersecting) {
video.pause();
video.removeAttribute("src");
}
unloadTimeouts.delete(video);
}, 50);
unloadTimeouts.set(video, timer2);
}
Keď video zmizne zo zorného poľa:
- akékoľvek čakajúce načítanie sa zruší (už ho nie je potrebné načítavať)
- naplánujeme veľmi krátky timeout (50ms) na:
pause()videa- opätovné odstránenie jeho
src→ uvoľnenie pamäte a zastavenie bufferovania
Práve tu dochádza k skutočným úsporám RAM a CPU.
7. Spustenie pozorovania všetkých videí
allVideos.forEach(function(video) {
observer.observe(video);
});
Nakoniec sa každé Elementor video pripojí k IntersectionObserver, čím sa celý systém stáva plne automatickým.
Žiadne zmeny widgetov, žiadne extra atribúty. Skript jednoducho funguje nad vaším existujúcim dizajnom.
Prečo tento skript funguje tak dobre
1. Intersection Observer riadi načítanie na základe viditeľnosti
Prehliadač načítava a prehráva videá len vtedy, keď je viditeľných aspoň 50 % elementu.
Ideálne pre pohybujúce sa karusely, kde videá plynulo vchádzajú a odchádzajú zo zorného poľa.
2. Odstránenie atribútu src videa drasticky znižuje spotrebu pamäte
Keď videá nie sú potrebné, skript odstráni ich atribút src.
To znamená:
- žiadne bufferovanie
- žiadne dekódovanie
- žiadna spotreba RAM
- žiadne využívanie GPU
3. Oneskorené načítanie predchádza špičkám CPU
setTimeout(..., 300) zabezpečuje, že prehliadač sa nepokúsi načítať príliš veľa videí naraz.
4. Okamžité odstránenie predchádza lagom
Keď video opustí viewport, pozastavíme ho a odstránime po 50ms.
5. Ľahký, čistý vanilla JavaScript
Žiadne závislosti. Žiadne výkonnostné náklady.
6. Funguje pre VŠETKY Elementor videá automaticky
Nie je potrebné upravovať widgety. Stačí pripojiť a funguje.
Ideálne prípady použitia
Tento skript je navrhnutý špeciálne pre rozloženia s výrazným pohybom, ako napríklad:
- Vertikálne karusely s autoplay
- Viacstĺpcové video mriežky s animáciou pri scrollovaní
- Sekcie s video pozadím s čiastočnou viditeľnosťou
- Opakujúce sa promo videá v slideroch
- Dlhé stránky s mnohými Elementor video widgetmi
Ak vaše rozloženie obsahuje:
- pohybujúce sa videá,
- autoplay slučky a
- viac ako 3 videá,
...tento skript je pre mobilných používateľov nevyhnutný.
Záverečné myšlienky
Predvolené video widgety Elementoru neobsahujú lazy-loading, riadenie na základe viditeľnosti ani optimalizáciu RAM. Na desktope je to zvládnuteľné, ale mobilné zariadenia môžu byť rýchlo preťažené.
Tento skript pridáva chýbajúcu logiku:
- inteligentné načítanie
- inteligentné odstránenie
- automatické pozastavenie
- sledovanie viewport
Vaši návštevníci získajú:
- plynulý výkon
- žiadne zamrznutia
- plnú interaktivitu
A vaše videá sa naďalej automaticky prehrávajú len vtedy, keď majú.
Ak riešite podobné problémy s výkonom, pozrite si moje služby Elementor vývoja a optimalizácie výkonu webov.
