Pengenalan Web Storage

Selamat datang di modul Web Storage! Pada modul sebelumnya kita telah mempelajari, menggunakan, serta mengimplementasikan Object Browser Model, Document Object Model, dan Event. Melalui ketiga hal tersebut, kita bisa membuat tampilan halaman web menjadi lebih dinamis mengikuti interaksi dari user. Walau begitu, bagaimana jika kita ingin menyimpan data, seperti input dari user yang dapat diakses oleh kode JavaScript ketika diperlukan?

Jika ingin menerapkan fitur penyimpanan data, kebanyakan dari kita langsung tertuju pada satu istilah bernama database. Untungnya, kita tidak perlu membuat database sendiri, melainkan sudah didukung oleh browser untuk menyimpan data di "lokasi" khusus yang merupakan fitur bawaan dari browser. Fitur bawaan yang dimaksud adalah Web Storage.

Kita akan menyelami fungsi web storage lebih dalam serta mengimplementasikannya pada sebuah halaman web. Sehingga, di akhir modul ini diharapkan Anda dapat:

  • Mengetahui apa itu web storage.
  • Mengetahui fungsi dari web storage.
  • Mengetahui macam-macam web storage, yakni local storage, dan session storage serta cara penggunaannya.
  • Mengetahui bagaimana menambahkan, memodifikasi, dan menghapus data ke web storage.
  • Mengetahui cara menyimpan data dengan struktur kompleks ke dalam web storage, seperti JSON.

Pengertian dan Fungsi Web Storage

Apa itu web storage? Web storage adalah salah satu Web API (perantara agar kode JavaScript bisa "berkomunikasi" dengan browser) yang dapat menyimpan data secara lokal pada sisi client (disimpan secara lokal pada perangkat kita). Berbeda dengan objek atau array, data yang disimpan pada objek atau array JavaScript bersifat sementara. Ia akan hilang jika terjadi reload atau pergantian URL pada browser. Sedangkan data yang disimpan pada Web Storage akan bertahan lebih lama karena data tersimpan dalam storage browser.

Data yang tersimpan dalam web storage tersedia berdasarkan domain. Artinya, data pada suatu domain web hanya dapat diakses oleh domain itu sendiri. Web storage dapat menampung sebesar 10MB untuk satu domain.

Pada umumnya, kita menganggap jika mengakses sebuah halaman web, data yang dibutuhkan akan dikirim dari database milik server web yang kita akses.


Tetapi tahukah Anda, bahwa tidak semua data harus diambil terus menerus dari database ketika kita mengakses halaman web tersebut? Jika proses pengambilan data dilakukan terus menerus dari database setiap kali mengakses halaman web, apakah justru malah tidak efisien?


Karena kita mengunduh data yang sama berulang kali? Di sinilah web storage memiliki kelebihan. Mari simak beberapa contoh kasus di mana web storage lebih cocok digunakan:

  • Menyimpan data dalam bentuk string yang dihasilkan oleh sebuah halaman web agar bisa diakses secara offline.
  • Menyimpan pengaturan preferensi pribadi untuk sebuah halaman web contohnya seperti tata letak dan tema warna halaman web.

Bayangkan jika pada skenario-skenario di atas tidak menggunakan web storage. Kita perlu mengunduh secara terus menerus untuk data yang cenderung sama. Boros sekali, bukan? Belum lagi pengaruhnya terhadap lamanya waktu yang dibutuhkan untuk menampilkan web. Terdapat 2 jenis web storage yang tersedia yang akan kita bahas berikutnya!


Macam-Macam Web Storage dan Cara Mengetahui Dukungan Web Storage

Web storage terbagi menjadi 2 yakni local storage dan session storage. Sebelum menggunakan Web Storage baik sessionStorage atau localStorage, kita perlu memastikan browser support terhadap fitur tersebut, dengan cara menjalankan perintah berikut pada console browser:

typeof(Storage);

Output yang dihasilkan semestinya tidak bertuliskan "undefined".


Seharusnya browser yang ada saat ini sudah mendukung kedua fitur tersebut, seperti Google Chrome dan Mozilla Firefox.

Data yang tersimpan dalam sessionStorage atau localStorage adalah nilai dengan tipe data primitif seperti number, boolean, atau string. Namun, kita juga dapat menyimpan data berbentuk JavaScript objek dengan mengubahnya ke dalam string (JSON) terlebih dahulu, begitu pula sebaliknya ketika kita ingin menggunakan datanya kembali.

Metode yang dapat digunakan untuk menyimpan dan mengakses data pada storage adalah key-value. Di sini key menjadi sebuah kunci untuk mengakses data pada storage. Ibarat sebuah loker, jika Anda ingin menyimpan sebuah benda pasti memerlukan kunci unik untuk loker tersebut. Sama halnya jika Anda ingin mengambil kembali barang tersebut, yakni membukanya dengan kunci yang tepat.

Data yang disimpan pada Web Storage dapat kita lihat menggunakan DevTools pada tabApplication (Google Chrome) atau tabStorage (Mozilla Firefox).


Data Local Storage dan Session Storage pada Google Chrome

Data Local Storage dan Session Storage pada Mozilla Firefox

Local Storage

Tipe storage yang pertama adalah local storage yang digunakan untuk menyimpan data tanpa ada batasan waktu. Data yang disimpan tidak akan hilang bila browser atau tabs browser ditutup kecuali jika kita menghapusnya.

Untuk menggunakan local storage, kita harus mengaksesnya melalui objek yang bernama "localStorage". Kemudian untuk menyimpan datanya, kita harus menggunakan method setItem() dari objek tersebut.

Method setItem() membutuhkan 2 parameter, di mana parameter pertama adalah key dan yang kedua adalah value yang ingin kita simpan. Kemudian untuk mengaksesnya kita memerlukan method getItem() yang membutuhkan 1 parameter yaitu nilai key yang kita gunakan untuk menyimpan data. Seperti yang disebutkan sebelumnya, data pada local storage bisa dihapus dengan method removeItem() yang menerima 1 parameter yakni nilai key yang kita gunakan untuk menyimpan sebuah objek.

Berikut penerapan dari local storage:

<!DOCTYPE html>
<html>
<head>
  <title>Web Storage World</title>
  <style>
    .contents {
      position: fixed;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      border: 2px solid black;
      padding: 15px;
    }
    
    #generateButton {
      margin-top: 5px;
      margin-bottom: 15px;
    }
  </style>
</head>
<body>
<div class="contents" align="center">
  <button id="incrementButton">Tambah nilai</button>
  <button id="clear">Hapus storage</button>
  <h3>Kamu sudah menekan tombol di atas sebanyak:</h3>
  <h2 id="count">0</h2>
</div>
 
<script>
  const localStorageKey = 'PRESS_FREQUENCY';
  
  // pengecekan apakah data localStorage dengan key count tersedia atau belum
  if (typeof (Storage) !== 'undefined') {
    if (localStorage.getItem(localStorageKey) === null) {
      // Jika item pada local storage belum ada 
      // maka akan diberi nilai awal yakni 0
      localStorage.setItem(localStorageKey, 0);
    }
    const Incrementbutton = document.querySelector('#incrementButton');
    const clearButton = document.querySelector('#clear');
    const countDisplay = document.querySelector('#count');
    
    // memberikan nilai item dari local storage
    countDisplay.innerText = localStorage.getItem(localStorageKey);
    
    // mengupdate nilai dari item local storage jika tombol ditekan
    Incrementbutton.addEventListener('click', function () {
      let count = localStorage.getItem(localStorageKey);
      count++;
      localStorage.setItem(localStorageKey, count);
      countDisplay.innerText = localStorage.getItem(localStorageKey);
    });
    
    // memberikan nilai 0 ke tampilan karena di-reset dan menghapus item
    clearButton.addEventListener('click', function () {
      localStorage.removeItem(localStorageKey);
      countDisplay.innerText = 0;
    });
  } else {
    alert('Browser yang Anda gunakan tidak mendukung Web Storage');
  }
</script>
</body>
</html>

Pada tag <div> terdapat atribut align dengan nilai center yang berfungsi untuk memosisikan seluruh child element berada di tengah-tengah parent element-nya. Namun, penggunaan atribut ini sudah usang (deprecated) sehingga penggunaannya sudah tidak di rekomendasikan kembali. Solusinya adalah menuliskan aturan styling menggunakan CSS.


Pada GIF di atas kita bisa mengetahui bahwa data yang disimpan pada local storage akan bertahan walaupun jendela browser atau tab browser ditutup. Untuk menghapus datanya, tekan tombol "Hapus Storage" di mana tombol tersebut akan memanggil method localStorage.removeItem().


Session Storage

Tipe storage yang kedua adalah Session Storage yang digunakan untuk menyimpan data sementara pada browser. Data akan hilang ketika browser atau tab browser ditutup.

Untuk menerapkan Session Storage, kita dapat menggunakan global objek sessionStorage. Method-method yang tersedia sama dengan milik objek localStorage. Kemudian untuk menyimpan data gunakan method setItem() mengakses data yang sudah dimasukkan dapat menggunakan method getItem() dan untuk menghapus item gunakan method removeItem().

Berikut contoh penerapan dari session storage:

<!DOCTYPE html>
<html>
<head>
  <title>Web Storage World</title>
  <style>
    .contents {
      position: fixed;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      border: 2px solid black;
      padding: 15px;
    }
    
    #generateButton {
      margin-top: 5px;
      margin-bottom: 15px;;
    }
  </style>
</head>
<body>
<div class="contents" align="center">
  <h1>Session Storage</h1>
  <button id="incrementButton">Tambah nilai</button>
  <button id="clear">Hapus storage</button>
  <h3>Kamu sudah menekan tombol di atas sebanyak:</h3>
  <h2 id="count">0</h2>
</div>
 
