Cara Mendeteksi Peristiwa Tombol Kembali Peramban - Lintas Peramban


218

Bagaimana Anda secara definitif mendeteksi apakah pengguna telah menekan tombol kembali di browser atau tidak?

Bagaimana Anda menegakkan penggunaan tombol kembali di dalam halaman di dalam aplikasi web satu halaman menggunakan #URLsistem?

Kenapa tidak tombol kembali browser menyalakan acara mereka sendiri !?


2
Saya berharap acara navigasi (mundur / maju) ditambahkan kapan-kapan. Sejauh ini ada sesuatu yang sedang dikembangkan developer.mozilla.org/en-US/docs/Web/API/…
llaaalu

Jawaban:


184

(Catatan: Sesuai umpan balik Sharky, saya telah menyertakan kode untuk mendeteksi backspaces)

Jadi, saya sering melihat pertanyaan-pertanyaan ini di SO, dan baru-baru ini mengalami masalah dalam mengontrol fungsi tombol kembali sendiri. Setelah beberapa hari mencari solusi terbaik untuk aplikasi saya (Satu Halaman dengan Navigasi Hash), saya telah membuat sistem yang sederhana, lintas peramban, tanpa perpustakaan untuk mendeteksi tombol kembali.

Kebanyakan orang merekomendasikan untuk menggunakan:

window.onhashchange = function() {
 //blah blah blah
}

Namun, fungsi ini juga akan dipanggil ketika pengguna menggunakan elemen dalam halaman yang mengubah hash lokasi. Bukan pengalaman pengguna terbaik ketika pengguna Anda mengklik dan halaman berjalan mundur atau maju.

Untuk memberi Anda garis besar umum sistem saya, saya mengisi array dengan hash sebelumnya saat pengguna saya bergerak melalui antarmuka. Itu terlihat seperti ini:

function updateHistory(curr) {
    window.location.lasthash.push(window.location.hash);
    window.location.hash = curr;
}

Cukup lurus ke depan. Saya melakukan ini untuk memastikan dukungan lintas-browser, serta dukungan untuk browser yang lebih lama. Cukup sampaikan hash baru ke fungsi, dan itu akan menyimpannya untuk Anda dan kemudian mengubah hash (yang kemudian dimasukkan ke dalam sejarah browser).

Saya juga memanfaatkan tombol kembali di dalam halaman yang menggerakkan pengguna di antara halaman menggunakan lasthasharray. Ini terlihat seperti ini:

function goBack() {
    window.location.hash = window.location.lasthash[window.location.lasthash.length-1];
    //blah blah blah
    window.location.lasthash.pop();
}

Jadi ini akan memindahkan pengguna kembali ke hash terakhir, dan menghapus hash terakhir dari array (saya tidak punya tombol maju sekarang).

Begitu. Bagaimana cara mendeteksi apakah pengguna telah menggunakan tombol kembali di-halaman saya, atau tombol browser?

Pada awalnya saya melihat window.onbeforeunload, tetapi tidak berhasil - itu hanya disebut jika pengguna akan mengubah halaman. Ini tidak terjadi dalam aplikasi satu halaman menggunakan navigasi hash.

Jadi, setelah beberapa penggalian lagi, saya melihat rekomendasi untuk mencoba mengatur variabel flag. Masalah dengan ini dalam kasus saya, adalah bahwa saya akan mencoba untuk mengaturnya, tetapi karena semuanya asinkron, itu tidak akan selalu diatur dalam waktu untuk pernyataan if dalam perubahan hash. .onMouseDowntidak selalu dipanggil klik, dan menambahkannya ke onclick tidak akan pernah memicunya cukup cepat.

Ini adalah ketika saya mulai melihat perbedaan antara document, dan window. Solusi terakhir saya adalah mengatur flag menggunakan document.onmouseover, dan menonaktifkannya menggunakan document.onmouseleave.

Yang terjadi adalah ketika mouse pengguna berada di dalam area dokumen (baca: halaman yang diberikan, tetapi tidak termasuk bingkai browser), boolean saya disetel ke true. Segera setelah mouse meninggalkan area dokumen, boolean membalik ke false.

Dengan cara ini, saya dapat mengubah window.onhashchangeke:

window.onhashchange = function() {
    if (window.innerDocClick) {
        window.innerDocClick = false;
    } else {
        if (window.location.hash != '#undefined') {
            goBack();
        } else {
            history.pushState("", document.title, window.location.pathname);
            location.reload();
        }
    }
}

Anda akan mencatat cek untuk #undefined. Ini karena jika tidak ada riwayat yang tersedia di array saya, itu kembali undefined. Saya menggunakan ini untuk bertanya kepada pengguna apakah mereka ingin pergi menggunakan window.onbeforeunloadacara.

Jadi, singkatnya, dan untuk orang-orang yang tidak perlu menggunakan tombol kembali di-halaman atau array untuk menyimpan sejarah:

document.onmouseover = function() {
    //User's mouse is inside the page.
    window.innerDocClick = true;
}

document.onmouseleave = function() {
    //User's mouse has left the page.
    window.innerDocClick = false;
}

window.onhashchange = function() {
    if (window.innerDocClick) {
        //Your own in-page mechanism triggered the hash change
    } else {
        //Browser back button was clicked
    }
}

Dan begitulah. sederhana, tiga bagian cara untuk mendeteksi penggunaan tombol kembali vs elemen dalam halaman sehubungan dengan navigasi hash.

EDIT:

Untuk memastikan bahwa pengguna tidak menggunakan backspace untuk memicu acara kembali, Anda juga dapat memasukkan yang berikut (Terima kasih kepada @thetoolman pada Pertanyaan ini ):

$(function(){
    /*
     * this swallows backspace keys on any non-input element.
     * stops backspace -> back
     */
    var rx = /INPUT|SELECT|TEXTAREA/i;

    $(document).bind("keydown keypress", function(e){
        if( e.which == 8 ){ // 8 == backspace
            if(!rx.test(e.target.tagName) || e.target.disabled || e.target.readOnly ){
                e.preventDefault();
            }
        }
    });
});

5
+1 ide bagus, tetapi saya pikir ini akan gagal jika pengguna menggunakan pintasan keyboard apa pun untuk "kembali" (tombol backspace pada firefox) sementara mouse-nya ada di dalam jendela browser
Sharky

3
Akan lakukan - Saya di kantor sekarang lagian :) (EDIT: Selesai sekarang)
Xarus

