WordPress

Elementor lazyloading za video background

5 min čitanja
elementor video lazy loading vimeo youtube mp4

Kako smo implementirali lazy učitavanje video-zapisa koristeći Cloudflare Workers

Elementor omogućava korišćenje pozadinskih video-zapisa (YouTube, Vimeo i self-hosted MP4) u sekcijama i kontejnerima, ali postoji jedan veliki problem koji se godinama ignoriše: pozadinski video-zapisi se ne lazy loaduju zaista.

Čak i kada je u Elementor podešavanjima uključena opcija za lazy load ili kada postoji placeholder slika, video se i dalje inicijalizuje odmah pri učitavanju stranice. To znači da se:

  • YouTube / Vimeo API učitavaju odmah
  • iframe se kreira odmah
  • video konekcije se otvaraju pre nego što korisnik vidi sekciju

Rezultat su:

  • loš TTFB
  • veći JS execution time
  • nepotrebni mrežni zahtevi
  • slabiji Lighthouse i Core Web Vitals skorovi

Ovo je naročito problematično na stranicama sa više sekcija i hero background videima.

Zašto klasična rešenja ne rade

Tokom godina smo testirali gotovo sve poznate pristupe:

  • JavaScript koji uklanja iframe i kasnije ga vraća
  • MutationObserver hackove
  • DOM manipulacije nakon DOMContentLoaded
  • Plugin-based lazy load rešenja
  • Elementor addone

Sva ova rešenja imaju bar jedan od sledećih problema:

  • lomljenje Elementor lifecycle-a
  • race condition sa Elementor frontend skriptama
  • flicker efekat
  • problemi pri update-u Elementora
  • ne rade konzistentno za Vimeo / YouTube / MP4

Ključni problem je jednostavan:
Elementor sam kontroliše kada se video aktivira, i ako se to ne preseče na pravom mestu, nema pravog lazy load-a.

Gde je stvarni problem u Elementor-u

U Elementor core JavaScript-u postoji handler BackgroundVideo koji u metodi run() odmah poziva activate() čim su ispunjeni osnovni uslovi:

  • dozvoljeno na trenutnom device-u
  • background type = video
  • postoji video link

Tu se dešava prerano učitavanje videa.

Drugim rečima:
Elementor nema koncept viewport-based lazy loadinga za background video.

Naše rešenje: Cloudflare Worker koji patch-uje Elementor JS

Umesto da:

  • menjamo Elementor fajlove
  • pravimo WordPress plugin
  • ili manipulišemo DOM-om

odlučili smo da problem rešimo na ivici mreže, koristeći Cloudflare Worker.

Ideja je jednostavna, ali moćna:

  • presrećemo tačno jedan Elementor JS fajl
  • menjamo samo jednu metodu (run())
  • uvodimo IntersectionObserver
  • aktiviramo video tek kada element uđe u viewport

Sve ostalo ostaje netaknuto.

Šta tačno patch radí

Naš Worker:

  1. Presreće fajl/wp-content/plugins/elementor/assets/js/shared-frontend-handlers.*.bundle.min.js
  2. Pronalazi originalnu run() metodu za BackgroundVideo
  3. Zamenjuje je verzijom koja:
    • ne poziva activate() odmah
    • kreira IntersectionObserver
    • čeka da se Elementor sekcija pojavi u viewport-u
    • tek tada poziva originalni activate()
  4. Poštuje sva Elementor pravila:
    • mobile / desktop settings
    • play once
    • privacy mode
    • YouTube, Vimeo i self-hosted MP4

Šta dobijaš ovim pristupom

Pravi lazy load (ne simulacija)

Video se ne inicijalizuje uopšte dok korisnik ne dođe blizu sekcije.

Radi sa Elementor core-om

Ne oslanja se na addon-e niti na DOM hackove.

Bez lomljenja editora

Elementor editor i frontend ostaju stabilni.

Update-safe

Ako se Elementor update-uje, Worker se može:

  • prilagoditi
  • ili trenutno isključiti

Bez rollback-a i bez downtime-a.

Globalno rešenje

Jedan Worker važi za ceo sajt ili više sajtova.

Koji video-zapisi su obuhvaćeni

Ovo rešenje važi za Elementor core background videos, konkretno:

  • YouTube background video
  • Vimeo background video
  • Self-hosted MP4 background video
  • Section i Container background
  • Desktop i mobile (prema Elementor pravilima)

Ne utiče na:

  • video widgete
  • popup video-zapise
  • third-party addon-e

Zašto je Cloudflare Worker idealan za ovo

Cloudflare Worker omogućava:

  • menjanje JavaScript-a bez dodira WordPress-a
  • edge-level optimizaciju
  • trenutno rollback-ovanje
  • precizno targetiranje samo jednog fajla

U ovom slučaju, Worker je čistiji i sigurniji nego bilo koji WordPress plugin.

Ovo nije trik.
Ovo je strukturno ispravno rešenje.

Ako koristiš Elementor i pozadinske video-zapise, ovo je trenutno najčistiji način da ih lazy load-uješ kako treba.

Worker Kod

export default {
  async fetch(request) {
    const url = new URL(request.url);

// Target ONLY Elementor background video handler bundle (all versions)
const isElementorBgHandler =
  /^\/wp-content\/plugins\/elementor\/assets\/js\/shared-frontend-handlers\..+\.bundle\.min\.js$/.test(
    url.pathname
  );

if (!isElementorBgHandler) {
  return fetch(request);
}


    const upstream = await fetch(request);

    if (!upstream.ok) {
      return upstream;
    }

    let js = await upstream.text();

    // EXACT original minified method
    const originalRun =
      'run(){const e=this.getElementSettings();(e.background_play_on_mobile||"mobile"!==elementorFrontend.getCurrentDeviceMode())&&("video"===e.background_background&&e.background_video_link?this.activate():this.deactivate())}';

    // Patched version with true lazy loading
    const patchedRun =
      'run(){const e=this.getElementSettings();' +
      'if(!(e.background_play_on_mobile||"mobile"!==elementorFrontend.getCurrentDeviceMode()))return;' +
      '("video"===e.background_background&&e.background_video_link)?this.__lazyBgVideoInit():this.deactivate()}' +

      '__lazyBgVideoInit(){' +
      'if(this.__bgVideoLazyDone||this.__bgVideoObserver)return;' +
      'const e=this.$element&&this.$element[0]?this.$element[0]:null;' +
      'if(!e){this.activate();this.__bgVideoLazyDone=!0;return;}' +

      'const t=()=>{' +
      'if(this.__bgVideoLazyDone)return;' +
      'this.__bgVideoLazyDone=!0;' +
      'try{this.__bgVideoObserver&&this.__bgVideoObserver.disconnect()}catch(e){}' +
      'this.__bgVideoObserver=null;' +
      'this.activate();' +
      '};' +

      'this.__bgVideoObserver=new IntersectionObserver((e)=>{' +
      'for(const i of e){if(i.isIntersecting||i.intersectionRatio>0){t();break}}' +
      '},{root:null,rootMargin:"300px 0px",threshold:0.01});' +

      'this.__bgVideoObserver.observe(e)}';

    if (js.includes(originalRun)) {
      js = js.replace(originalRun, patchedRun);
    }

    return new Response(js, {
      status: 200,
      headers: {
        "content-type": "application/javascript; charset=utf-8",
        "cache-control": "public, max-age=3600",
        "x-elementor-bg-video-lazyload": "true"
      }
    });
  }
};