<script>
  const sessionStorageKey = 'PRESS_FREQUENCY';
  
  // pengecekan apakah data sessionStorage dengan key count tersedia atau belum
  if (typeof (Storage) !== 'undefined') {
    if (sessionStorage.getItem(sessionStorageKey) === null) {
      // Jika item pada session storage belum ada
      // maka akan atur dengan nilai awal yakni 0
      sessionStorage.setItem(sessionStorageKey, 0);
    }
    
    const Incrementbutton = document.querySelector('#incrementButton');
    const clearButton = document.querySelector('#clear');
    const countDisplay = document.querySelector('#count');
    
    // memberikan nilai item dari session storage
    countDisplay.innerText = sessionStorage.getItem(sessionStorageKey);
    
    // mengupdate nilai dari item session storage jika tombol ditekan
    Incrementbutton.addEventListener('click', function () {
      let count = sessionStorage.getItem(sessionStorageKey);
      count++;
      sessionStorage.setItem(sessionStorageKey, count);
      countDisplay.innerText = sessionStorage.getItem(sessionStorageKey);
    });
    
    // memberikan nilai 0 ke tampilan karena di-reset dan menghapus item
    clearButton.addEventListener('click', function () {
      sessionStorage.removeItem(sessionStorageKey);
      countDisplay.innerText = 0;
    });
  } else {
    alert('Browser yang Anda gunakan tidak mendukung Web Storage');
  }
</script>
</body>
</html>


Bisa kita lihat bahwa data yang tersimpan dalam session storage akan bertahan jika terjadi reload/refresh pada browser. Namun, data tersebut akan hilang apabila tab browser atau browser itu sendiri ditutup.

Bagaimana? Penggunakan Web Storage cukup mudah, bukan? Selanjutnya kita akan menerapkan local storage dan session storage pada Web Inventory. Yuk, kita lanjut ke materi berikutnya.


Implementasi Web Storage


Menambahkan Item Storage

Untuk materi ini dan beberapa materi berikutnya, kita akan belajar bagaimana cara menambah, mengakses, memodifikasi, dan menghapus data di web storage. Semua proses tersebut akan kita gunakan untuk membuat sebuah permainan tebak angka sederhana di halaman web dengan tampilan sebagai berikut:


Dengan menggunakan web storage, kita bisa menyimpan berbagai data saat user memainkan permainan tebak angka, seperti berapa banyak user telah menebak, berapa kali user berhasil menebak dengan benar, dan sebagainya.

Mari kita mulai menerapkan kode untuk menambahkan item storage pada session storage dan local storage terlebih dahulu. Pertama buat terlebih dahulu sebuah berkas HTML dengan isi sebagai berikut:

<!DOCTYPE html>
<html>
<head>
  <title>Web Storage Game</title>
  <style>
    hr {
      height: 2px;
      color: black;
      background-color: black;
      width: 95%;
    }
    
    li {
      margin-top: 1%;
      margin-bottom: 1%;
      font-size: 120%;
    }
    
    .contents {
      margin-left: 5%;
      margin-right: 5%;
    }
    
    .child-content {
      border: 2px solid black;
      padding-bottom: 2%;
      margin-bottom: 1%;
    }
    
    .button {
      padding-left: 1%;
      padding-right: 1%;
      font-size: 140%;
    }
    
    .answer-button {
      margin-left: 2%;
      margin-right: 2%;
      padding: 2%;
      font-size: 140%;
      width: 12%;
    }
    
    #total-stats {
      float: right;
      width: 40%;
      height: 50%;
    }
    
    #gameboard {
      float: left;
      width: 55%;
      height: 40%;
    }
    
    #session-stats {
      float: left;
      margin-top: 1%;
      width: 55%;
    }
    
    #instructions {
      float: right;
      margin-top: 1%;
      width: 40%;
    }
    
    #instructions-list {
      margin-left: 1%;
      margin-right: 1%;
    }
  </style>
</head>
<body align="center">
<h1 align="center">Permainan Tebak Angka '123'</h1>
<div class="contents">
  <div class="child-content" id="total-stats" align="center">
    <h2>Local Stats</h2>
    <hr>
    <div id="all-local-stats">
      <div class="local-stat-item">
        <h3>Jumlah permainan yang berhasil dimenangkan:</h3>
        <h3 id="local-total-victory-field"></h3>
      </div>
      <div class="local-stat-item">
        <h3>Jumlah tebakan salah terbanyak sekali main:</h3>
        <h3 id="local-maximum-attempt-field"></h3>
      </div>
      <button class="button" id="destroy-data-button">Hapus semua data</button>
    </div>
  </div>
  
  <div class="child-content" id="gameboard" align="center">
    <h2>Game Board</h2>
    <hr>
    <div id="before-game-display">
      <h3>Tekan tombol "Play" di bawah jika sudah siap</h3>
      <button class="button" id="play-button">Bermain</button>
    </div>
    <div id="during-game-display" hidden>
      <h3>Silahkan menebak angka!</h3>
      <h3>Jawaban Anda:</h3>
      <h1>"<u><span id="session-user-answer-field"></span></u>"</h1>
      <button class="answer-button" id="answer-1-button">1</button>
      <button class="answer-button" id="answer-2-button">2</button>
      <button class="answer-button" id="answer-3-button">3</button>
      <h3>"<span id="session-user-wrong-answer-field"></span>" bukanlah jawabannya...</h3><br><br>
    </div>
    <div id="after-game-display" hidden>
      <h3>Selamat Tebakan Anda benar!</h3>
      <h3>Jawabannya adalah: </h3>
      <h1>"<span id="session-true-answer-field"></span>"</h1>
      <h3><i></i>Refresh halaman ini untuk mencoba lagi</h3>
    </div>
  </div>
  
  <div class="child-content" id="session-stats" align="center">
    <h2>Game Session Stats</h2>
    <hr>
    <div id="all-session-stats">
      <div class="session-stat-item">
        <h3>Jumlah Tebakan Salah:</h3>
        <h3 id="session-user-attempts-amount-field"></h3>
      </div>
    </div>
  </div>
  
  <div class="child-content" id="instructions">
    <h2 align="center">Cara bermain</h2>
    <hr>
    <h3 align="center">Selamat datang ke permainan tebak angka, berikut instruksi-nya:</h3>
    <ul id="instructions-list" align="left">
      <li>Anda harus mencari kombinasi angka yang terdiri dari angka "1", "2", dan "3",
        misalnya "231" dan "123".
      </li>
      <li>Angka yang ditebak hanya memiliki panjang 3 karakter saja,
        sehingga kombinasi seperti "1231" tidak akan menjadi jawaban.
      </li>
      <li>Sebuah angka hanya akan muncul sekali dan hanya sekali,
        sehingga kombinasi seperti "223" maupun "111" tidak akan menjadi jawaban.
      </li>
      <li><i>Have fun and don't cheat!</i></li>
    </ul>
  </div>
</div>
 
<script>
  //inisialiasi variabel untuk menampung elemen dokumen
  const localTotalVictoryField = document.getElementById('local-total-victory-field');
  const localMaximumAttemptField = document.getElementById('local-maximum-attempt-field');
  const destroyDataButton = document.getElementById('destroy-data-button');
  const playButton = document.getElementById('play-button');
  const beforeGameDisplay = document.getElementById('before-game-display');
  const duringGameDisplay = document.getElementById('during-game-display');
  const afterGameDisplay = document.getElementById('after-game-display');
  const answerButton1 = document.getElementById('answer-1-button');
  const answerButton2 = document.getElementById('answer-2-button');
  const answerButton3 = document.getElementById('answer-3-button');
  const sessionUserAnswerField = document.getElementById('session-user-answer-field');
  const sessionUserWrongAnswerField = document.getElementById('session-user-wrong-answer-field');
  const sessionTrueAnswerField = document.getElementById('session-true-answer-field');
  const sessionUserAttemptsField = document.getElementById('session-user-attempts-amount-field');
  
  //inisialisasi fungsi untuk menghasilkan jawaban permainan
  function getAnswer() {
    let answer = '123'.split('');
    for (let i = 0; i < answer.length; i++) {
      let j = Math.floor(Math.random() * (i + 1));
      let tmp = answer[i];
      answer[i] = answer[j];
      answer[j] = tmp;
    }
    return answer.join('');
  }
  
  //inisialiasi key untuk session storage
  const sessionAnswerKey = 'SESSION_ANSWER';
  const sessionUserAttemptsKey = 'SESSION_USER_ATTEMPTS';
  const sessionUserIsPlayingKey = 'SESSION_USER_IS_PLAYING';
  
  //inisialisasi key untuk local storage
  const localTotalVictoryKey = 'LOCAL_TOTAL_VICTORIES_PLAYED';
  const localMaximumAttemptsKey = 'LOCAL_MAXIMUM_ATTEMPTS';
</script>
</body>
</html>

Di dalam elemen <script>, sudah terdapat beberapa kode antara lain:

  • Variabel-variabel yang bisa kita gunakan untuk berinteraksi dengan DOM.
  • Sebuah fungsi getAnswer() yang berfungsi untuk menghasilkan kombinasi dari angka "1", "2", dan "3". Kombinasi tersebut akan menjadi jawaban untuk permainan tebak angka.
  • Variabel-variabel yang menampung key untuk session storage maupun local storage.

Jika, kita buka halaman web tersebut, layout-nya akan tampil sebagai berikut:


Jika tulisan pada halaman web tampak terlalu besar. Coba zoom out untuk mengecilkan ukuran font yang ditampilkan.

Untuk membuat item pada session storage maupun local storage, kita bisa menggunakan method setItem() dari masing-masing jenis storage.

// untuk session storage
sessionStorage.setItem(<namaKey>, <nilaiAwal>);
 
// untuk local storage
localStorage.setItem(<namaKey>, <nilaiAwal>);

Dari berkas HTML di atas, kita sudah menambahkan key yang digunakan untuk mengakses item terlebih dahulu pada session storage maupun local storage.

Selanjutnya mari kita tambahkan sebuah event listener "load" pada objek window. Sehingga, ketika berkas HTML sudah selesai ditampilkan, kita akan mengecek apakah item pada session storage maupun local storage sudah dibuat atau belum:

