Artikel panjang sering dibaca dengan dua cara: dilompati lewat daftar isi, atau disimpan untuk dibaca nanti. Bagian penutup proyek ini menyediakan keduanya. Daftar isi dibangun dari elemen details yang bekerja tanpa satu baris JavaScript pun, sedangkan tombol bookmark memakai JavaScript dan localStorage agar pilihan pengunjung bertahan meski browser ditutup. Prinsipnya sejalan dengan chapter Progressive Enhancement: halaman tetap utuh tanpa JavaScript, dan JavaScript hanya menambah kenyamanan.

File interaksi-artikel.html #

Simpan file berikut sebagai interaksi-artikel.html di folder bagian-4/, bersama interaksi-artikel.js dan hero.svg yang tetap dirujuk dari assets/:

<!doctype html>
<html lang="id">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Tips Menanam Cabai di Pot — Aneka Baca</title>
  <link rel="stylesheet" href="../styles/style.css">
  <script src="interaksi-artikel.js" defer></script>
</head>
<body>
  <nav aria-label="Navigasi artikel">
    <ul>
      <li><a href="../index.html">Beranda</a></li>
      <li><a href="artikel-hidroponik.html">Artikel sebelumnya</a></li>
      <li><a href="artikel-pupuk-organik.html">Artikel berikutnya</a></li>
    </ul>
  </nav>
  <article>
    <header>
      <h1>Tips Menanam Cabai di Pot</h1>
      <p class="lead">Cabai termasuk tanaman yang ramah bagi pemula asalkan dua keputusan awalnya tepat: pot yang dipakai dan jadwal penyiramannya. Artikel ini merangkum keduanya dalam panduan singkat.</p>
      <button id="tombol-bookmark" type="button" aria-pressed="false">Simpan artikel</button>
    </header>
    <details>
      <summary>Daftar isi</summary>
      <ul>
        <li><a href="#pot">Memilih pot dan media tanam</a></li>
        <li><a href="#penyiraman">Menyusun jadwal penyiraman</a></li>
      </ul>
    </details>
    <section id="pot">
      <h2>Memilih pot dan media tanam</h2>
      <p>Pilih pot berdiameter minimal 30 sentimeter yang memiliki lubang pembuangan di dasarnya. Lubang ini mencegah air menggenang dan akar membusuk. Isi pot dengan campuran tanah kebun dan kompos dengan perbandingan satu banding satu.</p>
      <p>Letakkan pot di tempat yang menerima sinar matahari minimal enam jam per hari. Tanaman cabai yang kekurangan cahaya tumbuh tinggi tetapi jarang berbuah.</p>
    </section>
    <section id="penyiraman">
      <h2>Menyusun jadwal penyiraman</h2>
      <p>Siram tanaman setiap pagi sebelum matahari tinggi. Periksa permukaan tanah terlebih dahulu: bila masih lembap, tunda penyiraman hingga sore hari.</p>
      <p>Dengan pot yang tepat dan penyiraman yang teratur, bunga pertama biasanya muncul dalam empat sampai enam minggu. Panen pertama menyusul sekitar tiga minggu kemudian.</p>
    </section>
    <footer>
      <p>Ditulis oleh Siti Rahma.</p>
      <p>Diterbitkan pada <time datetime="2025-01-18">18 Januari 2025</time>.</p>
    </footer>
  </article>
</body>
</html>

Mari kita bedah tambahan-tambahannya:

  1. Baris 7-8 (head): link memuat stylesheet proyek, dan script dengan atribut defer memuat file JavaScript — urutan muat yang diulas pada chapter Script dan Defer.
  2. Baris 22 (<button>): tombol bookmark dengan type="button" agar tidak berperilaku sebagai tombol submit, serta aria-pressed="false" yang menyatakan status tombol bagi pembaca layar.
  3. Baris 24-30 (<details>): pasangan summary dan daftar tautan. Browser menangani buka-tutup secara native; atribut href="#pot" menuju id pada section sehingga klik langsung melompat ke subtopik.
  4. Baris 31 dan 36 (id pada section): penanda tujuan anchor untuk daftar isi; nilai id harus unik dan cocok persis dengan tautannya.

