D
P
0
← Semua artikel Read in English

JavaScript, DOM & Animasi Browser

Video `autoplay` Cuma Nampilin Poster? Elemennya Duduk di Luar Layar Waktu First Paint

· · 6 menit baca
Video `autoplay` Cuma Nampilin Poster? Elemennya Duduk di Luar Layar Waktu First Paint

Section carousel di halaman depan itu bentuknya stage sticky setinggi 330svh: tiga slide meluncur horizontal mengikuti scroll, dan salah satu slide isinya video produk. Videonya tidak pernah jalan. Yang muncul cuma satu frame diam, poster yang memang saya siapkan sendiri untuk elemen itu, jadi sekilas slide-nya terlihat seperti foto biasa di antara dua foto lain.

Yang bikin bingung, atributnya tidak ada yang kurang:

<video autoplay muted loop playsinline poster="slide-poster_w1280.webp">
  <source src="slide-web.mp4" type="video/mp4" />
</video>

autoplay ada, muted ada, loop ada, playsinline ada supaya iOS tidak memaksa fullscreen. Kombinasi autoplay muted yang sama juga dipakai di tema ini untuk latar hero full-bleed 16:9 di halaman lain. Jadi tersangka paling gampang, yaitu satu atribut yang kelupaan, gugur sejak awal.

Bug ini tidak datang sendirian. Dia muncul bareng dua bug lain di section yang sama, dalam satu sesi tanggal 13 Mei 2026, setelah pekerjaan di halaman lain mendarat. Ketiganya masuk sebagai koreksi pasca-rilis di dalam v1.8.0, tanpa menaikkan nomor versi.

Posisinya, bukan atributnya

Yang membedakan video ini dari video hero di halaman lain bukan markup-nya, tapi di mana dia duduk waktu halaman pertama kali digambar.

Track carousel-nya selebar 300 persen viewport dan isinya tiga slide, jadi tiap slide memakan 100 persen: 300 dibagi 3. Video itu berada di offset 100 sampai 200 persen, sepertiga tengah track. Artinya saat first paint, elemen video itu bukan sekadar di bawah lipatan, dia ada satu layar penuh di sebelah kanan area yang terlihat. Dia baru masuk viewport setelah user scroll dan track-nya bergeser.

Yang saya catat sebagai penyebabnya waktu itu: Chrome dan Edge menunda autoplay untuk video yang berada di luar layar sebagai heuristik hemat baterai, sehingga atribut autoplay tidak pernah menyala. Saya tulis itu sebagai dugaan, bukan hasil pengukuran. Tidak ada angka dan tidak ada rujukan spesifikasi di catatan saya, yang saya pegang cuma satu fakta bentuk: elemennya secara fisik ada di luar layar sejak awal, dan atributnya tidak pernah berbunyi.

Perbaikannya: panggil play() sendiri

Kalau atribut autoplay tidak bisa diandalkan menyala, berhenti mengandalkannya. Saya tambahkan pemicu video.play() programatik di dalam init carousel:

root.querySelectorAll('video').forEach((v) => {
  v.play().catch(() => {});
  v.addEventListener('canplay', () => v.play().catch(() => {}));
});

Selektornya querySelectorAll('video') pada root section, jadi dia menyapu semua elemen video di dalam section tanpa perlu tahu ada berapa. Play dicoba dua kali: sekali saat init, dan sekali lagi pada event canplay. Percobaan pertama menutup kasus data video sudah cukup siap waktu init jalan, yang kedua menutup kasus sebaliknya, init selesai duluan sebelum browser punya cukup data untuk mulai memutar.

Dasar yang saya pakai waktu menulis perbaikan ini: video yang muted melewati autoplay policy kalau dimainkan secara programatik. Itu juga asumsi yang saya catat, bukan sesuatu yang saya buktikan lewat pengukuran, dan bukti praktisnya cuma videonya jadi jalan.

