Cara Kerja Konten Interaktif BBC di AMP, Aplikasi, dan Web
Diterbitkan: 2022-03-10Di tim Jurnalisme Visual di BBC, kami memproduksi konten visual, menarik, dan interaktif yang menarik, mulai dari kalkulator hingga visualisasi format bercerita baru.
Setiap aplikasi merupakan tantangan unik untuk diproduksi dengan caranya sendiri, tetapi terlebih lagi jika Anda menganggap bahwa kami harus menerapkan sebagian besar proyek dalam berbagai bahasa. Konten kami harus berfungsi tidak hanya di situs web Berita dan Olahraga BBC tetapi juga pada aplikasi yang setara di iOS dan Android, serta di situs pihak ketiga yang menggunakan konten BBC.
Sekarang pertimbangkan bahwa ada semakin banyak platform baru seperti AMP, Artikel Instan Facebook, dan Apple News. Setiap platform memiliki batasan dan mekanisme penerbitan eksklusifnya sendiri. Membuat konten interaktif yang berfungsi di semua lingkungan ini adalah tantangan nyata. Saya akan menjelaskan bagaimana kami mendekati masalah di BBC.
Contoh: Canonical vs. AMP
Ini semua sedikit teoretis sampai Anda melihatnya beraksi, jadi mari kita langsung ke sebuah contoh.
Berikut adalah artikel BBC yang berisi konten Jurnalisme Visual:

Ini adalah versi kanonik artikel, yaitu versi default, yang akan Anda dapatkan jika membuka artikel dari beranda.
Sekarang mari kita lihat artikel versi AMP:

Meskipun versi kanonis dan AMP terlihat sama, keduanya sebenarnya adalah dua titik akhir yang berbeda dengan perilaku yang berbeda:
- Versi kanonik menggulir Anda ke negara pilihan Anda saat Anda mengirimkan formulir.
- Versi AMP tidak men-scroll Anda, karena Anda tidak dapat men-scroll halaman induk dari dalam iframe AMP.
- Versi AMP menampilkan iframe yang dipangkas dengan tombol 'Tampilkan Lainnya', bergantung pada ukuran area pandang dan posisi gulir. Ini adalah fitur dari AMP.
Selain versi kanonik dan AMP dari artikel ini, proyek ini juga dikirimkan ke Aplikasi Berita, yang merupakan platform lain dengan kerumitan dan keterbatasannya sendiri. Jadi bagaimana kami mendukung semua platform ini?
Perkakas Adalah Kunci
Kami tidak membangun konten kami dari awal. Kami memiliki perancah berbasis Yeoman yang menggunakan Node untuk menghasilkan proyek boilerplate dengan satu perintah.
Proyek baru hadir dengan Webpack, SASS, penerapan, dan struktur komponenisasi di luar kotak. Internasionalisasi juga dimasukkan ke dalam proyek kami, menggunakan sistem templating Handlebars. Tom Maslen menulis tentang ini secara rinci dalam postingnya, 13 tips untuk membuat desain web responsif multi-bahasa.
Di luar kotak, ini berfungsi cukup baik untuk kompilasi untuk satu platform tetapi kami perlu mendukung banyak platform . Mari kita mempelajari beberapa kode.
Sematkan vs. Mandiri
Dalam Jurnalisme Visual, kami terkadang menampilkan konten kami di dalam iframe sehingga dapat menjadi "sematan" mandiri dalam sebuah artikel, tidak terpengaruh oleh skrip dan gaya global. Contohnya adalah interaktif Donald Trump yang disematkan dalam contoh kanonik sebelumnya di artikel ini.
Di sisi lain, terkadang kami menampilkan konten kami sebagai HTML mentah. Kami hanya melakukan ini ketika kami memiliki kendali atas seluruh halaman atau jika kami memerlukan interaksi gulir yang sangat responsif. Mari kita sebut ini sebagai output "sematkan" dan "mandiri".
Mari kita bayangkan bagaimana kita bisa membangun "Akankah robot mengambil pekerjaan Anda?" interaktif dalam format "sematkan" dan "mandiri".

