Cara Mendeteksi Penutupan Tab Browser dengna javascript

Terkadang, Anda perlu melakukan sesuatu dengan benar sebelum pengguna menutup tab. Itu bisa membersihkan cookie atau mengirim panggilan API.

Namun, dalam JavaScript/TypeScript, tidak ada cara langsung/standar untuk mengidentifikasi apakah pengguna telah menutup tab dan/atau jendela browser. Saya terkejut menemukan bahwa tidak ada cara langsung untuk melakukannya. Dalam artikel ini, Anda akan mempelajari cara mendeteksi tindakan pemuatan ulang halaman, penutupan tab, dan penutupan browser secara efektif dan juga membedakannya.

Contoh yang akan saya tunjukkan di bawah ini menggunakan Angular 10. Jika Anda menggunakan kerangka kerja lain atau JavaScript secara umum, sintaksnya akan bervariasi tetapi teorinya tetap sama.

sebelum dibongkar

JavaScript menyediakan event handler yang disebut beforeunload. Acara ini dipanggil setiap kali sumber daya dihapus/dibongkar dari klien.

Ini berarti peristiwa ini akan dipicu setiap kali pengguna memuat ulang halaman, menutup tab, atau jendela browser. Sudah cukup jika Anda hanya mencari itu. Namun, untuk sebagian besar aplikasi, kita perlu membedakan antara ‘close’ (tab atau jendela) dan ‘reload’. Mari kita lihat bagaimana kita bisa melakukannya.

// Contoh Angular untuk event handler 
beforeunload @HostListener('window:beforeunload', ['$event'])
beforeunloadHandler(event): void {
// Lakukan sesuatu
}

1) Mengidentifikasi Muat Ulang Halaman

Kami tahu sejauh ini, pemuatan ulang halaman juga akan memicu acara beforeunload. Saat peristiwa dipicu, kami tidak tahu apa yang memicunya (muat ulang, tutup tab, atau tutup browser). Dalam event handler itu, kita perlu mengidentifikasi apakah halaman tersebut dimuat ulang atau tidak.

Muat ulang halaman dapat diidentifikasi menggunakan NavigationTiming API. Anda dapat membaca semua tentang itu di sini .

Harap dicatat bahwa window.performance.navigation sudah usang dan karenanya, Anda harus menghindari menggunakannya. Gunakan properti yang disediakan oleh NavigationTiming API sebagai gantinya.

Berdasarkan dokumentasi NavigationTiming, kita perlu mencari tipe navigasi ‘reload’ di dalam array tipe entri navigasi. Kami akan menghindari penggunaan loop untuk kesederhanaan. Mari kita kode itu

let pageReloaded = window.performance 
.getEntriesByType('navigation')
.map((nav) => (nav as any).type)
.includes('reload');

Sama seperti itu, sekarang pageReloaded membawa nilai boolean apakah halaman tersebut dimuat ulang atau tidak. Anda ingin memasukkannya ke dalam event handler beforeunload kami.

CATATAN: Ada banyak alasan mengapa kami tidak menggunakan loop. Saya menjelaskan dengan baik tentang mengapa Anda tidak boleh menggunakan loop dalam artikel ini . Jika Anda menggunakan JavaScript dan bukan TypeScript, satu-satunya perubahan adalah penggunaan fungsi panah di map.

2) Mendeteksi Tab Semakin Ditutup

Tidak seperti pemuatan ulang halaman, tidak ada cara standar untuk mendeteksi apakah tab ditutup atau tidak. Namun, kita dapat menggunakan penyimpanan lokal untuk mencapai hal ini.

Penyimpanan Lokal di browser digunakan bersama oleh domain yang sama. Ini berarti Anda dapat membuka banyak tab di browser dari katakanlah www.google.com dan semuanya berbagi penyimpanan lokal yang sama.

Idenya sederhana, kita dapat menghitung jumlah total tab yang terbuka di browser untuk domain tertentu. Diinstantiated pada 1, itu akan bertambah 1 setiap kali tab baru dibuka (halaman baru dipakai) dan berkurang 1 ketika tab ditutup (menggunakan event handler beforeunload).

CATATAN: Untuk tab yang ditutup, kita perlu memastikan bahwa kita tidak mengurangi jumlah tab saat halaman dimuat ulang. Kami telah melihat di poin 1 bagaimana kami dapat mendeteksi pemuatan ulang halaman.

Mari kita lihat kodenya

2.1) Menambah jumlah tab

ngOnInit() { 
// Kita perlu mengurai menjadi integer karena penyimpanan lokal hanya dapat
// menyimpan string.
biarkan tabCount = parseInt(localStorage.getItem("windowCount")); // Kemudian kita instantiate tabCount jika belum ada
// ATAU
Naik 1 jika sudah ada tabCount = Number.isNaN(tabCount) ? 1 : ++tabHitung;

// Atur jumlah penyimpanan lokal
localStorage.setItem('tabCount', tabCount.toString());
}

