Wednesday

18-06-2025 Vol 19

Create a Markdown-Powered Textarea with Stimulus

Membangun Textarea Bertenaga Markdown dengan Stimulus: Panduan Lengkap

Markdown adalah bahasa markup ringan yang populer karena kesederhanaan dan keterbacaannya. Integrasi Markdown ke dalam aplikasi web Anda dapat meningkatkan pengalaman pengguna secara signifikan, memungkinkan mereka untuk memformat teks dengan mudah tanpa harus mempelajari HTML yang rumit. Artikel ini akan memandu Anda langkah demi langkah dalam membuat textarea bertenaga Markdown menggunakan Stimulus.js, sebuah kerangka kerja JavaScript sederhana untuk HTML yang dinamis.

Mengapa Menggunakan Markdown dan Stimulus?

Sebelum kita menyelam ke dalam kode, mari kita pahami mengapa kombinasi Markdown dan Stimulus adalah pilihan yang baik:

  1. Markdown:

    • Keterbacaan: Markdown mudah dibaca dan ditulis, bahkan dalam bentuk mentahnya.
    • Fleksibilitas: Dapat dikonversi ke HTML atau format lain dengan mudah.
    • Konsistensi: Memastikan pemformatan yang konsisten di seluruh aplikasi Anda.
  2. Stimulus.js:

    • Sederhana: Mudah dipelajari dan digunakan, bahkan untuk pengembang yang baru mengenal JavaScript.
    • Ringan: Tidak membebani performa aplikasi Anda.
    • Modular: Memungkinkan Anda untuk mengelola perilaku DOM dengan cara yang terstruktur.

Kerangka Posting Blog

Berikut adalah kerangka yang akan kita ikuti untuk membangun textarea bertenaga Markdown:

  1. Pengantar
    • Mengapa menggunakan Markdown dan Stimulus?
    • Apa yang akan kita bangun?
  2. Persiapan Awal
    • Menyiapkan lingkungan pengembangan.
    • Membuat proyek HTML dasar.
    • Menambahkan Stimulus.js ke proyek.
  3. Membuat Controller Stimulus
    • Membuat file controller JavaScript.
    • Mendefinisikan controller dan menghubungkannya ke HTML.
    • Menambahkan target untuk textarea dan area pratinjau.
  4. Menerapkan Konversi Markdown
    • Menggunakan pustaka JavaScript untuk konversi Markdown (contoh: Marked.js).
    • Menambahkan fungsi untuk memperbarui area pratinjau secara dinamis.
  5. Menambahkan Fitur Tambahan (Opsional)
    • Menambahkan tombol untuk memformat Markdown (contoh: bold, italic).
    • Menangani pemformatan otomatis (contoh: menambahkan tanda bintang saat mengetik).
  6. Peningkatan User Experience (Opsional)
    • Syntax Highlighting pada area pratinjau.
    • Fullscreen Mode.
    • Live Preview.
  7. Kesimpulan
    • Ringkasan apa yang telah kita pelajari.
    • Langkah selanjutnya (contoh: menerapkan fitur lanjutan).

Langkah 1: Persiapan Awal

Pertama, kita perlu menyiapkan lingkungan pengembangan kita. Pastikan Anda memiliki:

  1. Text Editor: VS Code, Sublime Text, atau editor pilihan Anda.
  2. Browser: Chrome, Firefox, Safari, atau browser modern lainnya.
  3. Node.js dan npm (Opsional): Diperlukan jika Anda ingin menggunakan bundler seperti Webpack atau Parcel.

Selanjutnya, buat proyek HTML dasar. Buat file bernama `index.html` dengan konten berikut:

index.html:

    
<!DOCTYPE html>
<html>
<head>
  <title>Markdown Editor</title>
  <meta charset="UTF-8">
</head>
<body>

  <div data-controller="markdown">
    <textarea data-markdown-target="input"></textarea>
    <div data-markdown-target="preview"></div>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/stimulus/dist/stimulus.umd.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
  <script src="application.js"></script>
</body>
</html>
    
  