Logika interaksi-artikel.js #

javascript interaksi-artikel.js
// interaksi-artikel.js — tombol bookmark proyek Website Artikel.
// Status tersimpan pada localStorage sehingga bertahan setelah halaman ditutup.

const KUNCI_BOOKMARK = "aneka-baca:bookmark";
const tombol = document.querySelector("#tombol-bookmark");

function bacaStatus() {
  try {
    return localStorage.getItem(KUNCI_BOOKMARK) === "ya";
  } catch (galat) {
    return false;
  }
}

function tulisStatus(tersimpan) {
  try {
    if (tersimpan) {
      localStorage.setItem(KUNCI_BOOKMARK, "ya");
    } else {
      localStorage.removeItem(KUNCI_BOOKMARK);
    }
  } catch (galat) {
    // Penyimpanan tidak tersedia (misalnya mode privat ketat);
    // tombol tetap berfungsi selama sesi ini tanpa mengingat status.
  }
}

function perbaruiTombol() {
  const tersimpan = bacaStatus();
  tombol.setAttribute("aria-pressed", tersimpan ? "true" : "false");
  tombol.textContent = tersimpan ? "Artikel tersimpan" : "Simpan artikel";
}

if (tombol) {
  tombol.addEventListener("click", function () {
    tulisStatus(!bacaStatus());
    perbaruiTombol();
  });
  perbaruiTombol();
}

Output yang Dihasilkan:

text
Saat halaman dibuka pertama kali:
  - Tombol berlabel "Simpan artikel" tampil di bawah paragraf pengantar
Saat tombol diklik:
  - Label berubah menjadi "Artikel tersimpan" dan warna tombol berubah
Saat halaman ditutup lalu dibuka kembali:
  - Label tetap "Artikel tersimpan" karena status dibaca dari localStorage
Saat tombol diklik lagi:
  - Status kembali kosong dan label kembali "Simpan artikel"

Mari kita bedah alurnya:

  1. Baris 4-5 (konstanta dan referensi): KUNCI_BOOKMARK adalah nama kunci di dalam localStorage, sedangkan tombol memegang referensi elemen lewat querySelector.
  2. Baris 7-13 (bacaStatus()): membaca kunci lalu membandingkannya dengan "ya". Blok try...catch menjaga halaman tetap berjalan bila penyimpanan tidak tersedia, misalnya pada sebagian mode privat browser.
  3. Baris 15-26 (tulisStatus()): memilih antara setItem dan removeItem sesuai status terbaru; penyimpanan yang tidak tersedia ditangani tanpa menghentikan klik.
  4. Baris 28-32 (perbaruiTombol()): satu tempat untuk memperbarui tampilan tombol — teks melalui textContent dan status melalui aria-pressed, sehingga tampilan dan informasi pembaca layar tidak pernah berbeda.
  5. Baris 34-40 (pemasangan listener): klik membalik status lalu memanggil perbaruiTombol(); pemanggilan perbaruiTombol() di akhir menyamakan tampilan tombol dengan status tersimpan saat halaman baru dibuka.

Perhatikan bahwa seluruh logika hanya menyentuh tombol; konten artikel tidak bergantung pada JavaScript sama sekali. Bila file JS gagal dimuat, pembaca tetap membaca artikel penuh dan daftar isi tetap berfungsi.

Langkah Pemeriksaan #

Tuntaskan Bagian ini sekaligus proyek dengan urutan berikut:

  1. Buka interaksi-artikel.html di browser. Klik daftar isi: halaman harus melompat ke subtopik terkait. Klik tombol bookmark, muat ulang halaman, dan pastikan label tetap “Artikel tersimpan”.
  2. Buka panel Application (atau Storage) pada alat pengembang browser, temukan kunci aneka-baca:bookmark, lalu hapus manual untuk menguji jalur reset.
  3. Jalankan ../tests/cek-struktur.html dan centang seluruh butir tersisa, termasuk kelompok Interaksi. Checklist menyimpan progresnya secara otomatis, jadi butir dari Bagian 1-3 tidak perlu dicentang ulang.