window.addEventListener('load', function () {
  if (typeof (Storage) !== 'undefined') {
    // inisialisasi semua item web storage yang kita akan gunakan jika belum ada
    if (sessionStorage.getItem(sessionAnswerKey) === null) {
      sessionStorage.setItem(sessionAnswerKey, '');
    }
    if (sessionStorage.getItem(sessionUserAttemptsKey) === null) {
      sessionStorage.setItem(sessionUserAttemptsKey, 0);
    }
    if (sessionStorage.getItem(sessionUserIsPlayingKey) === null) {
      sessionStorage.setItem(sessionUserIsPlayingKey, false);
    }
    if (localStorage.getItem(localTotalVictoryKey) === null) {
      localStorage.setItem(localTotalVictoryKey, 0);
    }
    if (localStorage.getItem(localMaximumAttemptsKey) === null) {
      localStorage.setItem(localMaximumAttemptsKey, 0);
    }
  } else {
    alert('Browser yang Anda gunakan tidak mendukung Web Storage');
  }
});

Kode di atas dimulai dari proses memeriksa apakah browser yang digunakan mendukung fitur web storage. Jika tidak, ia akan menampilkan sebuah alert dialog box. Sedangkan jika browser mendukung fitur web storage, kode JavaScript akan mengecek apakah setiap item ada atau tidak. Jika belum ada, buat item dengan memanggil sessionStorage.setItem() untuk session storage dan localStorage.setItem() untuk local storage.

Pastikan kode dalam elemen script kita berbentuk seperti ini:

<script>
  //inisialisasi variabel untuk menampung elemen dokumen
  const localTotalVictoryField = document.getElementById('local-total-victory-field');
  const localMaximumAttemptField = document.getElementById('local-maximum-attempt-field');
  const destroyDataButton = document.getElementById('destroy-data-button');
  const playButton = document.getElementById('play-button');
  const beforeGameDisplay = document.getElementById('before-game-display');
  const duringGameDisplay = document.getElementById('during-game-display');
  const afterGameDisplay = document.getElementById('after-game-display');
  const answerButton1 = document.getElementById('answer-1-button');
  const answerButton2 = document.getElementById('answer-2-button');
  const answerButton3 = document.getElementById('answer-3-button');
  const sessionUserAnswerField = document.getElementById('session-user-answer-field');
  const sessionUserWrongAnswerField = document.getElementById('session-user-wrong-answer-field');
  const sessionTrueAnswerField = document.getElementById('session-true-answer-field');
  const sessionUserAttemptsField = document.getElementById('session-user-attempts-amount-field');
  
  //inisialisasi fungsi untuk menghasilkan jawaban permainan
  function getAnswer() {
    let answer = '123'.split('');
    for (let i = 0; i < answer.length; i++) {
      let j = Math.floor(Math.random() * (i + 1));
      let tmp = answer[i];
      answer[i] = answer[j];
      answer[j] = tmp;
    }
    return answer.join('');
  }
  
  //inisialisasi key untuk session storage
  const sessionAnswerKey = 'SESSION_ANSWER';
  const sessionUserAttemptsKey = 'SESSION_USER_ATTEMPTS';
  const sessionUserIsPlayingKey = 'SESSION_USER_IS_PLAYING';
  
  //inisialisasi key untuk local storage
  const localTotalVictoryKey = 'LOCAL_TOTAL_VICTORIES_PLAYED';
  const localMaximumAttemptsKey = 'LOCAL_MAXIMUM_ATTEMPTS';
  
  window.addEventListener('load', function () {
    if (typeof (Storage) !== 'undefined') {
      // (MATERI 1)
      // inisialisasi semua item web storage yang kita akan gunakan jika belum ada
      if (sessionStorage.getItem(sessionAnswerKey) === null) {
        sessionStorage.setItem(sessionAnswerKey, '');
      }
      if (sessionStorage.getItem(sessionUserAttemptsKey) === null) {
        sessionStorage.setItem(sessionUserAttemptsKey, 0);
      }
      if (sessionStorage.getItem(sessionUserIsPlayingKey) === null) {
        sessionStorage.setItem(sessionUserIsPlayingKey, false);
      }
      if (localStorage.getItem(localTotalVictoryKey) === null) {
        localStorage.setItem(localTotalVictoryKey, 0);
      }
      if (localStorage.getItem(localMaximumAttemptsKey) === null) {
        localStorage.setItem(localMaximumAttemptsKey, 0);
      }
    } else {
      alert('Browser yang Anda gunakan tidak mendukung Web Storage');
    }
  });
</script>

Melalui kode di atas, kita telah membuat 3 item session storage dan 2 item local storage. Sekilas halaman permainan tebak angka kita masih memiliki rupa yang sama.


Bagaimana jika kita ingin memberikan nilai awal yakni "0" untuk semua stats pada Game Session Stats dan Local Stats? Kita harus mengakses konten innerText pada masing-masing elemen dan memberikan nilai awal dari item session storage maupun local storage yang baru dibuat. Untuk materi berikutnya, kita akan menambahkan kode untuk mengakses nilai pada item-item tersebut dan memberikan nilainya ke konten innerText elemen-elemen milik Game Session Stats dan Local Stats.

Sudah tidak sabar, bukan? Yuk, kita lanjutkan ke materi berikutnya.


Mengakses Item Storage

Untuk mengakses item pada session storage maupun local storage, kita bisa menggunakan method getItem() dari masing-masing jenis storage:

// untuk session storage
sessionStorage.getItem(<namaKey>);
 
// untuk local storage
localStorage.getItem(<namaKey>);

Kita akan menggunakan method tersebut untuk mengakses item storage yang sudah dibuat pada materi sebelumnya dan menampilkannya di halaman web.

Jika dilihat dari tampilan halaman web permainan tebak angka saat ini, di sana tidak ada nilai awal dari stats pada Game Session Stats dan Local Stats di lokasi yang ditandai dengan tanda tanya (?) merah seperti di bawah ini.


Dalam fungsi addEventListener milik objek window untuk event "load" yang sudah dibuat sebelumnya, kita bisa menambahkan beberapa baris kode di bagian akhir fungsi seperti berikut:

sessionUserAttemptsField.innerText = sessionStorage.getItem(sessionUserAttemptsKey);
localTotalVictoryField.innerText = localStorage.getItem(localTotalVictoryKey);
localMaximumAttemptField.innerText = localStorage.getItem(localMaximumAttemptsKey);

Kode di atas akan menampilkan nilai yang tersimpan pada item session storage untuk elemen stats pada Game Session Stats dan item local storage untuk elemen stats pada Local Stats. Sehingga, stats akan menampilkan nilai yang tersimpan pada masing-masing item storage ketika halaman web ditampilkan.

Sekarang fungsi addEventListener milik objek window untuk event "load" memiliki isi sebagai berikut:

window.addEventListener('load', function () {
  if (typeof (Storage) !== 'undefined') {
    // (MATERI 1)
    // inisialisasi semua item web storage yang kita akan gunakan jika belum ada
    if (sessionStorage.getItem(sessionAnswerKey) === null) {
      sessionStorage.setItem(sessionAnswerKey, '');
    }
    if (sessionStorage.getItem(sessionUserAttemptsKey) === null) {
      sessionStorage.setItem(sessionUserAttemptsKey, 0);
    }
    if (localStorage.getItem(localTotalVictoryKey) === null) {
      localStorage.setItem(localTotalVictoryKey, 0);
    }
    if (localStorage.getItem(localMaximumAttemptsKey) === null) {
      localStorage.setItem(localMaximumAttemptsKey, 0);
    }
  } else {
    alert('Browser yang Anda gunakan tidak mendukung Web Storage');
  }
  //inisialisasi semua nilai field pada dokumen yang menggunakan nilai dari web storage
  sessionUserAttemptsField.innerText = sessionStorage.getItem(sessionUserAttemptsKey);
  localTotalVictoryField.innerText = localStorage.getItem(localTotalVictoryKey);
  localMaximumAttemptField.innerText = localStorage.getItem(localMaximumAttemptsKey);
});

Dengan penambahan kode tersebut, ketika kita membuka halaman web, nilai stats yang ditampilkan akan selalu muncul.


Dapat dilihat nilai elemen dari "Jumlah tebakan salah", "Jumlah kombinasi yang berhasil di tebak", dan "Jumlah tebakan salah terbanyak sekali main" memiliki nilai awal "0". Nilai "0" tersebut sudah sesuai dengan nilai yang kita masukkan ke masing-masing item storage yang telah dibuat pada materi sebelumnya.

Sejauh ini kita sudah berhasil menambahkan beserta mengakses data yang tersimpan dalam item storage. Namun, bagaimana jika sebuah event terjadi ketika nilai data pada sebuah item storage harus diganti? Hal tersebut akan kita pelajari di materi berikutnya tentang cara mengakses sebuah item storage dan mengubah nilai-nya.


Memodifikasi Item Storage

Pada materi "Menambahkan Item Storage", kita sudah menggunakan method setItem() untuk membuat sebuah item storage pada session storage maupun local storage ketika item storage dengan key yang digunakan belum ada.


Namun, jika item storage tersebut sudah ada, kita bisa gunakan method setItem() untuk memodifikasi nilai yang terdapat pada item storage tersebut sesuai dengan nilai key-nya.


Untuk mengakses item pada session storage maupun local storage, kita bisa menggunakan method setItem() dari masing-masing jenis storage:

// untuk session storage
sessionStorage.setItem(<namaKey>,<nilaiBaru>);
 
// untuk local storage
localStorage.setItem(<namaKey>,<nilaiBaru>);

Kemudian kita akan menambahkan kode-kode berikut di bagian akhir isi dari elemen <script>:

playButton.addEventListener('click', function () {
  sessionStorage.setItem(sessionAnswerKey, getAnswer());
  sessionStorage.setItem(sessionUserIsPlayingKey, true);
  beforeGameDisplay.setAttribute('hidden', true);
  duringGameDisplay.removeAttribute('hidden');
});
 
answerButton1.addEventListener('click', function () {
  sessionUserAnswerField.innerText += '1';
  if (sessionUserAnswerField.innerText.length == 3) {
    checkAnswer(sessionUserAnswerField.innerText);
  }
});
 