Kedua versi konten akan berbagi sebagian besar kode mereka, tetapi akan ada beberapa perbedaan penting dalam penerapan JavaScript antara kedua versi.
Misalnya, lihat tombol 'Cari tahu risiko otomatisasi saya'. Ketika pengguna menekan tombol kirim, mereka harus secara otomatis menggulir ke hasil mereka.
Versi kode "mandiri" mungkin terlihat seperti ini:
button.on('click', (e) => { window.scrollTo(0, resultsContainer.offsetTop); });
Tetapi jika Anda membangun ini sebagai output "sematkan", Anda tahu bahwa konten Anda ada di dalam iframe, jadi perlu mengkodekannya secara berbeda:
// inside the iframe button.on('click', () => { window.parent.postMessage({ name: 'scroll', offset: resultsContainer.offsetTop }, '*'); }); // inside the host page window.addEventListener('message', (event) => { if (event.data.name === 'scroll') { window.scrollTo(0, iframe.offsetTop + event.data.offset); } });
Juga, bagaimana jika aplikasi kita perlu layar penuh? Ini cukup mudah jika Anda berada di halaman "mandiri":
document.body.className += ' fullscreen';
.fullscreen { position: fixed; top: 0; left: 0; right: 0; bottom: 0; }

Jika kami mencoba melakukan ini dari dalam "sematkan", kode yang sama ini akan memiliki penskalaan konten ke lebar dan tinggi iframe , daripada viewport:

…jadi selain menerapkan gaya layar penuh di dalam iframe, kita harus mengirim pesan ke halaman host untuk menerapkan gaya ke iframe itu sendiri:
// iframe window.parent.postMessage({ name: 'window:toggleFullScreen' }, '*'); // host page window.addEventListener('message', function () { if (event.data.name === 'window:toggleFullScreen') { document.getElementById(iframeUid).className += ' fullscreen'; } });
Ini dapat diterjemahkan ke dalam banyak kode spageti ketika Anda mulai mendukung banyak platform:
button.on('click', (e) => { if (inStandalonePage()) { window.scrollTo(0, resultsContainer.offsetTop); } else { window.parent.postMessage({ name: 'scroll', offset: resultsContainer.offsetTop }, '*'); } });
Bayangkan melakukan hal yang setara dengan ini untuk setiap interaksi DOM yang berarti dalam proyek Anda. Setelah Anda selesai bergidik, buatkan diri Anda secangkir teh yang menenangkan, dan baca terus.
Abstraksi Adalah Kunci
Daripada memaksa pengembang kami untuk menangani persyaratan ini di dalam kode mereka, kami membangun lapisan abstraksi antara konten mereka dan lingkungan. Kami menyebut lapisan ini sebagai 'pembungkus'.
Alih-alih menanyakan DOM atau kejadian browser asli secara langsung, sekarang kita dapat mem-proksi permintaan kita melalui modul wrapper
.
import wrapper from 'wrapper'; button.on('click', () => { wrapper.scrollTo(resultsContainer.offsetTop); });
Setiap platform memiliki implementasi pembungkusnya sendiri yang sesuai dengan antarmuka umum metode pembungkus. Pembungkus membungkus dirinya sendiri di sekitar konten kita dan menangani kerumitan untuk kita.

Implementasi fungsi scrollTo
pembungkus mandiri sangat sederhana, meneruskan argumen kita langsung ke window.scrollTo
di bawah tenda.
Sekarang mari kita lihat pembungkus terpisah yang mengimplementasikan fungsi yang sama untuk iframe:

Pembungkus "sematkan" mengambil argumen yang sama seperti pada contoh "mandiri" tetapi memanipulasi nilai sehingga offset iframe diperhitungkan. Tanpa penambahan ini, kami akan menggulir pengguna kami di suatu tempat yang sama sekali tidak diinginkan.
Pola Pembungkus
Menggunakan pembungkus menghasilkan kode yang lebih bersih, lebih mudah dibaca, dan konsisten antar proyek. Ini juga memungkinkan optimasi mikro dari waktu ke waktu, karena kami melakukan peningkatan bertahap pada pembungkus untuk membuat metode mereka lebih berperforma dan dapat diakses. Oleh karena itu, proyek Anda dapat memperoleh manfaat dari pengalaman banyak pengembang.
Jadi, seperti apa pembungkus itu?
Struktur Pembungkus
Setiap wrapper pada dasarnya terdiri dari tiga hal: template Handlebars, file wrapper JS, dan file SASS yang menunjukkan gaya khusus wrapper. Selain itu, ada tugas pembangunan yang menghubungkan ke peristiwa yang diekspos oleh perancah yang mendasarinya sehingga setiap pembungkus bertanggung jawab atas pra-kompilasi dan pembersihannya sendiri.
Ini adalah tampilan yang disederhanakan dari pembungkus sematan:
embed-wrapper/ templates/ wrapper.hbs js/ wrapper.js scss/ wrapper.scss
Scaffolding dasar kami memaparkan template proyek utama Anda sebagai bagian Handlebars, yang digunakan oleh wrapper. Misalnya, templates/wrapper.hbs
mungkin berisi:
<div class="bbc-news-vj-wrapper--embed"> {{>your-application}} </div>
scss/wrapper.scss
berisi gaya khusus pembungkus yang tidak perlu didefinisikan oleh kode aplikasi Anda sendiri. Pembungkus embed, misalnya, mereplikasi banyak gaya BBC News di dalam iframe.
Terakhir, js/wrapper.js
berisi implementasi iframe dari wrapper API, yang dirinci di bawah ini. Itu dikirimkan secara terpisah ke proyek, daripada dikompilasi dengan kode aplikasi — kami menandai wrapper
sebagai global dalam proses pembuatan Webpack kami. Ini berarti bahwa meskipun kami mengirimkan aplikasi kami ke berbagai platform, kami hanya mengkompilasi kode satu kali.
API Pembungkus
API pembungkus mengabstraksi sejumlah interaksi browser utama. Berikut adalah yang paling penting:
scrollTo(int)