Proyek Website Artikel tuntas: satu artikel semantik, terhubung, bergaya, dan interaktif. Pola yang sama — struktur, metadata, stylesheet, lalu interaksi — dapat kita pakai ulang untuk hampir semua halaman konten berikutnya.

Referensi resmi untuk pendalaman:

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

interaksi-artikel.html

Unduh berkas
html
<!doctype html>
<html lang="id">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Tips Menanam Cabai di Pot — Aneka Baca</title>
  <link rel="stylesheet" href="../styles/style.css">
  <script src="interaksi-artikel.js" defer></script>
</head>
<body>
  <nav aria-label="Navigasi artikel">
    <ul>
      <li><a href="../index.html">Beranda</a></li>
      <li><a href="artikel-hidroponik.html">Artikel sebelumnya</a></li>
      <li><a href="artikel-pupuk-organik.html">Artikel berikutnya</a></li>
    </ul>
  </nav>
  <article>
    <header>
      <h1>Tips Menanam Cabai di Pot</h1>
      <p class="lead">Cabai termasuk tanaman yang ramah bagi pemula asalkan dua keputusan awalnya tepat: pot yang dipakai dan jadwal penyiramannya. Artikel ini merangkum keduanya dalam panduan singkat.</p>
      <button id="tombol-bookmark" type="button" aria-pressed="false">Simpan artikel</button>
    </header>
    <details>
      <summary>Daftar isi</summary>
      <ul>
        <li><a href="#pot">Memilih pot dan media tanam</a></li>
        <li><a href="#penyiraman">Menyusun jadwal penyiraman</a></li>
      </ul>
    </details>
    <section id="pot">
      <h2>Memilih pot dan media tanam</h2>
      <p>Pilih pot berdiameter minimal 30 sentimeter yang memiliki lubang pembuangan di dasarnya. Lubang ini mencegah air menggenang dan akar membusuk. Isi pot dengan campuran tanah kebun dan kompos dengan perbandingan satu banding satu.</p>
      <p>Letakkan pot di tempat yang menerima sinar matahari minimal enam jam per hari. Tanaman cabai yang kekurangan cahaya tumbuh tinggi tetapi jarang berbuah.</p>
    </section>
    <section id="penyiraman">
      <h2>Menyusun jadwal penyiraman</h2>
      <p>Siram tanaman setiap pagi sebelum matahari tinggi. Periksa permukaan tanah terlebih dahulu: bila masih lembap, tunda penyiraman hingga sore hari.</p>
      <p>Dengan pot yang tepat dan penyiraman yang teratur, bunga pertama biasanya muncul dalam empat sampai enam minggu. Panen pertama menyusul sekitar tiga minggu kemudian.</p>
    </section>
    <footer>
      <p>Ditulis oleh Siti Rahma.</p>
      <p>Diterbitkan pada <time datetime="2025-01-18">18 Januari 2025</time>.</p>
    </footer>
  </article>
</body>
</html>

interaksi-artikel.js

Unduh berkas
javascript
// interaksi-artikel.js — tombol bookmark proyek Website Artikel.
// Status tersimpan pada localStorage sehingga bertahan setelah halaman ditutup.

const KUNCI_BOOKMARK = "aneka-baca:bookmark";
const tombol = document.querySelector("#tombol-bookmark");

function bacaStatus() {
  try {
    return localStorage.getItem(KUNCI_BOOKMARK) === "ya";
  } catch (galat) {
    return false;
  }
}

function tulisStatus(tersimpan) {
  try {
    if (tersimpan) {
      localStorage.setItem(KUNCI_BOOKMARK, "ya");
    } else {
      localStorage.removeItem(KUNCI_BOOKMARK);
    }
  } catch (galat) {
    // Penyimpanan tidak tersedia (misalnya mode privat ketat);
    // tombol tetap berfungsi selama sesi ini tanpa mengingat status.
  }
}

function perbaruiTombol() {
  const tersimpan = bacaStatus();
  tombol.setAttribute("aria-pressed", tersimpan ? "true" : "false");
  tombol.textContent = tersimpan ? "Artikel tersimpan" : "Simpan artikel";
}

if (tombol) {
  tombol.addEventListener("click", function () {
    tulisStatus(!bacaStatus());
    perbaruiTombol();
  });
  perbaruiTombol();
}