answerButton2.addEventListener('click', function () {
  sessionUserAnswerField.innerText += '2';
  if (sessionUserAnswerField.innerText.length == 3) {
    checkAnswer(sessionUserAnswerField.innerText);
  }
});
 
answerButton3.addEventListener('click', function () {
  sessionUserAnswerField.innerText += '3';
  if (sessionUserAnswerField.innerText.length == 3) {
    checkAnswer(sessionUserAnswerField.innerText);
  }
});
 
function checkAnswer(userGuess) {
  const answer = sessionStorage.getItem(sessionAnswerKey);
  if (userGuess == answer) {
    duringGameDisplay.setAttribute('hidden', true);
    afterGameDisplay.removeAttribute('hidden');
    sessionTrueAnswerField.innerText = answer;
    updateScore();
  } else {
    const previousAttemptAmount = parseInt(sessionStorage.getItem(sessionUserAttemptsKey));
    sessionStorage.setItem(sessionUserAttemptsKey, previousAttemptAmount + 1);
    sessionUserAttemptsField.innerText = sessionStorage.getItem(sessionUserAttemptsKey);
    sessionUserAnswerField.innerText = '';
    sessionUserWrongAnswerField.innerText = userGuess;
  }
}
 
function updateScore() {
  const sessionAttemptsValue = parseInt(sessionStorage.getItem(sessionUserAttemptsKey));
  const localAttemptsValue = parseInt(localStorage.getItem(localMaximumAttemptsKey));
  if (sessionAttemptsValue > localAttemptsValue) {
    localStorage.setItem(localMaximumAttemptsKey, sessionAttemptsValue);
    localMaximumAttemptField.innerText = sessionAttemptsValue;
  }
  const previousTotalVictoryAmount = parseInt(localStorage.getItem(localTotalVictoryKey));
  localStorage.setItem(localTotalVictoryKey, previousTotalVictoryAmount + 1);
  localTotalVictoryField.innerText = localStorage.getItem(localTotalVictoryKey);
}
 
window.addEventListener('beforeunload', function () {
  sessionUserAnswerField.innerText = '';
  sessionUserWrongAnswerField.innerText = '';
  sessionStorage.setItem(sessionUserAttemptsKey, 0);
  sessionUserAttemptsField.innerText = sessionStorage.getItem(sessionUserAttemptsKey);
});

Tenang, walau kelihatan banyak, kita akan membahas fungsi dan masing-masing method addEventListener di atas satu per satu.

Kita mulai dari penambahan event listener pada variabel playButton yakni variabel yang memiliki referensi ke tombol yang bertuliskan "Bermain".

playButton.addEventListener('click', function () {
  sessionStorage.setItem(sessionAnswerKey, getAnswer());
  beforeGameDisplay.setAttribute('hidden', true);
  duringGameDisplay.removeAttribute('hidden');
});

Tombol ini memiliki dua fungsionalitas yakni menghasilkan angka yang harus ditebak melalui fungsi getAnswer() dan menyimpannya pada session storage dengan key sessionAnswerKey. Fungsionalitas kedua adalah mengubah layout pada kumpulan elemen "Game Board".


Elemen ini sebenarnya mengandung 3 layout berbeda, di mana hanya 1 saja yang ditampilkan berdasarkan skenario tertentu. Layout-layout disembunyikan melalui atribut hidden. Jika ingin dimunculkan, atribut tersebut perlu dihilangkan dengan method removeAttribute(), sementara layout sebelumnya disembunyikan dengan method setAttribute(). Sehingga, jika tombol "Bermain" ditekan, maka layout pada elemen "Game Board" akan berubah.


Setelah layout dari elemen "Game Board" berubah, muncul layout baru di mana salah satu konten paling mencolok adalah 3 tombol masing-masing berisi angka "1", "2", dan "3". Masing-masing tombol tersebut akan kita berikan sebuah event listener untuk event "click".

answerButton1.addEventListener('click', function () {
  sessionUserAnswerField.innerText += '1';
  if (sessionUserAnswerField.innerText.length == 3) {
    checkAnswer(sessionUserAnswerField.innerText);
  }
});
 
answerButton2.addEventListener('click', function () {
  sessionUserAnswerField.innerText += '2';
  if (sessionUserAnswerField.innerText.length == 3) {
    checkAnswer(sessionUserAnswerField.innerText);
  }
});
 
answerButton3.addEventListener('click', function () {
  sessionUserAnswerField.innerText += '3';
  if (sessionUserAnswerField.innerText.length == 3) {
    checkAnswer(sessionUserAnswerField.innerText);
  }
});

Setiap event listener dari ketiga tombol tersebut kurang lebih memiliki fungsionalitas yang sama, yakni menambahkan angka ke dalam kombinasi tebakan user berdasarkan tombol yang ditekan.


Pada setiap event listener tombol-tombol di atas, kita bisa melihat bahwa jika input tebakan dari user sudah sepanjangan 3 karakter, sebuah fungsi yang bernama checkAnswer() akan dipanggil. Karakter tebakan dari user akan dimasukkan ke fungsi tersebut sebagai parameter.

Berikutnya kita akan membahas penerapan dari fungsi checkAnswer() untuk memeriksa apakah tebakan user sudah benar atau belum. Kode fungsi tersebut akan dibuat sebagai berikut:

function checkAnswer(userGuess) {
  const answer = sessionStorage.getItem(sessionAnswerKey);
  if (userGuess == answer) {
    duringGameDisplay.setAttribute('hidden', true);
    afterGameDisplay.removeAttribute('hidden');
    sessionTrueAnswerField.innerText = answer;
    updateScore();
  } else {
    const previousAttemptAmount = parseInt(sessionStorage.getItem(sessionUserAttemptsKey));
    sessionStorage.setItem(sessionUserAttemptsKey, previousAttemptAmount + 1);
    sessionUserAttemptsField.innerText = sessionStorage.getItem(sessionUserAttemptsKey);
    sessionUserAnswerField.innerText = '';
    sessionUserWrongAnswerField.innerText = userGuess;
  }
}

Fungsi checkAnswer akan menjalankan kode yang berbeda berdasarkan kondisi apakah user berhasil menebak angka yang tepat sesuai di session storage sebelumnya.

Jika user salah menebak, stats pada Game Session Stats akan menghitung jumlah masukan tebakan yang salah. Kemudian halaman web akan menampilkan informasi tebakan kita salah.


Namun, jika tebakan user sudah sama dengan jawaban yang dihasilkan pada sistem yang tersimpan pada session storage, tampilan elemen Game Board akan berubah. Tampilan elemen gameboard akan memberitahu user bahwa ia telah menebak dengan tepat. Selain itu, stats pada local stats akan juga ikut diperbarui.


Keren, bukan? Dengan memanfaatkan local storage dan session storage, kita bisa menyimpan progress selama memainkan permainan tebak angka! Tunggu sebentar, perhatikan kode yang dijalankan ketika user menebak dengan tepat. Terdapat pemanggilan fungsi yang bernama updateScore().

Fungsi updateScore() berguna untuk memperbarui stats user pada elemen Local Stats. Isi dari updateScore sebagai berikut:

function updateScore() {
  const sessionAttemptsValue = parseInt(sessionStorage.getItem(sessionUserAttemptsKey));
  const localAttemptsValue = parseInt(localStorage.getItem(localMaximumAttemptsKey));
  if (sessionAttemptsValue > localAttemptsValue) {
    localStorage.setItem(localMaximumAttemptsKey, sessionAttemptsValue);
    localMaximumAttemptField.innerText = sessionAttemptsValue;
  }
  const previousTotalVictoryAmount = parseInt(localStorage.getItem(localTotalVictoryKey));
  localStorage.setItem(localTotalVictoryKey, previousTotalVictoryAmount + 1);
  localTotalVictoryField.innerText = localStorage.getItem(localTotalVictoryKey);
}

Ketika user berhasil menebak angka, dua stats akan diperbarui, yakni "Jumlah kombinasi yang berhasil di tebak" dan "Jumlah tebakan salah terbanyak sekali main".


Jika dalam sebuah permainan jumlah tebakan salah kita lebih banyak dari "Jumlah tebakan salah terbanyak sekali main", maka stats "Jumlah tebakan salah terbanyak sekali main" akan diperbarui. Stats untuk "Jumlah kombinasi yang berhasil di tebak" juga akan bertambah.


Kalau kita telaah kode di dalam fungsi updateScore(), hasil dari pengambilan data dari session storage dan local storage akan dimasukkan ke fungsi parseInt() terlebih dahulu sebelum dimasukkan ke sebuah variabel.

Mengapa hasil pengambilan data dari session storage dan local storage harus terlebih dahulu dimasukkan ke fungsi parseInt()? Alasannya adalah kita ingin melakukan operasi matematis pada datanya. Sama halnya seperti "Jumlah kombinasi yang berhasil di tebak" ingin kita tambah 1 ketika user berhasil menebak kombinasi angka yang tepat.

Nah, walau data yang disimpan pada local storage dan session storage berupa angka, tetapi ketika diambil tipe datanya akan dianggap sebagai string.


Jika kita tidak ubah ke dalam bentuk Number terlebih dahulu, kode akan melakukan operasional matematika pada string dan memunculkan perilaku yang tidak terduga. Coba hilangkan fungsi parseInt-nya dan jalankan kembali permainan tebak angka. Perhatikan bugs yang akan muncul ketika berhasil menebak sebuah angka.

Sebelum menutup materi kali ini, masih tersisa beberapa baris kode lagi sebagai berikut:

window.addEventListener('beforeunload', function () {
  sessionUserAnswerField.innerText = '';
  sessionUserWrongAnswerField.innerText = '';
  sessionStorage.setItem(sessionUserAttemptsKey, 0);
  sessionUserAttemptsField.innerText = sessionStorage.getItem(sessionUserAttemptsKey);
});