Gulir ke posisi yang diberikan di jendela aktif. Pembungkus akan menormalkan bilangan bulat yang disediakan sebelum memicu gulir sehingga halaman host digulir ke posisi yang benar.
getScrollPosition: int
Mengembalikan posisi gulir pengguna saat ini (dinormalisasi). Dalam kasus iframe, ini berarti bahwa posisi gulir yang diteruskan ke aplikasi Anda sebenarnya negatif hingga iframe berada di bagian atas viewport. Ini sangat berguna dan memungkinkan kita melakukan hal-hal seperti menganimasikan suatu komponen hanya ketika komponen itu terlihat.
onScroll(callback)
Menyediakan kait ke acara gulir. Dalam pembungkus mandiri, ini pada dasarnya menghubungkan ke acara gulir asli. Di pembungkus sematan, akan ada sedikit penundaan dalam menerima acara gulir karena diteruskan melalui postMessage.
viewport: {height: int, width: int}
Sebuah metode untuk mengambil tinggi dan lebar viewport (karena ini diterapkan sangat berbeda ketika ditanya dari dalam iframe).
toggleFullScreen
Dalam mode mandiri, kami menyembunyikan menu dan footer BBC dari tampilan dan mengatur position: fixed
pada konten kami. Di Aplikasi Berita, kami tidak melakukan apa pun — konten sudah dalam layar penuh. Yang rumit adalah iframe, yang bergantung pada penerapan gaya baik di dalam maupun di luar iframe, yang dikoordinasikan melalui postMessage.
markPageAsLoaded
Beri tahu pembungkus konten Anda telah dimuat. Ini penting agar konten kami berfungsi di Aplikasi Berita, yang tidak akan berusaha menampilkan konten kami kepada pengguna sampai kami secara eksplisit memberi tahu aplikasi bahwa konten kami sudah siap. Itu juga menghapus pemintal pemuatan di versi web konten kami.
Daftar Pembungkus
Di masa mendatang, kami membayangkan membuat pembungkus tambahan untuk platform besar seperti Artikel Instan Facebook dan Apple News. Kami telah membuat enam pembungkus hingga saat ini:
Pembungkus Mandiri
Versi konten kami yang seharusnya masuk ke halaman mandiri. Dilengkapi dengan branding BBC.
Sematkan Pembungkus
Versi iframe dari konten kami, yang aman untuk ditempatkan di dalam artikel atau untuk disindikasikan ke situs non-BBC, karena kami memegang kendali atas konten tersebut.
Pembungkus AMP
Ini adalah titik akhir yang ditarik sebagai amp-iframe
ke halaman AMP.
Pembungkus Aplikasi Berita
Konten kami harus melakukan panggilan ke protokol bbcvisualjournalism://
milik.
Pembungkus Inti
Hanya berisi HTML — tidak ada CSS atau JavaScript proyek kami.
Pembungkus JSON
Representasi JSON dari konten kami, untuk dibagikan di seluruh produk BBC.
Pembungkus Kabel Hingga Platform
Agar konten kami muncul di situs BBC, kami menyediakan jalur dengan spasi nama kepada jurnalis:
/include/[department]/[unique ID], eg
/include/visual-journalism/123-quiz
Wartawan menempatkan "include path" ini ke dalam CMS, yang menyimpan struktur artikel ke dalam database. Semua produk dan layanan berada di hilir mekanisme penerbitan ini. Setiap platform bertanggung jawab untuk memilih jenis konten yang diinginkan dan meminta konten tersebut dari server proxy.
Mari kita ambil interaktif Donald Trump dari sebelumnya. Di sini, jalur sertakan dalam CMS adalah:
/include/newsspec/15996-trump-tracker/english/index
Halaman artikel kanonik mengetahui bahwa ia menginginkan versi konten "sematkan", sehingga menambahkan /embed
ke jalur penyertaan:
/include/newsspec/15996-trump-tracker/english/index
/embed
…sebelum memintanya dari server proxy:
https://news.files.bbci.co.uk/include/newsspec/15996-trump-tracker/english/index/embed
Halaman AMP, di sisi lain, melihat jalur include dan menambahkan /amp
:
/include/newsspec/15996-trump-tracker/english/index
/amp
Perender AMP melakukan sedikit keajaiban untuk merender beberapa HTML AMP yang mereferensikan konten kita, menarik versi /amp
sebagai iframe:
<amp-iframe src="https://news.files.bbci.co.uk/include/newsspec/15996-trump-tracker/english/index/amp" width="640" height="360"> <!-- some other AMP elements here --> </amp-iframe>
Setiap platform yang didukung memiliki versi kontennya sendiri:
/include/newsspec/15996-trump-tracker/english/index
/amp
/include/newsspec/15996-trump-tracker/english/index
/core
/include/newsspec/15996-trump-tracker/english/index
/envelope
...dan seterusnya
Solusi ini dapat diskalakan untuk memasukkan lebih banyak jenis platform saat muncul.
Abstraksi Itu Sulit
Membangun arsitektur "tulis sekali, sebarkan di mana saja" terdengar cukup idealis, dan memang demikian. Agar arsitektur pembungkus berfungsi, kita harus sangat ketat dalam bekerja di dalam abstraksi. Ini berarti kita harus melawan godaan untuk “melakukan peretasan ini untuk membuatnya bekerja di [masukkan nama platform di sini].” Kami ingin konten kami sama sekali tidak menyadari lingkungan pengirimannya — tetapi ini lebih mudah diucapkan daripada dilakukan.
Fitur Platform Sulit Dikonfigurasi Secara Abstrak
Sebelum pendekatan abstraksi kami, kami memiliki kontrol penuh atas setiap aspek keluaran kami, termasuk, misalnya, markup iframe kami. Jika kami perlu mengubah apa pun berdasarkan proyek, seperti menambahkan atribut title
ke iframe untuk alasan aksesibilitas, kami hanya dapat mengedit markup.
Sekarang markup pembungkus ada dalam isolasi dari proyek, satu-satunya cara untuk mengonfigurasinya adalah dengan mengekspos kait di perancah itu sendiri. Kita dapat melakukan ini dengan relatif mudah untuk fitur lintas platform, tetapi mengekspos kait untuk platform tertentu merusak abstraksi. Kami tidak benar-benar ingin mengekspos opsi konfigurasi 'judul iframe' yang hanya digunakan oleh satu pembungkus.
Kita bisa menamai properti secara lebih umum, misalnya title
, dan kemudian menggunakan nilai ini sebagai atribut title
iframe. Namun, mulai menjadi sulit untuk melacak apa yang digunakan di mana, dan kami berisiko mengabstraksi konfigurasi kami hingga tidak lagi memahaminya. Pada umumnya, kami mencoba untuk menjaga konfigurasi kami tetap ramping mungkin, hanya mengatur properti yang memiliki penggunaan global.
Perilaku Komponen Bisa Kompleks
Di web, modul alat berbagi kami menampilkan tombol berbagi jaringan sosial yang dapat diklik satu per satu dan membuka pesan berbagi yang telah diisi sebelumnya di jendela baru.