Pada file ini, kita telah menambahkan:

  • Tag `<div data-controller=”markdown”>` yang menandakan bahwa div ini dikendalikan oleh controller Stimulus bernama “markdown”.
  • Tag `<textarea data-markdown-target=”input”></textarea>` sebagai textarea tempat pengguna akan memasukkan teks Markdown. `data-markdown-target=”input”` memberi tahu Stimulus bahwa textarea ini adalah target bernama “input” di dalam controller “markdown”.
  • Tag `<div data-markdown-target=”preview”></div>` sebagai div tempat kita akan menampilkan pratinjau HTML dari teks Markdown. `data-markdown-target=”preview”` memberi tahu Stimulus bahwa div ini adalah target bernama “preview” di dalam controller “markdown”.
  • Script untuk memasukkan Stimulus.js dari CDN.
  • Script untuk memasukkan Marked.js, pustaka JavaScript untuk melakukan konversi Markdown ke HTML, dari CDN.
  • Script untuk memasukkan `application.js`, yang akan kita buat di langkah selanjutnya, dari CDN.

Selanjutnya, kita perlu menambahkan Stimulus.js ke proyek kita. Cara termudah adalah dengan menggunakan CDN (Content Delivery Network). Tambahkan baris berikut ke tag `<head>` atau tepat sebelum tag `</body>`:

    
<script src="https://cdn.jsdelivr.net/npm/stimulus/dist/stimulus.umd.js"></script>
    
  

Kita juga akan menggunakan Marked.js untuk melakukan konversi Markdown ke HTML. Tambahkan baris berikut ke tag `<head>` atau tepat sebelum tag `</body>`:

    
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
    
  

Langkah 2: Membuat Controller Stimulus

Sekarang, mari kita buat controller Stimulus. Buat file JavaScript bernama `application.js` (atau nama lain yang sesuai) dan tambahkan kode berikut:

application.js:

    
import { Application, Controller } from "stimulus"

const application = Application.start()

application.register("markdown", class extends Controller {
  static targets = [ "input", "preview" ]

  connect() {
    this.updatePreview()
  }

  updatePreview() {
    this.previewTarget.innerHTML = marked.parse(this.inputTarget.value);
  }
})
    
  

Mari kita uraikan kode ini:

  • import { Application, Controller } from "stimulus": Mengimpor class `Application` dan `Controller` dari pustaka Stimulus.
  • const application = Application.start(): Memulai aplikasi Stimulus.
  • application.register("markdown", class extends Controller { ... }): Mendaftarkan sebuah controller bernama “markdown”. Ini mencocokkan `data-controller=”markdown”` pada elemen HTML kita.
  • static targets = [ "input", "preview" ]: Mendefinisikan dua target: “input” dan “preview”. Ini memungkinkan kita untuk mengakses elemen-elemen HTML yang memiliki atribut `data-markdown-target=”input”` dan `data-markdown-target=”preview”` di dalam controller kita. Di dalam controller, kita bisa mengakses elemen input dengan `this.inputTarget` dan elemen preview dengan `this.previewTarget`.
  • connect() { this.updatePreview() }: Metode `connect()` dipanggil ketika controller terhubung ke DOM. Di sini, kita memanggil `this.updatePreview()` untuk pertama kalinya untuk menampilkan pratinjau awal, bahkan sebelum pengguna mulai mengetik.
  • updatePreview() { this.previewTarget.innerHTML = marked.parse(this.inputTarget.value); }: Metode `updatePreview()` bertanggung jawab untuk mengonversi teks Markdown ke HTML dan memperbarui area pratinjau.

    • this.inputTarget.value: Mengambil nilai dari textarea (elemen input).
    • marked.parse(this.inputTarget.value): Menggunakan fungsi `marked.parse()` dari pustaka Marked.js untuk mengonversi teks Markdown ke HTML.
    • this.previewTarget.innerHTML = ...: Menetapkan HTML yang dihasilkan ke properti `innerHTML` dari elemen pratinjau. Ini akan memperbarui konten elemen pratinjau dengan HTML yang baru.

Pastikan untuk memasukkan script `application.js` ke dalam file `index.html`:

    
 <script src="application.js"></script>
    
  

Sekarang, buka `index.html` di browser Anda. Anda akan melihat textarea dan area pratinjau kosong. Ketik beberapa teks Markdown di textarea, dan Anda akan melihat pratinjau HTML diperbarui secara dinamis.

Langkah 3: Menerapkan Konversi Markdown

Seperti yang kita lihat di langkah sebelumnya, kita sudah menggunakan Marked.js untuk mengonversi Markdown ke HTML. Namun, kita belum menghubungkan input dari textarea ke fungsi konversi. Mari kita tambahkan fungsi untuk memperbarui area pratinjau setiap kali teks di textarea berubah.

Modifikasi controller `markdown` di `application.js` untuk menambahkan tindakan (action) yang dipicu setiap kali textarea berubah:

    
import { Application, Controller } from "stimulus"