Dengan menambahkan event listener untuk event "beforeunload", browser kita akan menghapus dan mengonfigurasi semua nilai dari item-item session storage kembali ke nilai awal. Sehingga, jika user melakukan proses refresh/reload halaman, permainan yang belum selesai akan dihapus. Jika user ingin bermain lagi, ia harus menekan tombol "Bermain".


Keren ya, permainan tebak angka kita hampir memiliki semua fungsionalitas. Sehingga, isi elemen <script> pada berkas HTML menjadi sebagai berikut:

<script>
  //inisialiasi variabel untuk menampung elemen dokumen
  const localTotalVictoryField = document.getElementById('local-total-victory-field');
  const localMaximumAttemptField = document.getElementById('local-maximum-attempt-field');
  const destroyDataButton = document.getElementById('destroy-data-button');
  const playButton = document.getElementById('play-button');
  const beforeGameDisplay = document.getElementById('before-game-display');
  const duringGameDisplay = document.getElementById('during-game-display');
  const afterGameDisplay = document.getElementById('after-game-display');
  const answerButton1 = document.getElementById('answer-1-button');
  const answerButton2 = document.getElementById('answer-2-button');
  const answerButton3 = document.getElementById('answer-3-button');
  const sessionUserAnswerField = document.getElementById('session-user-answer-field');
  const sessionUserWrongAnswerField = document.getElementById('session-user-wrong-answer-field');
  const sessionTrueAnswerField = document.getElementById('session-true-answer-field');
  const sessionUserAttemptsField = document.getElementById('session-user-attempts-amount-field');
  
  //inisialisasi fungsi untuk menghasilkan jawaban permainan
  function getAnswer() {
    let answer = '123'.split('');
    for (let i = 0; i < answer.length; i++) {
      let j = Math.floor(Math.random() * (i + 1));
      let tmp = answer[i];
      answer[i] = answer[j];
      answer[j] = tmp;
    }
    return answer.join('');
  }
  
  //inisialiasi key untuk session storage
  const sessionAnswerKey = 'SESSION_ANSWER';
  const sessionUserAttemptsKey = 'SESSION_USER_ATTEMPTS';
  
  //inisialisasi key untuk local storage
  const localTotalVictoryKey = 'LOCAL_TOTAL_VICTORIES_PLAYED';
  const localMaximumAttemptsKey = 'LOCAL_MAXIMUM_ATTEMPTS';
  
  window.addEventListener('load', function () {
    if (typeof (Storage) !== 'undefined') {
      // (MATERI 1)
      // inisiliasi semua item web storage yang kita akan gunakan jika belum ada
      if (sessionStorage.getItem(sessionAnswerKey) === null) {
        sessionStorage.setItem(sessionAnswerKey, '');
      }
      if (sessionStorage.getItem(sessionUserAttemptsKey) === null) {
        sessionStorage.setItem(sessionUserAttemptsKey, 0);
      }
      if (localStorage.getItem(localTotalVictoryKey) === null) {
        localStorage.setItem(localTotalVictoryKey, 0);
      }
      if (localStorage.getItem(localMaximumAttemptsKey) === null) {
        localStorage.setItem(localMaximumAttemptsKey, 0);
      }
    } else {
      alert('Browser yang Anda gunakan tidak mendukung Web Storage');
    }
    // inisialiasi semua nilai field pada dokumen yang menggunakan nilai dari web storage
    sessionUserAttemptsField.innerText = sessionStorage.getItem(sessionUserAttemptsKey);
    localTotalVictoryField.innerText = localStorage.getItem(localTotalVictoryKey);
    localMaximumAttemptField.innerText = localStorage.getItem(localMaximumAttemptsKey);
  });
  
  // (MATERI 3)
  playButton.addEventListener('click', function () {
    sessionStorage.setItem(sessionAnswerKey, getAnswer());
    beforeGameDisplay.setAttribute('hidden', true);
    duringGameDisplay.removeAttribute('hidden');
  });
  
  answerButton1.addEventListener('click', function () {
    sessionUserAnswerField.innerText += '1';
    if (sessionUserAnswerField.innerText.length == 3) {
      checkAnswer(sessionUserAnswerField.innerText);
    }
  });
  
  answerButton2.addEventListener('click', function () {
    sessionUserAnswerField.innerText += '2';
    if (sessionUserAnswerField.innerText.length == 3) {
      checkAnswer(sessionUserAnswerField.innerText);
    }
  });
  
  answerButton3.addEventListener("click", function () {
    sessionUserAnswerField.innerText += '3';
    if (sessionUserAnswerField.innerText.length == 3) {
      checkAnswer(sessionUserAnswerField.innerText);
    }
  });
  
  function checkAnswer(userGuess) {
    const answer = sessionStorage.getItem(sessionAnswerKey);
    if (userGuess == answer) {
      duringGameDisplay.setAttribute('hidden', true);
      afterGameDisplay.removeAttribute('hidden');
      sessionTrueAnswerField.innerText = answer;
      updateScore();
    } else {
      const previousAttemptAmount = parseInt(sessionStorage.getItem(sessionUserAttemptsKey));
      sessionStorage.setItem(sessionUserAttemptsKey, previousAttemptAmount + 1);
      sessionUserAttemptsField.innerText = sessionStorage.getItem(sessionUserAttemptsKey);
      sessionUserAnswerField.innerText = '';
      sessionUserWrongAnswerField.innerText = userGuess;
    }
  }
  
  function updateScore() {
    const sessionAttemptsValue = parseInt(sessionStorage.getItem(sessionUserAttemptsKey));
    const localAttemptsValue = parseInt(localStorage.getItem(localMaximumAttemptsKey));
    if (sessionAttemptsValue > localAttemptsValue) {
      localStorage.setItem(localMaximumAttempsKey, sessionAttemptsValue);
      localMaximumAttemptField.innerText = sessionAttemptsValue;
    }
    const previousTotalVictoryAmount = parseInt(localStorage.getItem(localTotalVictoryKey));
    localStorage.setItem(localTotalVictoryKey, previousTotalVictoryAmount + 1);
    localTotalVictoryField.innerText = localStorage.getItem(localTotalVictoryKey);
  }
  
  window.addEventListener('beforeunload', function () {
    sessionUserAnswerField.innerText = '';
    sessionUserWrongAnswerField.innerText = '';
    sessionStorage.setItem(sessionUserAttemptsKey, 0);
    sessionUserAttemptsField.innerText = sessionStorage.getItem(sessionUserAttemptsKey);
  });
</script>

Masih ada satu elemen yang belum kita tambahkan event listener berupa tombol yang memiliki tulisan "Hapus semua data". Fungsi tombol tersebut adalah menghapus semua item storage pada session storage maupun local storage. Proses menghapus item storage akan kita bahas pada materi berikutnya. Semangat ya karena permainan tebak angka kita hampir selesai.


Menghapus Item Storage

Sampai titik ini, kita sudah belajar menambah, mengakses, dan memodifikasi item storage pada session storage maupun local storage. Pada materi kali ini, kita akan lanjut untuk membahas cara menghapus item storage yang terdapat pada session storage maupun local storage di halaman web permainan tebak angka. Pada akhir elemen <script> berkas HTML halaman web permainan tebak angka, tambahkan kode berikut:

destroyDataButton.addEventListener('click', function () {
  sessionStorage.removeItem(sessionAnswerKey);
  sessionStorage.removeItem(sessionUserAttemptsKey);
  sessionStorage.removeItem(sessionUserIsPlayingKey);
  localStorage.removeItem(localTotalVictoryKey);
  localStorage.removeItem(localMaximumAttemptsKey);
  alert('Mohon me-refresh halaman ini kembali');
});

Pada kode di atas, kita menambahkan event listener ke dalam tombol yang memiliki tulisan "Hapus semua data".


Jika tombol tersebut ditekan, semua item storage yang terdapat pada session storage dan local storage akan terhapus.


Untuk melihat perubahannya, lakukan proses refresh/reload pada halaman web permainan tebak angka.

Setelah menambahkan berbagai fitur di dalam elemen script kita, berkas HTML halaman web permainan tebak angka menjadi sebagai berikut:

<!DOCTYPE html>
<html>
<head>
  <title>Web Storage Game</title>
  <style>
    hr {
      height: 2px;
      color: black;
      background-color: black;
      width: 95%;
    }
    
    li {
      margin-top: 1%;
      margin-bottom: 1%;
      font-size: 120%;
    }
    
    .contents {
      margin-left: 5%;
      margin-right: 5%;
    }
    
    .child-content {
      border: 2px solid black;
      padding-bottom: 2%;
      margin-bottom: 1%;
    }
    
    .button {
      padding-left: 1%;
      padding-right: 1%;
      font-size: 140%;
    }
    
    .answer-button {
      margin-left: 2%;
      margin-right: 2%;
      padding: 2%;
      font-size: 140%;
      width: 12%;
    }
    
    #total-stats {
      float: right;
      width: 40%;
      height: 50%;
    }
    
    #gameboard {
      float: left;
      width: 55%;
      height: 40%;
    }
    
    #session-stats {
      float: left;
      margin-top: 1%;
      width: 55%;
    }
    
    #instructions {
      float: right;
      margin-top: 1%;
      width: 40%;
    }
    
    #instructions-list {
      margin-left: 1%;
      margin-right: 1%;
    }
  </style>