Di Aplikasi Berita, kami tidak ingin berbagi melalui web seluler. Jika pengguna telah menginstal aplikasi yang relevan (misalnya Twitter), kami ingin berbagi di aplikasi itu sendiri. Idealnya, kami ingin menyajikan menu berbagi iOS/Android asli kepada pengguna, lalu membiarkan mereka memilih opsi berbagi sebelum kami membuka aplikasi untuk mereka dengan pesan berbagi yang sudah diisi sebelumnya. Kami dapat memicu menu berbagi asli dari aplikasi dengan melakukan panggilan ke protokol bbcvisualjournalism://
milik.

Namun, layar ini akan dipicu apakah Anda mengetuk 'Twitter' atau 'Facebook' di bagian 'Bagikan hasil Anda', sehingga pengguna akhirnya harus membuat pilihan dua kali; pertama kali di dalam konten kami, dan kedua kalinya di popup asli.
Ini adalah perjalanan pengguna yang aneh, jadi kami ingin menghapus ikon berbagi individu dari aplikasi Berita dan menampilkan tombol berbagi umum sebagai gantinya. Kami dapat melakukan ini dengan secara eksplisit memeriksa pembungkus mana yang digunakan sebelum kami merender komponen.

Membangun lapisan abstraksi pembungkus bekerja dengan baik untuk proyek secara keseluruhan, tetapi ketika pilihan pembungkus Anda memengaruhi perubahan pada tingkat komponen , sangat sulit untuk mempertahankan abstraksi yang bersih. Dalam hal ini, kami kehilangan sedikit abstraksi, dan kami memiliki beberapa logika forking yang berantakan dalam kode kami. Untungnya, kasus-kasus ini sedikit dan jarang terjadi.
Bagaimana Kami Menangani Fitur yang Hilang?
Menjaga abstraksi semuanya baik dan bagus. Kode kami memberi tahu wrapper apa yang diinginkan platform untuk dilakukan, misalnya "layar penuh". Tetapi bagaimana jika platform tujuan pengiriman kami tidak dapat benar-benar menjadi layar penuh?
Pembungkus akan mencoba yang terbaik untuk tidak pecah sama sekali, tetapi pada akhirnya Anda memerlukan desain yang dengan anggun kembali ke solusi yang berfungsi apakah metode ini berhasil atau tidak. Kami harus mendesain secara defensif.
Katakanlah kita memiliki bagian hasil yang berisi beberapa diagram batang. Kami sering ingin mempertahankan nilai diagram batang pada nol hingga diagram digulir ke tampilan, di mana kami memicu batang yang bergerak ke lebar yang benar.

Tetapi jika kita tidak memiliki mekanisme untuk menghubungkan ke posisi scroll — seperti yang terjadi pada pembungkus AMP kita — maka bilah akan selamanya tetap nol, yang merupakan pengalaman yang benar-benar menyesatkan.

Kami semakin mencoba untuk mengadopsi lebih banyak pendekatan peningkatan progresif dalam desain kami. Misalnya, kami dapat menyediakan tombol yang akan terlihat untuk semua platform secara default, tetapi akan disembunyikan jika pembungkusnya mendukung pengguliran. Dengan begitu, jika scroll gagal memicu animasi, pengguna masih dapat memicu animasi secara manual.

Rencana Untuk Masa Depan
Kami berharap dapat mengembangkan pembungkus baru untuk platform seperti Apple News dan Artikel Instan Facebook, serta menawarkan semua platform baru versi 'inti' dari konten kami di luar kotak.
Kami juga berharap untuk menjadi lebih baik dalam peningkatan progresif; berhasil di bidang ini berarti berkembang secara defensif. Anda tidak akan pernah bisa berasumsi bahwa semua platform sekarang dan di masa depan akan mendukung interaksi tertentu, tetapi proyek yang dirancang dengan baik harus dapat menyampaikan pesan intinya tanpa jatuh pada rintangan teknis pertama.
Bekerja dalam batas-batas pembungkus adalah sedikit perubahan paradigma, dan terasa seperti rumah setengah jalan dalam hal solusi jangka panjang . Tetapi sampai industri matang menjadi standar lintas platform, penerbit akan dipaksa untuk meluncurkan solusi mereka sendiri, atau menggunakan alat seperti Distro untuk konversi platform-ke-platform, atau mengabaikan seluruh bagian audiens mereka sama sekali.
Ini adalah hari-hari awal bagi kami, tetapi sejauh ini kami telah sukses besar dalam menggunakan pola pembungkus untuk membangun konten kami sekali dan mengirimkannya ke berbagai platform yang sekarang digunakan audiens kami.