const application = Application.start()

application.register("markdown", class extends Controller {
  static targets = [ "input", "preview" ]

  connect() {
    this.updatePreview()
  }

  updatePreview() {
    this.previewTarget.innerHTML = marked.parse(this.inputTarget.value);
  }
})
    
  

Ubah file `index.html` untuk menambahkan action ke elemen textarea:

    
<!DOCTYPE html>
<html>
<head>
  <title>Markdown Editor</title>
  <meta charset="UTF-8">
</head>
<body>

  <div data-controller="markdown">
    <textarea data-markdown-target="input" data-action="input->markdown#updatePreview"></textarea>
    <div data-markdown-target="preview"></div>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/stimulus/dist/stimulus.umd.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
  <script src="application.js"></script>
</body>
</html>
    
  

Perhatikan perubahan pada baris textarea: <textarea data-markdown-target="input" data-action="input->markdown#updatePreview"></textarea>. Atribut `data-action` memberi tahu Stimulus untuk memanggil metode `updatePreview` pada controller “markdown” setiap kali peristiwa “input” terjadi pada textarea (yaitu, setiap kali pengguna mengetik sesuatu).

Sekarang, setiap kali Anda mengetik di textarea, area pratinjau akan diperbarui secara dinamis dengan HTML yang dihasilkan dari teks Markdown.

Langkah 4: Menambahkan Fitur Tambahan (Opsional)

Sekarang kita memiliki textarea bertenaga Markdown dasar, mari kita tambahkan beberapa fitur tambahan untuk meningkatkan pengalaman pengguna.

1. Menambahkan Tombol untuk Memformat Markdown

Kita dapat menambahkan tombol untuk memudahkan pengguna memformat teks Markdown (contoh: bold, italic). Pertama, kita perlu menambahkan tombol-tombol ini ke file `index.html` di dalam `<div data-controller=”markdown”>`:

    
<button data-action="click->markdown#bold">Bold</button>
<button data-action="click->markdown#italic">Italic</button>
    
  

Selanjutnya, kita perlu menambahkan metode `bold` dan `italic` ke controller `markdown` di `application.js`:

    
import { Application, Controller } from "stimulus"

const application = Application.start()

application.register("markdown", class extends Controller {
  static targets = [ "input", "preview" ]

  connect() {
    this.updatePreview()
  }

  updatePreview() {
    this.previewTarget.innerHTML = marked.parse(this.inputTarget.value);
  }

  bold() {
    this.insertText("**" + this.getSelectedText() + "**")
  }

  italic() {
    this.insertText("*" + this.getSelectedText() + "*")
  }

  getSelectedText() {
    return this.inputTarget.value.substring(this.inputTarget.selectionStart, this.inputTarget.selectionEnd);
  }

   insertText(text) {
    const start = this.inputTarget.selectionStart;
    const end = this.inputTarget.selectionEnd;
    const currentValue = this.inputTarget.value;

    this.inputTarget.value = currentValue.substring(0, start) + text + currentValue.substring(end);

    // Secara opsional, atur posisi kursor setelah teks yang disisipkan
    this.inputTarget.selectionStart = this.inputTarget.selectionEnd = start + text.length;

    this.updatePreview();
  }
})
    
  

Mari kita uraikan kode ini:

  • bold() { this.insertText("**" + this.getSelectedText() + "**") }: Metode `bold` mengambil teks yang dipilih, menambahkan tanda bintang ganda di sekelilingnya (untuk membuat teks menjadi tebal di Markdown), dan menyisipkannya kembali ke textarea.
  • italic() { this.insertText("*" + this.getSelectedText() + "*") }: Metode `italic` melakukan hal yang sama seperti `bold`, tetapi menggunakan satu tanda bintang untuk membuat teks menjadi miring.
  • getSelectedText() { return this.inputTarget.value.substring(this.inputTarget.selectionStart, this.inputTarget.selectionEnd); }: Metode ini mengembalikan teks yang saat ini dipilih oleh pengguna di textarea. `this.inputTarget.selectionStart` dan `this.inputTarget.selectionEnd` menunjukkan indeks awal dan akhir dari teks yang dipilih.
  • insertText(text) { ... }: Metode ini menyisipkan teks yang diberikan ke dalam textarea, menggantikan teks yang dipilih (jika ada). Ini juga memposisikan ulang kursor setelah teks yang disisipkan untuk memudahkan pengguna untuk terus mengetik.