2.2) Mengurangi jumlah tab

@HostListener('window:beforeunload', ['$event']) 
beforeunloadHandler(event): void {
if(!pageReloaded) { // Boolean pageReloaded yang kita atur sebelumnya
let tabCount = parseInt(localStorage.getItem('tabCount') );
--tab Hitung;
localStorage.setItem('tabCount', tabCount.toString());
}
}

Sama seperti ini, sekarang kita dapat mengetahui kapan sebuah tab akan ditutup. Setiap kali tabCount kami berkurang 1 tab telah ditutup. Anda dapat melakukan apa pun yang Anda inginkan sekarang untuk mendeteksi ini.

Ada cara lain untuk mendapatkan semua tab yang dibuka di browser. Menggunakan window.getAll() kita dapat mencapai hal yang tepat plus lebih banyak lagi. Namun, itu tidak didukung oleh Firefox dan Safari versi terbaru. Ini juga membutuhkan izin dari pengguna. Untuk tujuan kita, menggunakan metode penyimpanan lokal lebih tepat (karena window.getAll() tidak sinkron) dan itu membuat semuanya tetap sederhana.

Sekarang mari kita selangkah lebih maju. Kami sekarang akan mendeteksi apakah itu browser close atau tab close.

CATATAN: Jika yang Anda inginkan hanyalah mendeteksi apakah tab atau browser ditutup dan tidak membedakannya, Anda bahkan tidak perlu menyimpan tabCount. Jika Anda menekan event handler beforeunload dan boolean pageReloaded salah, dipastikan bahwa tab atau jendela telah ditutup.

3) Mendeteksi Penutupan Browser

Untuk memahami browser close, pertama-tama kita perlu memahami bagaimana browser ditutup.

Saat pengguna menutup jendela browser, setiap tab di dalam browser itu ditutup satu demi satu (berdasarkan eksperimen saya, penundaan di Chrome adalah sekitar 1 md saat memuat ringan hingga rata-rata).

Sekarang, kita tahu bahwa browser close akan memicu event handler beforeunload. Karena setiap tab akan ditutup satu demi satu, kita dapat mencoba menangkap penundaan antara dua penutupan tab yang berurutan. Jika penundaan penutupan adalah menit (1ms-50ms), itu sinyal kami bahwa browser sedang menutup.

isBrowserClosed() { 
var localStorageTime = parseInt(localStorage.getItem('storageTime'));
var currentTime = Tanggal baru().getTime();
var timeDifference = currentTime - localStorageTime; if (timeDifference < 50) {
//Browser sedang ditutup
// Lakukan sesuatu sebelum browser ditutup.
}
}

CATATAN: Untuk mendeteksi penutupan browser secara akurat, kita perlu memperbarui StorageTime jika perbedaan antara waktu saat ini dan waktu penyimpanan sebelum pemuatan terlalu tinggi. Kode di bawah ini adalah contohnya.

@HostListener('window:beforeunload', ['$event']) 
beforeunloadHandler(event): void {
if(!pageReloaded) { // Boolean pageReloaded yang kita atur sebelumnya
if (storageTime === null || storageTime === undefined
|| (storageTime - new Date().getTime()) > 1000) {
// Jika storageTime adalah null, undefined atau lebih lama dari 1 detik
// kami memperbarui waktu penyimpanan.
}
}
}

Trik kecil ini juga mengurangi kemungkinan kita akan melewatkan acara penutupan browser karena kinerja perangkat keras klien yang buruk.

Dalam kasus kinerja perangkat keras klien yang sangat buruk, skenario terburuknya adalah bahwa setiap peristiwa sebelum pembongkaran akan terdeteksi sebagai penutupan tab.

Kesimpulan

Berdasarkan apa yang kami lihat sejauh ini, kami dapat merebus metode deteksi kami menjadi 3 poin:

  1. Jika entri navigasi memiliki jenis navigasi muat ulang, pengguna memuat ulang halaman.
  2. Jika jumlah tab berkurang, pengguna menutup tab.
  3. Jika jumlah tab berkurang DAN perbedaan waktu penyimpanan kurang dari 50 ms, pengguna mungkin menutup browser.

Kami mengatakan mungkin di poin ketiga karena hambatan kami adalah kinerja perangkat keras klien. Meskipun kami telah menempatkan langkah-langkah ekstra untuk memastikan hasil yang akurat, masih ada kemungkinan bahwa kami mungkin melewatkannya. Yang sedang berkata, dalam banyak kasus, ini sudah cukup.