7
Bagaimana dengan ponsel (mis.
IPad

5
Bagaimana dengan swipe events dari trackpad di MAC. Mungkinkah itu ditangkap juga?
Sriganesh Navaneethakrishnan

6
Bagaimana saya bisa membedakan tombol maju dan mundur?
Mr_Perfect

79

Anda dapat mencoba popstateevent handler, misalnya:

window.addEventListener('popstate', function(event) {
    // The popstate event is fired each time when the current history entry changes.

    var r = confirm("You pressed a Back button! Are you sure?!");

    if (r == true) {
        // Call Back button programmatically as per user confirmation.
        history.back();
        // Uncomment below line to redirect to the previous page instead.
        // window.location = document.referrer // Note: IE11 is not supporting this.
    } else {
        // Stay on the current page.
        history.pushState(null, null, window.location.pathname);
    }

    history.pushState(null, null, window.location.pathname);

}, false);

Catatan: Untuk hasil terbaik, Anda harus memuat kode ini hanya pada halaman tertentu di mana Anda ingin menerapkan logika untuk menghindari masalah yang tidak terduga lainnya.

Acara popstate dipecat setiap kali ketika entri riwayat saat ini berubah (pengguna menavigasi ke negara baru). Itu terjadi ketika pengguna mengklik Kembali / Forward tombol browser atau ketika history.back(), history.forward(), history.go()metode programatik disebut.

The event.stateadalah milik dari acara ini adalah sama dengan objek negara sejarah.

Untuk sintaks jQuery, lilitkan (untuk menambahkan pendengar genap setelah dokumen siap):

(function($) {
  // Above code here.
})(jQuery);

Lihat juga: window.onpopstate saat memuat halaman


Lihat juga contoh di Aplikasi Halaman Tunggal dan halaman pushState HTML5 :

<script>
// jQuery
$(window).on('popstate', function (e) {
    var state = e.originalEvent.state;
    if (state !== null) {
        //load content with ajax
    }
});

// Vanilla javascript
window.addEventListener('popstate', function (e) {
    var state = e.state;
    if (state !== null) {
        //load content with ajax
    }
});
</script>

Ini harus kompatibel dengan Chrome 5+, Firefox 4+, IE 10+, Safari 6+, Opera 11.5+ dan yang serupa.


2
Kenorb, Anda benar bahwa popstate berfungsi untuk mengambil perubahan, tetapi tidak membedakan antara program yang memanggil acara dan ketika pengguna mengklik tombol browser.
Xarus

1
Pos hebat di Aplikasi Satu Halaman dan pushState HTML5 . Sempurna untuk "tata letak satu halaman" atau "aplikasi satu halaman" modern. Terima kasih atas tautannya dan jawaban Anda!
lowtechsun

1
e.state tampaknya selalu tidak terdefinisi di browser modern
FAjir

Saya sarankan Anda memasukkan kode ke dalam snippet sehingga Anda dapat menjalankannya langsung di sini.
FourCinnamon0

16

Saya telah berjuang dengan persyaratan ini cukup lama dan mengambil beberapa solusi di atas untuk mengimplementasikannya. Namun, saya menemukan sebuah pengamatan dan tampaknya bekerja di browser Chrome, Firefox dan Safari + Android dan iPhone

Pemuatan halaman:

window.history.pushState({page: 1}, "", "");

window.onpopstate = function(event) {

  // "event" object seems to contain value only when the back button is clicked
  // and if the pop state event fires due to clicks on a button
  // or a link it comes up as "undefined" 

  if(event){
    // Code to handle back button or prevent from navigation
  }
  else{
    // Continue user action through link or button
  }
}

Beri tahu saya jika ini membantu. Jika saya melewatkan sesuatu, saya akan senang untuk mengerti.


1
Tidak benar. eventmemiliki nilai bahkan untuk tombol maju
Daniel Birowsky Popeski

14

Dalam javascript, tipe navigasi 2berarti tombol kembali atau maju browser diklik dan browser sebenarnya mengambil konten dari cache.

if(performance.navigation.type == 2)
{
    //Do your code here
}

1
Ini tidak berfungsi pada aplikasi halaman tunggal, hasilnya selalu 1 (yang berarti memuat ulang). Selain itu tidak membuat perbedaan antara tombol kembali dan maju, yang sangat disayangkan.
papillon

"Apalagi itu tidak membuat perbedaan antara tombol kembali dan maju, yang sangat disayangkan." Ya karena setiap kali data diambil dari cache saat itu performance.navigation.type adalah 2
Hasan Badshah

Itu tidak dijamin berfungsi di semua browser! jadi Anda harus menulis sesuatu seperti ini: if (window.performance) {// kode terkait kinerja Anda} juga lebih baik menggunakan TYPE_BACK_FORWARD daripada 2 untuk lebih mudah dibaca.
blank94

7

Ini pasti akan berfungsi (Untuk mendeteksi klik tombol kembali)

$(window).on('popstate', function(event) {
 alert("pop");
});

6

Lihat ini:

history.pushState(null, null, location.href);
    window.onpopstate = function () {
        history.go(1);
    };

itu bekerja dengan baik ...


Tidak bekerja di Chrome 78.0.3904.70 (Bangun Resmi) (64-bit)
Jeffz

Dikonfirmasi bekerja pada Brave v1.3.118 >> Chromium: 80.0.3987.116 (Bangun Resmi) (64-bit)
ZeroThe2nd

Tidak berfungsi jika Anda datang pada halaman dari situs eksternal.
Milan Vlach

5
if (window.performance && window.performance.navigation.type == window.performance.navigation.TYPE_BACK_FORWARD) {
  alert('hello world');
}

Ini adalah satu-satunya solusi yang berfungsi untuk saya (ini bukan situs web onsite). Ini bekerja dengan Chrome, Firefox, dan Safari.


1
window.performance.navigation telah ditinggalkan. Berikut adalah docs developer.mozilla.org/en-US/docs/Web/API/Performance/navigation
llaaalu

Ini berfungsi untuk saya di halaman .cshtml saya. Berhasil bekerja di Chrome dan IE. Terima kasih
Justjyde

5

Jawaban yang benar sudah ada untuk menjawab pertanyaan. Saya ingin menyebutkan baru JavaScript API PerformanceNavigationTiming , itu menggantikan performance.navigation yang sudah usang .

Kode berikut akan masuk konsol "back_forward" jika pengguna mendarat di halaman Anda menggunakan tombol kembali atau maju. Lihatlah tabel kompatibilitas sebelum menggunakannya dalam proyek Anda.

var perfEntries = performance.getEntriesByType("navigation");
for (var i = 0; i < perfEntries.length; i++) {
    console.log(perfEntries[i].type);
}

Ini bukan cara untuk mengetahui apakah tombol kembali diklik pada halaman ini . Itu memberitahu jika itu diklik pada halaman terakhir .
Seph Reed

Tapi kebetulan itu adalah jawaban yang sebenarnya saya cari karena masalah yang saya dapatkan adalah setelah saya menekan tombol kembali dan tidak sebelum berarti saya dapat membangun beberapa kode respons yang menghentikan masalah permintaan duplikat saya. Terima kasih.
Andrew

1
Ini mungkin tidak menjawab pertanyaan, tetapi persis apa yang saya butuhkan! Yang bagus - Saya tidak tahu ini ...
Hogsmill

4

Browser: https://jsfiddle.net/Limitlessisa/axt1Lqoz/

Untuk kontrol seluler: https://jsfiddle.net/Limitlessisa/axt1Lqoz/show/

$(document).ready(function() {
  $('body').on('click touch', '#share', function(e) {
    $('.share').fadeIn();
  });
});

// geri butonunu yakalama
window.onhashchange = function(e) {
  var oldURL = e.oldURL.split('#')[1];
  var newURL = e.newURL.split('#')[1];

  if (oldURL == 'share') {
    $('.share').fadeOut();
    e.preventDefault();
    return false;
  }
  //console.log('old:'+oldURL+' new:'+newURL);
}
.share{position:fixed; display:none; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,.8); color:white; padding:20px;
<!DOCTYPE html>
<html>

<head>
    <title>Back Button Example</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

</head>

<body style="text-align:center; padding:0;">
    <a href="#share" id="share">Share</a>
    <div class="share" style="">
        <h1>Test Page</h1>
        <p> Back button press please for control.</p>
    </div>
</body>

</html>


Ini tidak menjawab pertanyaan yang diajukan. Pertanyaannya adalah tentang mendeteksi penggunaan tombol kembali di-halaman vs tombol kembali asli browser.
Xarus

2
Pertanyaan ditandai javascript, bukan jquery.
Skwny

3

Inilah pendapat saya. Asumsinya adalah, ketika URL berubah tetapi tidak ada klik di dalam yang documentterdeteksi, itu adalah browser kembali (ya, atau meneruskan). Klik pengguna direset setelah 2 detik untuk membuatnya berfungsi pada halaman yang memuat konten melalui Ajax:

(function(window, $) {
  var anyClick, consoleLog, debug, delay;
  delay = function(sec, func) {
    return setTimeout(func, sec * 1000);
  };
  debug = true;
  anyClick = false;
  consoleLog = function(type, message) {
    if (debug) {
      return console[type](message);
    }
  };
  $(window.document).click(function() {
    anyClick = true;
    consoleLog("info", "clicked");
    return delay(2, function() {
      consoleLog("info", "reset click state");
      return anyClick = false;
    });
  });
  return window.addEventListener("popstate", function(e) {
    if (anyClick !== true) {
      consoleLog("info", "Back clicked");
      return window.dataLayer.push({
        event: 'analyticsEvent',
        eventCategory: 'test',
        eventAction: 'test'
      });
    }
  });
})(window, jQuery);

2

Saya dapat menggunakan beberapa jawaban di utas ini dan lainnya untuk membuatnya berfungsi di IE dan Chrome / Edge. history.pushState bagi saya tidak didukung di IE11.

if (history.pushState) {
    //Chrome and modern browsers
    history.pushState(null, document.title, location.href);
    window.addEventListener('popstate', function (event) {
        history.pushState(null, document.title, location.href);
    });
}
else {
    //IE
    history.forward();
}

Baru saja mencobanya di Chrome 78.0.3904.70 (Pembuatan Resmi) (64-bit) dan tidak berhasil.
Jeffz

2

Komponen lengkap dapat diimplementasikan hanya jika Anda mendefinisikan ulang API (mengubah metode objek 'sejarah') Saya akan membagikan kelas yang baru saja ditulis. Diuji hanya pada Chrome dan Mozilla. Mendukung HTML5 dan ECMAScript5-6

class HistoryNavigation {
    static init()
    {
        if(HistoryNavigation.is_init===true){
            return;
        }
        HistoryNavigation.is_init=true;

        let history_stack=[];
        let n=0;
        let  current_state={timestamp:Date.now()+n};
        n++;
        let init_HNState;
        if(history.state!==null){
            current_state=history.state.HNState;
            history_stack=history.state.HNState.history_stack;
            init_HNState=history.state.HNState;
        } else {
            init_HNState={timestamp:current_state.timestamp,history_stack};
        }
        let listenerPushState=function(params){
            params=Object.assign({state:null},params);
            params.state=params.state!==null?Object.assign({},params.state):{};
            let h_state={ timestamp:Date.now()+n};
            n++;
            let key = history_stack.indexOf(current_state.timestamp);
            key=key+1;
            history_stack.splice(key);
            history_stack.push(h_state.timestamp);
            h_state.history_stack=history_stack;
            params.state.HNState=h_state;
            current_state=h_state;
            return params;
        };
        let listenerReplaceState=function(params){
            params=Object.assign({state:null},params);
            params.state=params.state!==null?Object.assign({},params.state):null;
            let h_state=Object.assign({},current_state);
            h_state.history_stack=history_stack;
            params.state.HNState=h_state;
            return params;
        };
        let desc=Object.getOwnPropertyDescriptors(History.prototype);
        delete desc.constructor;
        Object.defineProperties(History.prototype,{

            replaceState:Object.assign({},desc.replaceState,{
                value:function(state,title,url){
                    let params={state,title,url};
                    HistoryNavigation.dispatchEvent('history.state.replace',params);
                    params=Object.assign({state,title,url},params);
                    params=listenerReplaceState(params);
                    desc.replaceState.value.call(this,params.state,params.title,params.url);
                }
            }),
            pushState:Object.assign({},desc.pushState,{
                value:function(state,title,url){
                    let params={state,title,url};
                    HistoryNavigation.dispatchEvent('history.state.push',params);
                    params=Object.assign({state,title,url},params);
                    params=listenerPushState(params);
                    return desc.pushState.value.call(this, params.state, params.title, params.url);
                }
            })
        });
        HistoryNavigation.addEventListener('popstate',function(event){
            let HNState;
            if(event.state==null){
                HNState=init_HNState;
            } else {
                HNState=event.state.HNState;
            }
            let key_prev=history_stack.indexOf(current_state.timestamp);
            let key_state=history_stack.indexOf(HNState.timestamp);
            let delta=key_state-key_prev;
            let params={delta,event,state:Object.assign({},event.state)};
            delete params.state.HNState;
            HNState.history_stack=history_stack;
            if(event.state!==null){
                event.state.HNState=HNState;
            }
            current_state=HNState;
            HistoryNavigation.dispatchEvent('history.go',params);
        });

    }
    static addEventListener(...arg)
    {
        window.addEventListener(...arg);
    }
    static removeEventListener(...arg)
    {
        window.removeEventListener(...arg);
    }
    static dispatchEvent(event,params)
    {
        if(!(event instanceof Event)){
            event=new Event(event,{cancelable:true});
        }
        event.params=params;
        window.dispatchEvent(event);
    };
}
HistoryNavigation.init();

// exemple

HistoryNavigation.addEventListener('popstate',function(event){
    console.log('Will not start because they blocked the work');
});
HistoryNavigation.addEventListener('history.go',function(event){
    event.params.event.stopImmediatePropagation();// blocked popstate listeners
    console.log(event.params);
    // back or forward - see event.params.delta

});
HistoryNavigation.addEventListener('history.state.push',function(event){
    console.log(event);
});
HistoryNavigation.addEventListener('history.state.replace',function(event){
    console.log(event);
});
history.pushState({h:'hello'},'','');
history.pushState({h:'hello2'},'','');
history.pushState({h:'hello3'},'','');
history.back();

    ```

Pendekatan yang bagus! Terima kasih telah menambahkan ini (Saya masih merasa luar biasa bahwa ada percakapan di utas ini setelah bertahun-tahun)
Xarus

Ini tampaknya merupakan mekanisme pelacakan riwayat yang cerdas. Namun, kode ini tidak dicommentasikan, sehingga cukup sulit untuk diikuti. Jika seseorang menavigasi jauh dari halaman saat ini, kode tidak akan mendeteksi tombol yang ditekan, yang tidak menjawab pertanyaan asli "Bagaimana Anda secara definitif mendeteksi apakah pengguna telah menekan tombol kembali di browser atau tidak?" Penekanan tombol dan pelacakan riwayat adalah dua hal yang berbeda walaupun satu dapat memicu yang lainnya. Kenyataan masih ada percakapan menunjukkan kelemahan utama dalam desain browser web.
CubicleSoft

1

Document.mouseover tidak berfungsi untuk IE dan FireFox. Namun saya sudah mencoba ini:

$(document).ready(function () {
  setInterval(function () {
    var $sample = $("body");
    if ($sample.is(":hover")) {
      window.innerDocClick = true;
    } else {
      window.innerDocClick = false;
    }
  });

});

window.onhashchange = function () {
  if (window.innerDocClick) {
    //Your own in-page mechanism triggered the hash change
  } else {
    //Browser back or forward button was pressed
  }
};

Ini berfungsi untuk Chrome dan IE dan bukan FireFox. Masih bekerja untuk memperbaiki FireFox. Cara mudah untuk mendeteksi klik tombol Kembali / Maju dari browser dipersilahkan, tidak terutama di JQuery tetapi juga AngularJS atau Javascript biasa.


1
 <input style="display:none" id="__pageLoaded" value=""/>


 $(document).ready(function () {
        if ($("#__pageLoaded").val() != 1) {

            $("#__pageLoaded").val(1);


        } else {
            shared.isBackLoad = true;
            $("#__pageLoaded").val(1);  

            // Call any function that handles your back event

        }
    });

Kode di atas bekerja untuk saya. Pada browser seluler, ketika pengguna mengklik tombol kembali, kami ingin mengembalikan status halaman sesuai kunjungan sebelumnya.


Kode di atas berfungsi untuk saya, pada peramban seluler, ketika pengguna mengeklik tombol kembali, kami ingin memulihkan status laman sesuai kunjungan sebelumnya
Ashwin

0

Saya menyelesaikannya dengan melacak acara asli yang memicu hashchange(baik itu gesekan, klik atau roda), sehingga acara tersebut tidak akan keliru dengan pendaratan sederhana di halaman, dan menggunakan bendera tambahan di masing-masing binding acara saya. Peramban tidak akan mengatur bendera lagi falseketika menekan tombol kembali:

var evt = null,
canGoBackToThePast = true;

$('#next-slide').on('click touch', function(e) {
    evt = e;
    canGobackToThePast = false;
    // your logic (remember to set the 'canGoBackToThePast' flag back to 'true' at the end of it)
}

-21

Saya mencoba opsi di atas tetapi tidak ada yang bekerja untuk saya. Ini solusinya

if(window.event)
   {
        if(window.event.clientX < 40 && window.event.clientY < 0)
        {
            alert("Browser back button is clicked...");
        }
        else
        {
            alert("Browser refresh button is clicked...");
        }
    }

Lihat tautan ini http://www.codeproject.com/Articles/696526/Solution-to-Browser-Back-Button-Click-Event-Handli untuk rincian lebih lanjut


8
@MunamYousuf Cukup jelas mengapa kode ini tidak benar-benar berfungsi. Ini melacak tombol kembali browser tetapi tombol berada di luar viewport, maka koordinat clientX dan clientY seharusnya tidak dapat diperoleh. Dengan asumsi itu berfungsi, bagaimana dengan iOS? Tombol kembali ada di bagian bawah ... Juga cara penulisannya sangat tidak efisien, itu merupakan persyaratan untuk setiap peristiwa yang dipicu ... Ya Tuhan, sangat buruk sehingga saya akan memberikan suaranya.
James Wong - Reinstate Monica

Ini menghebohkan! mengapa kamu melakukan hal seperti itu? : / dan itu tidak akan berfungsi seperti yang dikatakan James.
msqar
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.