Sekarang, ketika Anda memilih teks di textarea dan mengklik tombol “Bold” atau “Italic”, teks yang dipilih akan diformat dengan Markdown yang sesuai.

2. Menangani Pemformatan Otomatis

Kita dapat menambahkan fitur pemformatan otomatis untuk meningkatkan pengalaman pengguna lebih lanjut. Contohnya, kita dapat menambahkan tanda bintang secara otomatis saat pengguna mengetik tanda bintang di awal baris untuk membuat daftar unordered.

Tambahkan action `autoFormat` ke textarea di `index.html`:

    
<textarea data-markdown-target="input" data-action="input->markdown#updatePreview input->markdown#autoFormat"></textarea>
    
  

Tambahkan metode `autoFormat` ke controller `markdown` di `application.js`:

    
import { Application, Controller } from "stimulus"

const application = Application.start()

application.register("markdown", class extends Controller {
  static targets = [ "input", "preview" ]

  connect() {
    this.updatePreview()
  }

  updatePreview() {
    this.previewTarget.innerHTML = marked.parse(this.inputTarget.value);
  }

  bold() {
    this.insertText("**" + this.getSelectedText() + "**")
  }

  italic() {
    this.insertText("*" + this.getSelectedText() + "*")
  }

  getSelectedText() {
    return this.inputTarget.value.substring(this.inputTarget.selectionStart, this.inputTarget.selectionEnd);
  }

  insertText(text) {
    const start = this.inputTarget.selectionStart;
    const end = this.inputTarget.selectionEnd;
    const currentValue = this.inputTarget.value;

    this.inputTarget.value = currentValue.substring(0, start) + text + currentValue.substring(end);

    // Secara opsional, atur posisi kursor setelah teks yang disisipkan
    this.inputTarget.selectionStart = this.inputTarget.selectionEnd = start + text.length;

    this.updatePreview();
  }

  autoFormat() {
    const cursorPosition = this.inputTarget.selectionStart;
    const currentValue = this.inputTarget.value;

    // Dapatkan baris saat ini
    const lineStart = currentValue.lastIndexOf('\n', cursorPosition - 1) + 1;
    const lineEnd = currentValue.indexOf('\n', cursorPosition);
    const currentLine = currentValue.substring(lineStart, lineEnd === -1 ? undefined : lineEnd);

    // Jika baris saat ini hanya berisi '*', tambahkan spasi setelahnya
    if (currentLine.trim() === '*') {
      this.inputTarget.value = currentValue.substring(0, lineStart) + '* ' + currentValue.substring(lineEnd === -1 ? currentValue.length : lineEnd);
      this.inputTarget.selectionStart = this.inputTarget.selectionEnd = cursorPosition + 1;
      this.updatePreview();
    }
  }
})
    
  

Sekarang, jika Anda mengetik “*” di awal baris dan menekan spasi, kode akan secara otomatis menambahkan spasi setelah tanda bintang, mengubahnya menjadi item daftar unordered.

Langkah 5: Peningkatan User Experience (Opsional)

Mari kita tingkatkan pengalaman pengguna dengan menambahkan fitur-fitur berikut:

1. Syntax Highlighting pada Area Pratinjau

Untuk menambahkan syntax highlighting pada area pratinjau, kita dapat menggunakan pustaka seperti Prism.js atau highlight.js. Pertama, kita perlu menambahkan pustaka yang dipilih ke proyek kita. Untuk contoh ini, kita akan menggunakan Prism.js.

Tambahkan link ke CSS dan JavaScript Prism.js ke tag `<head>` di `index.html`:

    
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism.min.css" integrity="sha512-tN7Ec6zAFvlejpHJTHkKyoLy9ztQGMCRcxj13apQLtYpJ/o4jWOB6qzWpwm8mniEAnffT/rWzAkbLI1IHpRVA==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js" integrity="sha512-APxjwsmEplvC8NwEqRz9ZC5QOTJqJmEQEuWnic+fDWXyN8nN3623KeaGs2ziWBqm2tKIcU9Xj0jW5xk9GMgXEA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    
  

Selanjutnya, kita perlu menginisialisasi Prism.js setelah konten pratinjau diperbarui. Ubah metode `updatePreview` di controller `markdown`:

    
 updatePreview() {
    this.previewTarget.innerHTML = marked.parse(this.inputTarget.value);
    Prism.highlightAll();
  }
    
  