.catch(() => {}) di situ bukan hiasan. play() mengembalikan Promise, dan kalau policy tetap memblokir, Promise itu reject. Catch-nya sengaja menelan penolakan akibat blokir policy supaya tidak ada rejection yang menggantung. Gerbang desktop-only di init section ini juga sudah saya lepas sebelumnya supaya efek scroll-nya jalan di semua viewport, jadi pemicu play ini ikut jalan di mana-mana.

Bug kedua: thumb yang seharusnya video

Di kiri dan kanan slide besar ada strip thumb pratinjau. Untuk slide video, dua thumb itu ternyata bukan video sama sekali, melainkan <img> berisi frame poster, frame 1 video sebagai gambar diam. Poster itu sendiri saya ekstrak pakai ffmpeg dari mp4-nya di detik 0,5, jadi dua ukuran webp, dan yang w720 dipakai di thumb.

Karena tipe elemennya <img>, tidak ada cara menganimasikannya. Ini bukan video yang gagal jalan, ini memang gambar. Waktu saya lacak ke belakang, komponen galeri asal yang section ini di-port darinya memang memakai video di posisi itu. Video-nya hilang waktu porting.

Perbaikannya mengganti dua <img> poster itu dengan elemen video sungguhan, di slot 2 strip kiri dan slot 1 strip kanan:

<!-- sebelum -->
<img src="slide-poster_w720.webp" alt="" />
 
<!-- sesudah -->
<video autoplay muted loop playsinline preload="auto" poster="slide-poster_w720.webp">
  <source src="slide-web.webm" type="video/webm" />
  <source src="slide-web.mp4" type="video/mp4" />
</video>

Satu hal yang hampir kelewat: aturan CSS yang memposisikan isi thumb cuma menyasar img. Di tema ini sudah ada preseden aturan img global menyentuh elemen media, img { max-width: 100% } pernah bikin logo header mengkerut sampai sekitar 34px, jadi saya periksa selektornya dan memang video tidak kena apa-apa. Aturannya saya perluas ke dua tipe elemen dengan deklarasi yang sama persis:

.carousel__thumb img,
.carousel__thumb video {
  position: absolute;
  inset: 0;
  object-fit: cover;
  display: block;
}

Yang menyenangkan, perbaikan JS dari bug pertama otomatis ikut menangani dua video baru ini, karena querySelectorAll('video') menyapu apa pun yang ada di dalam root section. Nol baris tambahan.

Tiga elemen video, satu berkas

Setelah perbaikan, section itu punya tiga elemen video: satu video utama dan dua video strip, semuanya menunjuk ke berkas yang sama.

Alasan yang saya pegang waktu memutuskan itu aman: cache HTTP browser membagi berkasnya ke semua elemen video sehingga tetap satu kali fetch. Saya tidak membuka panel Network untuk membuktikannya, jadi perlakukan itu sebagai pertimbangan, bukan sebagai hasil ukur.

Yang bentuknya bisa saya pastikan: strip-nya display: none pada lebar 1023px ke bawah, jadi di mobile cuma video utama yang main dan dua elemen tambahan itu tidak pernah hadir di viewport kecil. Video di tema ini juga sudah disiapkan dwiformat mp4 dan webm lewat kompresi ffmpeg. Yang 16:9 turun dari 167MB jadi 6,6MB untuk MP4 dan 8,5MB untuk WebM, sekitar 25 kali lebih kecil untuk versi MP4-nya, dari 167 dibagi 6,6.

Yang tidak sempat selesai

Autoplay video lifestyle di mobile masih saya tandai sendiri sebagai sesuatu yang bisa janky dan mungkin butuh disablepictureinpicture atau pause-on-blur. Itu berhenti di daftar follow-up dan tidak pernah saya kerjakan.

Alasannya bukan karena saya lupa. Klien mengubah arah, brief-nya berganti jadi replika visual sebuah situs referensi, dan seluruh kerja tema sampai v1.8.7 dinyatakan dibuang, termasuk pattern, CSS, dan JS yang memuat semua perbaikan di atas. Bug-nya nyata, perbaikannya jalan, kodenya sudah tidak ada lagi di repo.

Poin yang saya bawa pulang