</head>
<body>
<h1 align="center">Permainan Tebak Angka '123'</h1>
<div class="contents">
  <div class="child-content" id="total-stats" align="center">
    <h2>Local Stats</h2>
    <hr>
    <div id="all-local-stats">
      <div class="local-stat-item">
        <h3>Jumlah kombinasi yang berhasil di tebak:</h3>
        <h3 id="local-total-victory-field"></h3>
      </div>
      <div class="local-stat-item">
        <h3>Jumlah tebakan salah terbanyak sekali main:</h3>
        <h3 id="local-maximum-attempt-field"></h3>
      </div>
      <button class="button" id="destroy-data-button">Hapus semua data</button>
    </div>
  </div>
  
  <div class="child-content" id="gameboard" align="center">
    <h2>Game Board</h2>
    <hr>
    <div id="before-game-display">
      <h3>Tekan tombol "Play" di bawah jika sudah siap</h3>
      <button class="button" id="play-button">Bermain</button>
    </div>
    <div id="during-game-display" hidden>
      <h3>Silahkan menebak angka!</h3>
      <h3>Jawaban Anda:</h3>
      <h1>"<u><span id="session-user-answer-field"></span></u>"</h1>
      <button class="answer-button" id="answer-1-button">1</button>
      <button class="answer-button" id="answer-2-button">2</button>
      <button class="answer-button" id="answer-3-button">3</button>
      <h3>"<span id="session-user-wrong-answer-field"></span>" bukanlah jawabannya...</h3><br><br>
    </div>
    <div id="after-game-display" hidden>
      <h3>Selamat tebakan Anda benar!</h3>
      <h3>Jawabannya adalah: </h3>
      <h1>"<span id="session-true-answer-field"></span>"</h1>
      <h3><i></i>Refresh halaman ini untuk mencoba lagi</h3>
    </div>
  </div>
  
  <div class="child-content" id="session-stats" align="center">
    <h2>Game Session Stats</h2>
    <hr>
    <div id="all-session-stats">
      <div class="session-stat-item">
        <h3>Jumlah tebakan salah:</h3>
        <h3 id="session-user-attempts-amount-field"></h3>
      </div>
    </div>
  </div>
  
  <div class="child-content" id="instructions">
    <h2 align="center">Cara bermain</h2>
    <hr>
    <h3 align="center">Selamat datang ke permainan tebak angka, berikut instruksi-nya:</h3>
    <ul id="instructions-list" align="left">
      <li>Anda harus mencari kombinasi angka yang terdiri dari angka "1", "2", dan "3",
        misalnya "231" dan "123".
      </li>
      <li>Angka yang ditebak hanya memiliki panjang 3 karakter saja,
        sehingga kombinasi seperti "1231" tidak akan menjadi jawaban.
      </li>
      <li>Sebuah angka hanya akan muncul sekali dan hanya sekali,
        sehingga kombinasi seperti "223" maupun "111" tidak akan menjadi jawaban.
      </li>
      <li><i>Have fun and don't cheat!</i></li>
    </ul>
  </div>
</div>
<script>
  //inisialiasi variabel untuk menampung elemen dokumen
  const localTotalVictoryField = document.getElementById('local-total-victory-field');
  const localMaximumAttemptField = document.getElementById('local-maximum-attempt-field');
  const destroyDataButton = document.getElementById('destroy-data-button');
  const playButton = document.getElementById('play-button');
  const beforeGameDisplay = document.getElementById('before-game-display');
  const duringGameDisplay = document.getElementById('during-game-display');
  const afterGameDisplay = document.getElementById('after-game-display');
  const answerButton1 = document.getElementById('answer-1-button');
  const answerButton2 = document.getElementById('answer-2-button');
  const answerButton3 = document.getElementById('answer-3-button');
  const sessionUserAnswerField = document.getElementById('session-user-answer-field');
  const sessionUserWrongAnswerField = document.getElementById('session-user-wrong-answer-field');
  const sessionTrueAnswerField = document.getElementById('session-true-answer-field');
  const sessionUserAttemptsField = document.getElementById('session-user-attempts-amount-field');
  
  //inisialisasi fungsi untuk menghasilkan jawaban permainan
  function getAnswer() {
    let answer = '123'.split('');
    for (let i = 0; i < answer.length; i++) {
      let j = Math.floor(Math.random() * (i + 1));
      let tmp = answer[i];
      answer[i] = answer[j];
      answer[j] = tmp;
    }
    return answer.join('');
  }
  
  //inisialiasi key untuk session storage
  const sessionAnswerKey = 'SESSION_ANSWER';
  const sessionUserAttemptsKey = 'SESSION_USER_ATTEMPTS';
  
  //inisialisasi key untuk local storage
  const localTotalVictoryKey = 'LOCAL_TOTAL_VICTORIES_PLAYED';
  const localMaximumAttemptsKey = 'LOCAL_MAXIMUM_ATTEMPTS';
  
  window.addEventListener("load", function () {
    if (typeof (Storage) !== "undefined") {
      // inisiliasi semua item web storage yang kita akan gunakan jika belum ada
      if (sessionStorage.getItem(sessionAnswerKey) === null) {
        sessionStorage.setItem(sessionAnswerKey, '');
      }
      if (sessionStorage.getItem(sessionUserAttemptsKey) === null) {
        sessionStorage.setItem(sessionUserAttemptsKey, 0);
      }
      if (localStorage.getItem(localTotalVictoryKey) === null) {
        localStorage.setItem(localTotalVictoryKey, 0);
      }
      if (localStorage.getItem(localMaximumAttemptsKey) === null) {
        localStorage.setItem(localMaximumAttemptsKey, 0);
      }
    } else {
      alert('Browser yang Anda gunakan tidak mendukung Web Storage');
    }
    // inisialiasi semua nilai field pada dokumen yang menggunakan nilai dari web storage
    sessionUserAttemptsField.innerText = sessionStorage.getItem(sessionUserAttemptsKey);
    localTotalVictoryField.innerText = localStorage.getItem(localTotalVictoryKey);
    localMaximumAttemptField.innerText = localStorage.getItem(localMaximumAttemptsKey);
  });
  
  playButton.addEventListener('click', function () {
    sessionStorage.setItem(sessionAnswerKey, getAnswer());
    beforeGameDisplay.setAttribute('hidden', true);
    duringGameDisplay.removeAttribute('hidden');
  });
  
  answerButton1.addEventListener('click', function () {
    sessionUserAnswerField.innerText += '1';
    if (sessionUserAnswerField.innerText.length == 3) {
      checkAnswer(sessionUserAnswerField.innerText);
    }
  });
  
  answerButton2.addEventListener('click', function () {
    sessionUserAnswerField.innerText += '2';
    if (sessionUserAnswerField.innerText.length == 3) {
      checkAnswer(sessionUserAnswerField.innerText);
    }
  });
  
  answerButton3.addEventListener('click', function () {
    sessionUserAnswerField.innerText += '3';
    if (sessionUserAnswerField.innerText.length == 3) {
      checkAnswer(sessionUserAnswerField.innerText);
    }
  });
  
  function checkAnswer(userGuess) {
    const answer = sessionStorage.getItem(sessionAnswerKey);
    if (userGuess == answer) {
      duringGameDisplay.setAttribute('hidden', true);
      afterGameDisplay.removeAttribute('hidden');
      sessionTrueAnswerField.innerText = answer;
      updateScore();
    } else {
      const previousAttemptAmount = parseInt(sessionStorage.getItem(sessionUserAttemptsKey));
      sessionStorage.setItem(sessionUserAttemptsKey, previousAttemptAmount + 1);
      sessionUserAttemptsField.innerText = sessionStorage.getItem(sessionUserAttemptsKey);
      sessionUserAnswerField.innerText = '';
      sessionUserWrongAnswerField.innerText = userGuess;
    }
  }
  
  function updateScore() {
    const sessionAttemptsValue = parseInt(sessionStorage.getItem(sessionUserAttemptsKey));
    const localAttemptsValue = parseInt(localStorage.getItem(localMaximumAttemptsKey));
    if (sessionAttemptsValue > localAttemptsValue) {
      localStorage.setItem(localMaximumAttemptsKey, sessionAttemptsValue);
      localMaximumAttemptField.innerText = sessionAttemptsValue;
    }
    const previousTotalVictoryAmount = parseInt(localStorage.getItem(localTotalVictoryKey));
    localStorage.setItem(localTotalVictoryKey, previousTotalVictoryAmount + 1);
    localTotalVictoryField.innerText = localStorage.getItem(localTotalVictoryKey);
  }
  
  window.addEventListener('beforeunload', function () {
    sessionUserAnswerField.innerText = '';
    sessionUserWrongAnswerField.innerText = '';
    sessionStorage.setItem(sessionUserAttemptsKey, 0);
    sessionUserAttemptsField.innerText = sessionStorage.getItem(sessionUserAttemptsKey);
  });
  
  destroyDataButton.addEventListener('click', function () {
    sessionStorage.removeItem(sessionAnswerKey);
    sessionStorage.removeItem(sessionUserAttemptsKey);
    localStorage.removeItem(localTotalVictoryKey);
    localStorage.removeItem(localMaximumAttemptsKey);
    alert('Mohon me-refresh halaman ini kembali');
  });
</script>
</body>
</html>

Silakan mainkan permainan tebak angka tersebut. Lumayan untuk menghilangkan rasa kebosanan, bukan?

Akhirnya kita sudah melakukan proses menambahkan, mengakses, memodifikasi serta menghapus item storage pada local storage maupun session storage. Sejauh ini, kita hanya mampu menyimpan data yang sederhana seperti angka dalam bentuk string. Namun, bagaimana jika kita ingin mempelajari cara menyimpan data yang lebih kompleks, contohnya seperti JSON? Simak uraiannya di materi berikutnya.


Menyimpan Data Kompleks pada Storage menggunakan objek JSON

Sejauh ini kita hanya menggunakan tipe data string untuk menambah, mengakses, memodifikasi serta menghapus item storage. Ada satu tipe data yang bisa kita gunakan untuk menyimpan data lebih kompleks yakni JSON (JavaScript object Notation). Misalnya kita ingin menyimpan data mengenai nama, umur, dan domisili seseorang. Jika kita pertimbangkan, solusi paling baik adalah menggunakan JSON. Hal tersebut dikarenakan data nama, umur, dan domisili lebih mudah diakses, diolah, dan kodenya lebih mudah dipahami.

Untungnya session storage dan local storage mendukung penyimpanan data berupa JSON. Walau demikian, untuk kali ini kita hanya akan menggunakan local storage saja. Kita akan membuat sebuah halaman web sederhana yang menyimpan data user yang di-input dan menampilkannya.


Pertama, mari kita buat sebuah berkas HTML dengan isi sebagai berikut:

<!DOCTYPE html>
<html>
<head>
  <title>Web Storage JSON</title>
  <style>
    hr {
      height: 2px;
      color: black;
      background-color: black;
      width: 95%;
    }
    
    table {
      border-collapse: collapse;
      border-spacing: 0;
      width: 100%;
      border: 1px solid #ddd;
    }
    
    .contents {
      margin-left: 5%;
      margin-right: 5%;
    }
    
    .child-content {
      border: 2px solid black;
      padding-bottom: 2%;
      margin-bottom: 1%;
    }
    
    .form-element {
      margin: 2%;
    }
    
    #user-form {
      width: 50%;
      height: 40%;
    }
    
    #user-list {
      margin-top: 1%;
      width: 55%;
    }
    
    #instructions-list {
      margin-left: 1%;
      margin-right: 1%;
    }
  </style>
</head>
<body>
<h1 align="center">Penambahan User Baru</h1>
<div class="contents" align="center">
  
  <div class="child-content" id="user-form">
    <h2>Form User Data</h2>
    <hr>
    <form id="form-data-user">
      <div class="form-element">
        <label for="nama">Nama user:</label><br>
        <input id="nama" type="text" name="nama" placeholder="Nama user" value="" maxlength="10" required><br>
      </div>
      <div class="form-element">
        <label for="umur">Umur user:</label><br>
        <input id="umur" type="text" name="umur" placeholder="Umur user" value="" maxlength="2" required><br>
      </div>
      <div class="form-element">
        <label for="domisili">Domisili user:</label><br>
        <input id="domisili" type="text" name="domisili" placeholder="Domisili user" value="" maxlength="10"
               required><br>
      </div>
      <div class="form-element">
        <input id="submitButton" type="submit" value="Submit Data">
      </div>
    </form>
  </div>
  
  <div class="child-content" id="user-list">
    <h2>User List</h2>
    <hr>
    <table id=user-list-table>
      <thead>
        <tr>
          <th>Nama</th>
          <th>Umur</th>
          <th>Domisili</th>
        </tr>
      </thead>
      <tbody id="user-list-detail" align="center">
      </tbody>
    </table>
  </div>
</div>
<script>
  const storageKey = 'STORAGE_KEY';
  const submitAction = document.getElementById('form-data-user');
</script>
</body>
</html>

Terdapat beberapa HTML Element yang memiliki atribut align. Namun, penggunaan atribut ini sudah usang (deprecated) sehingga penggunaannya sudah tidak di rekomendasikan kembali. Solusinya adalah menuliskan aturan styling menggunakan CSS.

Dari berkas HTML di atas, kita sudah memiliki tampilan halaman web sebagai berikut:


Walau tampilannya sudah tampak, isi elemen <script>-nya sedikit. Bahkan hanya mendeklarasikan dua variabel. Berikutnya kita akan menambahkan beberapa fungsionalitas secara perlahan.

Fungsionalitas pertama adalah memeriksa apakah fitur web storage didukung oleh browser yang kita gunakan melalui sebuah fungsi bernama checkForStorage(). Silakan tambahkan kode berikut.

<script>
  // Kode lainnya disembunyikan...
  
  function checkForStorage() {
    return typeof (Storage) !== 'undefined';
  }
</script>

Fungsi di atas akan mengembalikan nilai true jika fitur web storage didukung oleh browser dan false jika tidak. Berikutnya kita akan menerapkan sebuah fungsi bernama putUserList(). Fungsi ini berguna untuk membuat item storage, membuat nilai awalnya serta untuk memodifikasi nilai pada item storage-nya juga. Silakan tambahkan kode bercetak tebal berikut.

<script>
  // Kode lainnya disembunyikan...
 
  function putUserList(data) {
    if (checkForStorage()) {
      let userData = [];
      if (localStorage.getItem(storageKey) !== null) {
        userData = JSON.parse(localStorage.getItem(storageKey));
      }
      userData.unshift(data);
      if (userData.length > 5) {
        userData.pop();
      }
      localStorage.setItem(storageKey, JSON.stringify(userData));
    }
  }
</script>

Fungsi di atas akan memeriksa apakah fitur web storage sudah didukung melalui fungsi checkForStorage(). Jika iya, kita akan membuat sebuah variabel bernama userData yang akan menampung semua data pada item storage. Jika item storage yang digunakan belum dibuat, kita akan memberikan nilai array kosong ke variabel userData. Jika tidak, kita akan mengambil data yang disimpan dan memasukkannya ke fungsi JSON.parse().

Apa kegunaan dari fungsi JSON.parse()? Fungsi tersebut berguna untuk mengonversi sebuah JSON yang ditulis dalam bentuk string ke bentuk JSON "asli". Masih ingat materi "Memodifikasi Item Storage" bahwa data apa pun yang disimpan ke web storage akan menjadi bentuk string? Nah, untuk mengubah sebuah JSON yang ditulis dalam bentuk string ke bentuk JSON "asli", gunakan fungsi JSON.parse().

Kode userData.unshift(data) akan memasukkan nilai yang disimpan di parameter data di elemen paling depan array yang tersimpan di variabel userData. Kode di dalam kondisi if terakhir (userData.length > 5) berfungsi untuk menghilangkan data pada elemen paling terakhir jika panjang userData melebihi 5.

Hal ini dilakukan untuk memunculkan 5 data dari user yang paling baru agar tampilan halaman web tetap rapi. Baris terakhir adalah menyimpan data tersebut ke dalam local storage melalui method setItem(). Sebelum disimpan, kita harus mengubah array yang berisi data-data JSON ke dalam bentuk string terlebih dahulu. Hal ini bisa kita capai melalui fungsi JSON.stringify().

Fungsi berikutnya yang akan kita tulis adalah getUserList() yang berguna untuk mendapatkan semua data pada item storage yang berisi data user yang sudah di-input. Silakan tambahkan kode berikut.

<script>
  // Kode lainnya disembunyikan...
 
  function getUserList() {
    if (checkForStorage()) {
      return JSON.parse(localStorage.getItem(storageKey)) || [];
    } else {
      return [];
    }
  }
</script>

Fungsi ini mengembalikan nilai array dari localStorage ketika sudah memiliki nilai sebelumnya melalui JSON.parse(). Namun, jika item storage yang kita ambil masih kosong, fungsi ini akan mengembalikan nilai array kosong.

Berikutnya kita akan menambahkan fungsi untuk merender data user pada tabel HTML. Fungsi ini bernama renderUserList(). Silakan tambahkan kode bercetak tebal berikut.

<script>
  // Kode lainnya disembunyikan...
 
  function renderUserList() {
    const userData = getUserList();
    const userList = document.querySelector('#user-list-detail');
    userList.innerHTML = '';
    for (let user of userData) {
      let row = document.createElement('tr');
      row.innerHTML = '<td>' + user.nama + '</td>';
      row.innerHTML += '<td>' + user.umur + '</td>';
      row.innerHTML += '<td>' + user.domisili + '</td>';
      userList.appendChild(row);
    }
  } 
</script>

Kemudian kita akan menambahkan event listener ke tombol submit untuk mengambil semua data yang sudah di-input ke semua field di form. Lalu kita akan menyimpannya pada item storage melalui fungsi putUserList(). Selanjutnya daftar user yang baru saja kita masukkan akan langsung ditampilkan melalui fungsi renderUserList(). Silakan tambahkan kode berikut.

<script>
  // Kode lainnya disembunyikan...
 
  submitAction.addEventListener('submit', function (event) {
    const inputNama = document.getElementById('nama').value;
    const inputUmur = document.getElementById('umur').value;
    const inputDomisili = document.getElementById('domisili').value;
    const newUserData = {
      nama: inputNama,
      umur: inputUmur,
      domisili: inputDomisili,
    }
    putUserList(newUserData);
    renderUserList();
  });
</script>

Terakhir, kita akan menambahkan event listener ke objek window untuk event "load". Event handler-nya akan berisi perintah untuk menampilkan semua data yang sudah kita input ke dalam item storage. Silakan tambahkan kode bercetak tebal berikut.

<script>
  // Kode lainnya disembunyikan...
 
  window.addEventListener('load', function () {
    if (checkForStorage) {
      if (localStorage.getItem(storageKey) !== null) {
        renderUserList();
      }
    } else {
      alert('Browser yang Anda gunakan tidak mendukung Web Storage');
    }
  });
</script>

Dari keseluruhan contoh kode di atas, berikut adalah kode keseluruhan kode yang telah kita tuliskan.

<script>
  const storageKey = 'STORAGE_KEY';
  const submitAction = document.getElementById('form-data-user');
  
  function checkForStorage() {
    return typeof (Storage) !== 'undefined';
  }
  
  function putUserList(data) {
    if (checkForStorage()) {
      let userData = [];
      if (localStorage.getItem(storageKey) !== null) {
        userData = JSON.parse(localStorage.getItem(storageKey));
      }
 
      userData.unshift(data);
      if (userData.length > 5) {
        userData.pop();
      }
 
      localStorage.setItem(storageKey, JSON.stringify(userData));
    }
  }
  
  function getUserList() {
    if (checkForStorage()) {
      return JSON.parse(localStorage.getItem(storageKey)) || [];
    } else {
      return [];
    }
  }
  
  function renderUserList() {
    const userData = getUserList();
    const userList = document.querySelector('#user-list-detail');
    
    userList.innerHTML = '';
    for (let user of userData) {
      let row = document.createElement('tr');
      row.innerHTML = '<td>' + user.nama + '</td>';
      row.innerHTML += '<td>' + user.umur + '</td>';
      row.innerHTML += '<td>' + user.domisili + '</td>';
      
      userList.appendChild(row);
    }
  }
  
  submitAction.addEventListener('submit', function (event) {
    const inputNama = document.getElementById('nama').value;
    const inputUmur = document.getElementById('umur').value;
    const inputDomisili = document.getElementById('domisili').value;
    const newUserData = {
      nama: inputNama,
      umur: inputUmur,
      domisili: inputDomisili,
    };
 
    putUserList(newUserData);
    renderUserList();
  });
  
  window.addEventListener('load', function () {
    if (checkForStorage) {
      if (localStorage.getItem(storageKey) !== null) {
        renderUserList();
      }
    } else {
      alert('Browser yang Anda gunakan tidak mendukung Web Storage');
    }
  });