Perhatikan penambahan baris Prism.highlightAll();. Ini akan memicu Prism.js untuk mencari dan menyorot semua blok kode di dalam area pratinjau.

Sekarang, ketika Anda menambahkan blok kode ke textarea Markdown Anda (menggunakan tanda backtick ““), blok kode tersebut akan disorot dengan syntax highlighting di area pratinjau.

2. Fullscreen Mode

Kita dapat menambahkan mode fullscreen ke editor kita untuk memberikan pengalaman menulis yang lebih imersif. Pertama, kita perlu menambahkan tombol fullscreen ke file `index.html`:

    
<button data-action="click->markdown#toggleFullscreen">Fullscreen</button>
    
  

Selanjutnya, tambahkan metode `toggleFullscreen` ke controller `markdown` di `application.js`:

    
import { Application, Controller } from "stimulus"

const application = Application.start()

application.register("markdown", class extends Controller {
  static targets = [ "input", "preview" ]

  connect() {
    this.updatePreview()
  }

  updatePreview() {
    this.previewTarget.innerHTML = marked.parse(this.inputTarget.value);
     Prism.highlightAll();
  }

  bold() {
    this.insertText("**" + this.getSelectedText() + "**")
  }

  italic() {
    this.insertText("*" + this.getSelectedText() + "*")
  }

  getSelectedText() {
    return this.inputTarget.value.substring(this.inputTarget.selectionStart, this.inputTarget.selectionEnd);
  }

   insertText(text) {
    const start = this.inputTarget.selectionStart;
    const end = this.inputTarget.selectionEnd;
    const currentValue = this.inputTarget.value;

    this.inputTarget.value = currentValue.substring(0, start) + text + currentValue.substring(end);

    // Secara opsional, atur posisi kursor setelah teks yang disisipkan
    this.inputTarget.selectionStart = this.inputTarget.selectionEnd = start + text.length;

    this.updatePreview();
  }

  autoFormat() {
    const cursorPosition = this.inputTarget.selectionStart;
    const currentValue = this.inputTarget.value;

    // Dapatkan baris saat ini
    const lineStart = currentValue.lastIndexOf('\n', cursorPosition - 1) + 1;
    const lineEnd = currentValue.indexOf('\n', cursorPosition);
    const currentLine = currentValue.substring(lineStart, lineEnd === -1 ? undefined : lineEnd);

    // Jika baris saat ini hanya berisi '*', tambahkan spasi setelahnya
    if (currentLine.trim() === '*') {
      this.inputTarget.value = currentValue.substring(0, lineStart) + '* ' + currentValue.substring(lineEnd === -1 ? currentValue.length : lineEnd);
      this.inputTarget.selectionStart = this.inputTarget.selectionEnd = cursorPosition + 1;
      this.updatePreview();
    }
  }

  toggleFullscreen() {
    if (!document.fullscreenElement) {
      document.documentElement.requestFullscreen();
    } else {
      if (document.exitFullscreen) {
        document.exitFullscreen();
      }
    }
  }
})
    
  

Metode `toggleFullscreen` memeriksa apakah elemen saat ini dalam mode fullscreen. Jika tidak, ia meminta mode fullscreen. Jika ya, ia keluar dari mode fullscreen.

3. Live Preview

Kode kita saat ini memberikan pratinjau live. Tidak perlu ada perubahan tambahan.

Kesimpulan

Dalam tutorial ini, kita telah mempelajari cara membuat textarea bertenaga Markdown menggunakan Stimulus.js. Kita telah mempelajari cara menyiapkan lingkungan pengembangan, membuat controller Stimulus, menerapkan konversi Markdown, dan menambahkan fitur tambahan untuk meningkatkan pengalaman pengguna.

Berikut adalah beberapa langkah selanjutnya yang dapat Anda ambil:

  1. Menerapkan Fitur Lanjutan: Jelajahi fitur lanjutan seperti dukungan tabel, diagram, dan fitur Markdown lainnya.
  2. Menyesuaikan Gaya: Sesuaikan gaya textarea dan area pratinjau agar sesuai dengan desain aplikasi Anda.
  3. Mengintegrasikan dengan Backend: Integrasikan editor Markdown dengan backend Anda untuk menyimpan dan mengambil konten Markdown.

Dengan pengetahuan yang Anda peroleh dalam tutorial ini, Anda dapat membuat editor Markdown yang kuat dan fleksibel untuk aplikasi web Anda.

“`

omcoding

Leave a Reply

Your email address will not be published. Required fields are marked *