</script>

Jalankan kembali halaman web, maka semua fungsionalitas sudah lengkap. Sehingga, saat ini kita sudah bisa menyimpan, mengakses, serta memodifikasi data pada item storage melalui JSON.


Mengimplementasikan Web Storage ke Proyek Todo Apps

Akhirnya, Anda telah mempelajari konsep baru untuk menyimpan data menggunakan web storage dengan menggunakan mekanisme local storage dan session storage.

Nah, masih ingatkah dengan permasalahan Todo Apps sebelumnya ? Mari kita ingat-ingat kembali melalui animasi berikut:


Dapat kita lihat bahwa ketika suatu task telah berhasil dibuat, kemudian dipindah, lalu kita reload halamannya, maka datanya kembali kosong. Hal ini dikarenakan browser tidak menyimpan semua data task yang telah ditambahkan ke aplikasi.

Inilah yang menjadi fokus kita sekarang untuk menyelesaikan permasalahan sebelumnya. Agar lebih terstruktur, mari kita breakdown langkah apa saja yang ditempuh.

  • Menambahkan metode manipulasi data web storage
  • Menambahkan fungsi memuat data ketika Todo Apps dibuka

Menambahkan Metode Manipulasi Data Web Storage

Setelah semua sudah siap, mari kita terapkan metode manipulasi data dengan menggunakan web storage. Sebelum menuliskan kode, mari kita pahami terlebih dahulu mengenai bagaimana flow dari aplikasi Todo Apps kita menampilkan data.


Nah, agar bisa menyimpan data ke web storage, mari kita tambahkan metode menyimpan data ke web storage. Sehingga, flow dari aplikasi akan menjadi seperti ini :


Pada chart tersebut terlihat bahwa ada tambahan flow save setelah melakukan update todos dan render. Dari sini, kita bisa mengerti bahwa setiap update data yang dilakukan terhadap array todos, maka kita harus menyimpan datanya ke storage.

Serta, kita juga membutuhkan storage yang persistent, yakni tidak hilang sampai data dari situs dihapus. Maka, tipe data yang akan kita gunakan adalah localStorage.

Oke, setelah semua hal sudah diketahui, mari kita implementasikan pada kode. Pada file script.js, silakan tambahkan beberapa berikut :

function addTodo() {
  // ...
  document.dispatchEvent(new Event(RENDER_EVENT));
  saveData();
}
 
 
function addTaskToCompleted(todoId) {
  // ...
  document.dispatchEvent(new Event(RENDER_EVENT));
  saveData();
}
 
 
function removeTaskFromCompleted(todoId) {
  // ...
  document.dispatchEvent(new Event(RENDER_EVENT));
  saveData();
}
 
 
function undoTaskFromCompleted(todoId) {
  // ...
  document.dispatchEvent(new Event(RENDER_EVENT));
  saveData();
}

Intisari dari penambahan kode ini adalah menambahkan pemanggilan fungsi saveData pada setiap fungsi kelola data. Sehingga, ketika data pada array todos ada perubahan, maka diharapkan perubahan tersebut dapat tersimpan pada storage secara langsung, seperti mekanisme flow yang sudah kita buat sebelumnya.

Namun perlu digaris bawahi bahwa fungsi tersebut bukan merupakan fungsi yang disediakan secara bawaan oleh JavaScript, jadi kita perlu menuliskan terlebih dahulu penerapan kode dari fungsinya. Untuk itu, silakan ketikkan beberapa kode di bawah ini.

function saveData() {
  if (isStorageExist()) {
    const parsed = JSON.stringify(todos);
    localStorage.setItem(STORAGE_KEY, parsed);
    document.dispatchEvent(new Event(SAVED_EVENT));
  }
}

Mari kita pahami terlebih dahulu mengenai beberapa kode di atas.

Untuk memastikan bahwa browser yang dipakai memang mendukung localStorage, maka sebaiknya kita periksa terlebih dahulu sebelum mulai eksekusi kode simpan ke storage. Di sini, kita menggunakan fungsi pembantu isStorageExist() yang mengembalikan nilai boolean untuk menentukan apakah memang benar didukung atau tidak.

Jika memang mendukung, maka kita akan mengeksekusi beberapa tahapan berikut (sesuai dengan kode di atas) untuk menyimpan data.

  1. Dikarenakan localStorage hanya mendukung tipe data teks, maka diperlukan konversi data object ke string agar bisa disimpan. Di sini kita gunakan JSON.stringify() untuk konversinya.
  2. Menyimpan data ke storage sesuai dengan key yang kita tentukan. Dalam hal ini key yang kita gunakan adalah "TODO_APPS" dalam variabel STORAGE_KEY.
  3. Untuk mempermudah debugging atau tracking ketika terjadi perubahan data, kita akan memanggil sebuah custom event baru yang bernama "saved-todo" dalam variabel SAVED_EVENT.

Masih ada beberapa kode yang perlu ditambahkan, yakni STORAGE_KEY, SAVED_EVENT & isStorageExist(). Silakan tuliskan kode berikut ini pada file JavaScript.

const SAVED_EVENT = 'saved-todo';
const STORAGE_KEY = 'TODO_APPS';
 
function isStorageExist() /* boolean */ {
  if (typeof (Storage) === undefined) {
    alert('Browser kamu tidak mendukung local storage');
    return false;
  }
  return true;
}

Kemudian, agar dapat memudahkan dalam mengetahui bahwa pada setiap perubahan data bisa secara sukses memperbarui data pada storage, kita bisa menerapkan listener dari event SAVED_EVENT. Kemudian, di dalam event listener tersebut kita bisa memanggil getItem(KEY) untuk mengambil data dari localStorage, lalu bisa kita tampilkan secara sederhana menggunakan console log.

Oke, mari kita terapkan secara langsung dengan menuliskan kode berikut.

document.addEventListener(SAVED_EVENT, function () {
  console.log(localStorage.getItem(STORAGE_KEY));
});

Kemudian, coba lagi aplikasinya sambil membuka DevTools -> Console untuk melihat log yang telah kita buat untuk debugging. Berikut adalah hasilnya. Untuk melihat animasi ini dengan maksimal, kamu bisa zoom gambar ini.


Selain menggunakan metode di atas untuk memeriksa data yang telah tersimpan, Anda juga bisa menggunakan tool yang disediakan oleh Dev Tool. Berikut ini adalah animasi yang menunjukkan cara mengakses fitur tersebut.


Selamat! Aplikasi Todo Apps yang telah dibuat telah berhasil menyimpan data kedalam storage.

Namun, masih ada permasalahan yang belum kita selesaikan saat ini, yakni ketika web dimuat ulang, data sebelumnya masih belum dapat ditampilkan. Akan tetapi, data tersebut jika kita lihat pada localStorage masih tersimpan dengan baik.


Hmmm… masih ada satu kasus lagi yang belum selesai.

Tapi tenang, mari kita pergi ke modul selanjutnya untuk memperbaiki hal ini ya :).


Menambahkan Fungsi Memuat Data Ketika Todo Apps Dibuka

Aplikasi Todo Apps yang telah kita buat sudah bisa menyimpan data, namun masih terdapat case di mana data yang tersimpan tersebut masih belum dapat ditampilkan ketika dimuat ulang. Agar case ini terpecahkan, mari kita coba bersama - sama untuk memperbaiki hal tersebut.

Sebelumnya, telah dikatakan bahwa localStorage ini bisa menyimpan sebuah data hingga data dari web app tersebut dihapus total. Dihapus total pada konteks ini adalah Clear Website Data, di mana semua data yang terkait dengan alamat situs tersebut dihapus, seperti cookies, cache, dan sebagainya.

Mekanisme localStorage ini juga tidak seperti session storage yang mana akan menyimpan data hingga kondisi tertentu atau sampai sesi dari web tersebut habis. Dengan behavior seperti ini, maka web app yang kita buat dapat mengakses data kapan pun dan tidak takut data akan hilang, kecuali memang dihapus dengan cara sengaja.

Oke, kita sudah memakai localStorage untuk menyimpan data. Selanjutnya, kita akan membuat web app menampilkan data dari localStorage ketika halaman pertama kali dimuat. Caranya, kita buat fungsi loadDataFromStorage() dengan dengan menjalankan strategi sebagai berikut:

  1. Ambil data dari localStorage, data ini akan disediakan dalam format teks JSON.
  2. Kemudian parse data JSON tadi menjadi sebuah object.
  3. Lalu, masukkan satu persatu data dari object ke array todos.
  4. Agar bisa diperbarui pada tampilan, panggil Event RENDER_EVENT.

Mari kita ubah strategi di atas menjadi bentuk kode, silakan tulis kode dari fungsi loadDataFromStorage() seperti ini:

function loadDataFromStorage() {
  const serializedData = localStorage.getItem(STORAGE_KEY);
  let data = JSON.parse(serializedData);
 
  if (data !== null) {
    for (const todo of data) {
      todos.push(todo);
    }
  }
 
  document.dispatchEvent(new Event(RENDER_EVENT));
}

Kemudian, kita perlu panggil fungsi tersebut pada saat semua elemen HTML sudah selesai dimuat menjadi DOM. Untuk itu, pastikan kode yang ada pada listener DOMContentLoaded menjadi seperti ini.

document.addEventListener('DOMContentLoaded', function () {
  // ...
  if (isStorageExist()) {
    loadDataFromStorage();
  }
});

Setelah selesai, jangan lupa untuk mencoba reload (muat ulang) kembali halaman website. Gunakanlah Empty Cache & Hard Reload agar file script yang tersimpan pada browser bisa diperbarui. Sehingga, hasilnya bisa nampak seperti ini.


Jika sudah berhasil seperti pada animasi di atas, maka aplikasi Todo Apps yang telah kita buat dari awal sudah selesai. Mantap!


Sebelumnya : Membuat Aplikasi Todo Apps