Membuat Syntax Highlighter Modern dengan Tombol Copy di Blogger

Tutorial membuat syntax highlighter modern di Blogger dengan warna kode, nomor baris, dan tombol Copy menggunakan HTML, CSS, dan JavaScript.
Membuat Syntax Highlighter Modern dengan Tombol Copy

Ketika membuat tutorial HTML, CSS, atau JavaScript di Blogger, menampilkan potongan kode dengan rapi merupakan hal yang cukup penting. Tanpa format yang tepat, kode bisa sulit dibaca, terlebih jika memiliki banyak baris dan atribut.

Salah satu solusinya adalah menggunakan Syntax Highlighter, yaitu komponen yang memberikan warna berbeda pada bagian kode berdasarkan jenis dan fungsinya.

Dalam tutorial ini, kita akan membuat syntax highlighter modern dengan tampilan seperti editor kode profesional. Komponen ini dilengkapi pewarnaan sintaks, nomor baris otomatis, label bahasa pemrograman, serta tombol Copy untuk memudahkan pembaca menyalin kode.

Menariknya, kita akan menggunakan HTML, CSS, dan JavaScript murni tanpa library eksternal, sehingga tampilannya bisa disesuaikan dengan identitas blog masing-masing.

Apa Itu Syntax Highlighter?

Syntax highlighter merupakan fitur yang menampilkan kode pemrograman menggunakan warna berbeda untuk membedakan elemen seperti tag, atribut, string, komentar, dan kata kunci.

Misalnya, dalam kode HTML, nama tag dapat ditampilkan berwarna merah muda, atribut berwarna oranye, dan nilai atribut berwarna hijau. Perbedaan tersebut membantu pembaca mengenali struktur kode dengan lebih mudah.

Selain membuat artikel terlihat profesional, syntax highlighter juga bermanfaat untuk blog yang membagikan tutorial pemrograman, template Blogger, widget, maupun resource pengembangan website.

Fitur Syntax Highlighter yang Akan Dibuat

Komponen yang akan kita buat memiliki beberapa fitur utama:

  • Modern Dark UI: Tampilan gelap dengan sudut membulat menyerupai editor kode.
  • Syntax Highlighting: Pewarnaan kode HTML, CSS, dan JavaScript dasar.
  • Line Numbers: Nomor baris ditampilkan secara otomatis.
  • Copy Button: Menyalin kode tanpa menyertakan nomor baris.
  • Language Label: Menampilkan jenis kode pada bagian header.
  • Responsive Layout: Mendukung scrolling horizontal pada layar kecil.
  • Tanpa Library: Tidak memerlukan Prism.js, highlight.js, atau framework tambahan.

Perlu diketahui bahwa pewarnaan kode pada tutorial ini dirancang untuk kebutuhan dasar. Parser yang lebih lengkap seperti Prism.js tetap lebih sesuai apabila kamu membutuhkan dukungan banyak bahasa pemrograman dan sintaks kompleks.

1. Cadangkan Template Blogger

Sebelum memasang kode, sebaiknya cadangkan template terlebih dahulu. Langkah ini berguna untuk mengembalikan kondisi website jika terjadi kesalahan dalam proses pengeditan.

  1. Masuk ke dashboard Blogger.
  2. Pilih menu Tema.
  3. Klik tombol panah di samping Sesuaikan.
  4. Pilih Cadangkan untuk mengunduh template XML.
  5. Setelah itu, buka Edit HTML.

Jika template yang kamu gunakan sudah memiliki syntax highlighter bawaan, sebaiknya jangan memasang fitur serupa dua kali karena berpotensi menyebabkan konflik CSS atau JavaScript.

2. Memasang CSS Syntax Highlighter

Langkah pertama adalah menambahkan CSS yang mengatur tampilan utama, mulai dari warna background, header, tombol Copy, nomor baris, hingga pewarnaan sintaks.

Buka editor HTML template Blogger, kemudian cari tag </head> menggunakan Ctrl + F. Letakkan kode CSS berikut tepat di atasnya.

<style>
/* FajriLab Syntax Highlighter */

.flCodeBox {
  margin: 28px 0;
  overflow: hidden;
  border-radius: 12px;
  background: #2e303d;
  color: #dadce8;
  font: 13px/1.75 Consolas, Monaco, monospace;
  direction: ltr;
}

.flCodeHead {
  display: flex;
  align-items: center;
  height: 48px;
  padding: 0 16px;
  background: #1e1f29;
  color: #aeb0bd;
  font: 600 12px Arial, sans-serif;
}

.flCodeDots {
  display: flex;
  gap: 6px;
}

.flCodeDots i {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #ff605c;
}

.flCodeDots i:nth-child(2) {
  background: #ffbd44;
}

.flCodeDots i:nth-child(3) {
  background: #00ca4e;
}

.flCodeLanguage {
  flex: 1;
  text-align: center;
  text-transform: uppercase;
}

.flCodeCopy {
  padding: 8px;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  cursor: pointer;
}

.flCodeCopy:hover {
  color: #fff;
}

.flCodeCopy:focus-visible {
  outline: 2px solid #b8df2b;
  border-radius: 4px;
}

.flCodeScroll {
  overflow-x: auto;
  padding: 18px 0 20px;
}

.flCodeBox pre.flCodePre,
.flCodeBox code.flCodeContent {
  display: block;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  line-height: inherit;
  white-space: pre;
}

.flCodeBox pre.flCodePre {
  overflow: visible;
}

.flCodeLine {
  display: grid;
  grid-template-columns: 45px max-content;
  min-width: max-content;
  min-height: 1.75em;
}

.flCodeNumber {
  position: sticky;
  left: 0;
  padding-right: 12px;
  text-align: right;
  color: #777988;
  background: #2e303d;
  border-right: 1px solid #555766;
  user-select: none;
}

.flCodeText {
  display: block;
  padding: 0 22px 0 24px;
  white-space: pre;
}

/* Syntax Colors */
.flCodeTag { color: #fa866f; }
.flCodeAttr { color: #ff9f43; }
.flCodeString { color: #9bc99c; }
.flCodeComment { color: #85889a; }
.flCodeKeyword { color: #c9a4fa; }
.flCodeNum { color: #f2bf8e; }

@media (max-width: 640px) {
  .flCodeBox {
    font-size: 12px;
    border-radius: 9px;
  }

  .flCodeHead {
    height: 44px;
    padding: 0 12px;
  }

  .flCodeLine {
    grid-template-columns: 36px max-content;
  }

  .flCodeNumber {
    padding-right: 8px;
  }

  .flCodeText {
    padding-left: 16px;
    padding-right: 16px;
  }
}
</style>

CSS di atas menggunakan awalan kelas flCode agar lebih mudah dibedakan dari komponen lain pada template Blogger.

Kamu juga bisa mengubah warna background maupun warna sintaks melalui bagian CSS sesuai kebutuhan desain blog.

3. Menambahkan JavaScript Syntax Highlighter

Setelah CSS terpasang, kita memerlukan JavaScript untuk membaca potongan kode, menambahkan warna, membuat nomor baris, dan mengaktifkan tombol Copy.

Cari tag </body> pada editor HTML Blogger, lalu letakkan kode berikut tepat di atasnya.

<script>
//<![CDATA[
(function () {
  "use strict";

  function escapeHTML(value) {
    return value.replace(/&/g, "&amp;")
      .replace(/</g, "&lt;")
      .replace(/>/g, "&gt;")
      .replace(/"/g, "&quot;")
      .replace(/'/g, "&#39;");
  }

  function span(text, type) {
    return '<span class="flCode' + type + '">' +
      escapeHTML(text) + '</span>';
  }

  function languageOf(code, source) {
    var value = (
      code.getAttribute("class") || ""
    ).toLowerCase();

    var match = value.match(
      /language-(html|xml|css|javascript|js)/
    );

    if (match) {
      return match[1] === "js"
        ? "JavaScript"
        : match[1].toUpperCase();
    }

    if (/^\s*</.test(source)) return "HTML";

    if (/[.#\w-]+\s*\{[^}]*:[^}]*\}/.test(source)) {
      return "CSS";
    }

    return "JavaScript";
  }

  function paintHTML(lines) {
    var state = {
      tag: false,
      quote: "",
      comment: false
    };

    return lines.map(function (line) {
      var output = "";
      var i = 0;

      while (i < line.length) {
        if (state.comment) {
          var end = line.indexOf("-->", i);

          if (end === -1) {
            output += span(line.slice(i), "Comment");
            break;
          }

          output += span(
            line.slice(i, end + 3), "Comment"
          );

          i = end + 3;
          state.comment = false;
          continue;
        }

        if (state.tag) {
          if (state.quote) {
            var stop = line.indexOf(state.quote, i);

            if (stop === -1) {
              output += span(
                line.slice(i), "String"
              );
              break;
            }

            output += span(
              line.slice(i, stop + 1), "String"
            );

            i = stop + 1;
            state.quote = "";
            continue;
          }

          if (line.slice(i, i + 2) === "/>") {
            output += span("/>", "Tag");
            i += 2;
            state.tag = false;
            continue;
          }

          if (line[i] === ">") {
            output += span(">", "Tag");
            i++;
            state.tag = false;
            continue;
          }

          if (line[i] === '"' || line[i] === "'") {
            state.quote = line[i];
            continue;
          }

          var attr = line.slice(i).match(
            /^[a-zA-Z_:][\w:.-]*/
          );

          if (attr) {
            output += span(attr[0], "Attr");
            i += attr[0].length;
            continue;
          }

          output += escapeHTML(line[i]);
          i++;
          continue;
        }

        if (line.slice(i, i + 4) === "<!--") {
          state.comment = true;
          continue;
        }

        var tag = line.slice(i).match(
          /^<\/?[a-zA-Z][\w:-]*/
        );

        if (tag) {
          output += span(tag[0], "Tag");
          i += tag[0].length;
          state.tag = true;
          continue;
        }

        output += escapeHTML(line[i]);
        i++;
      }

      return output;
    });
  }

  function paintOther(line) {
    var pattern =
      /(\/\/.*$|"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\b(?:const|let|var|function|return|if|else|for|while|new|true|false|null|async|await)\b|\b\d+(?:\.\d+)?\b)/g;

    var output = "";
    var last = 0;
    var match;

    while ((match = pattern.exec(line))) {
      output += escapeHTML(
        line.slice(last, match.index)
      );

      var value = match[0];
      var type = "Keyword";

      if (value.slice(0, 2) === "//") {
        type = "Comment";
      } else if (/^["']/.test(value)) {
        type = "String";
      } else if (/^\d/.test(value)) {
        type = "Num";
      }

      output += span(value, type);
      last = pattern.lastIndex;
    }

    return output + escapeHTML(line.slice(last));
  }

  function createBlock(code) {
    var original = code.parentElement;

    if (!original || original.tagName !== "PRE") {
      return;
    }

    if (original.closest(".flCodeBox")) return;

    var source = code.textContent
      .replace(/\r\n?/g, "\n")
      .replace(/^\n/, "")
      .replace(/\n$/, "");

    if (!source.trim()) return;

    var language = languageOf(code, source);

    var box = document.createElement("div");
    box.className = "flCodeBox";

    var header = document.createElement("div");
    header.className = "flCodeHead";

    var dots = document.createElement("span");
    dots.className = "flCodeDots";
    dots.setAttribute("aria-hidden", "true");

    for (var d = 0; d < 3; d++) {
      dots.appendChild(document.createElement("i"));
    }

    var title = document.createElement("span");
    title.className = "flCodeLanguage";
    title.textContent = language;

    var copy = document.createElement("button");
    copy.className = "flCodeCopy";
    copy.type = "button";
    copy.textContent = "Copy";

    var scroll = document.createElement("div");
    scroll.className = "flCodeScroll";

    var pre = document.createElement("pre");
    pre.className = "flCodePre";

    var content = document.createElement("code");
    content.className = "flCodeContent";

    var lines = source.split("\n");

    var painted = language === "HTML" ||
      language === "XML"
        ? paintHTML(lines)
        : lines.map(paintOther);

    painted.forEach(function (html, index) {
      var row = document.createElement("span");
      row.className = "flCodeLine";

      var number = document.createElement("span");
      number.className = "flCodeNumber";
      number.setAttribute("aria-hidden", "true");
      number.textContent = index + 1;

      var text = document.createElement("span");
      text.className = "flCodeText";
      text.innerHTML = html;

      row.appendChild(number);
      row.appendChild(text);
      content.appendChild(row);
    });

    pre.appendChild(content);
    scroll.appendChild(pre);

    header.appendChild(dots);
    header.appendChild(title);
    header.appendChild(copy);

    box.appendChild(header);
    box.appendChild(scroll);

    copy.addEventListener("click", function () {
      function done() {
        copy.textContent = "Copied!";

        setTimeout(function () {
          copy.textContent = "Copy";
        }, 1600);
      }

      function fallback() {
        var textarea = document.createElement(
          "textarea"
        );

        textarea.value = source;
        textarea.style.position = "fixed";
        textarea.style.opacity = "0";

        document.body.appendChild(textarea);
        textarea.select();

        try {
          if (document.execCommand("copy")) {
            done();
          }
        } catch (error) {}

        textarea.remove();
      }

      if (navigator.clipboard &&
          navigator.clipboard.writeText) {
        navigator.clipboard.writeText(source)
          .then(done)
          .catch(fallback);
      } else {
        fallback();
      }
    });

    original.replaceWith(box);
  }

  function init() {
    document.querySelectorAll(
      ".post-body pre > code, .pS pre > code"
    ).forEach(createBlock);
  }

  if (document.readyState === "loading") {
    document.addEventListener(
      "DOMContentLoaded", init
    );
  } else {
    init();
  }
})();
//]]>
</script>

JavaScript akan mendeteksi elemen <pre><code> di dalam artikel, kemudian mengubahnya menjadi komponen kode lengkap dengan nomor baris dan tombol Copy.

Nomor baris hanya digunakan sebagai elemen visual. Ketika pembaca menekan tombol Copy, yang disalin tetap kode aslinya.

4. Cara Menggunakan Syntax Highlighter di Artikel Blogger

Setelah CSS dan JavaScript dipasang, kamu bisa mulai menampilkan potongan kode pada artikel.

Buka artikel melalui Tampilan HTML, lalu gunakan elemen <pre> dan <code> dengan kelas bahasa yang sesuai.

Contoh Kode HTML

Untuk menampilkan kode HTML, gunakan struktur berikut:

<pre><code class="language-html">
&lt;img
  src="gambar.webp"
  alt="Deskripsi gambar"
  loading="lazy"
  width="800"
  height="500"
/&gt;
</code></pre>

Pada contoh di atas, language-html digunakan untuk menentukan jenis kode yang ingin ditampilkan.

Karakter pembuka dan penutup tag HTML perlu ditulis sebagai &lt; dan &gt; di sumber artikel agar browser menampilkannya sebagai teks.

Contoh Kode CSS

Untuk menampilkan CSS, ubah kelas bahasa menjadi language-css.

<pre><code class="language-css">
.button {
  background: #b8df2b;
  color: #050505;
  padding: 12px 24px;
  border-radius: 8px;
}
</code></pre>

Contoh tersebut akan menampilkan aturan CSS lengkap dengan penomoran baris dan tombol untuk menyalinnya.

Contoh Kode JavaScript

Untuk JavaScript, gunakan language-js atau language-javascript.

<pre><code class="language-js">
const button = document.querySelector(".button");

button.addEventListener("click", function () {
  alert("Halo dari FajriLab!");
});
</code></pre>

Ketiga contoh tersebut memakai pola HTML yang sama. Kamu hanya perlu mengganti kelas bahasa dan isi kodenya sesuai kebutuhan.

5. Mengubah Warna Syntax Highlighter

Karena kita menggunakan CSS sendiri, kamu bisa mengubah setiap warna tanpa harus mengganti library atau mengunduh tema tambahan.

Misalnya, jika ingin menggunakan kombinasi warna lain untuk elemen kode, cukup sesuaikan CSS berikut:

.flCodeTag {
  color: #fa866f;
}

.flCodeAttr {
  color: #ff9f43;
}

.flCodeString {
  color: #9bc99c;
}

.flCodeComment {
  color: #85889a;
}

.flCodeKeyword {
  color: #c9a4fa;
}

Untuk mengubah warna background utama, cari kelas .flCodeBox. Sementara itu, warna header dapat diubah melalui .flCodeHead.

Usahakan menggunakan kombinasi warna dengan kontras yang cukup agar kode tetap nyaman dibaca, terutama pada perangkat mobile.

6. Apakah Syntax Highlighter Memengaruhi Performa Blogger?

Menambahkan fitur baru tentu memberikan beban tambahan pada website, termasuk ketika menggunakan JavaScript untuk memproses potongan kode.

Namun, penggunaan JavaScript murni tanpa library eksternal memungkinkan kita mengontrol ukuran kode dan menghindari permintaan jaringan tambahan untuk mengambil library.

Hal tersebut bukan berarti performa otomatis menjadi lebih baik. Jumlah blok kode, panjang potongan kode, serta kompleksitas pemrosesan tetap memengaruhi kinerja browser.

Untuk mengetahui dampaknya, kamu bisa membandingkan hasil pengujian melalui Google PageSpeed Insights sebelum dan setelah pemasangan widget.

Saya pribadi lebih menyukai pendekatan yang menyeimbangkan kualitas desain, kemudahan penggunaan, dan performa website, daripada hanya mengejar tampilan menarik tanpa memperhatikan pengalaman pengunjung.

7. Mengatasi Masalah Syntax Highlighter

Warna kode tidak muncul

Pastikan CSS dan JavaScript terpasang dengan benar. Periksa juga apakah elemen kode menggunakan struktur <pre><code> dan kelas bahasa yang sesuai.

Kode HTML justru berubah menjadi elemen

Masalah ini biasanya terjadi karena kode HTML belum di-escape. Jika ingin menampilkan tag HTML sebagai teks, gunakan HTML entities untuk karakter khusus.

Tombol Copy tidak berfungsi

Pastikan blog menggunakan HTTPS. Clipboard API modern umumnya membutuhkan secure context. Pada browser tertentu, metode penyalinan cadangan mungkin digunakan jika Clipboard API tidak tersedia.

Tampilan kode bertabrakan dengan CSS template

Beberapa template sudah memiliki pengaturan CSS untuk elemen pre dan code. Jika tampilannya tidak sesuai, periksa apakah ada selector bawaan yang memiliki prioritas lebih tinggi.

JavaScript tidak berjalan

Periksa apakah kode JavaScript ditempatkan dengan benar di dalam XML Blogger. Kamu juga dapat membuka Developer Tools melalui tombol F12, kemudian melihat tab Console untuk menemukan pesan kesalahan.

Penutup

Syntax highlighter merupakan komponen sederhana yang dapat meningkatkan kualitas penyajian tutorial kode di Blogger.

Dengan tampilan yang rapi, warna sintaks, nomor baris otomatis, serta tombol Copy, pengunjung bisa membaca dan menyalin kode dengan lebih nyaman.

Melalui FajriLab, saya ingin terus berbagi widget dan komponen yang bukan hanya berfungsi dengan baik, tetapi juga memiliki desain modern dan perhatian terhadap pengalaman pengguna.

Semoga tutorial ini bermanfaat dan bisa menjadi referensi untuk mengembangkan tampilan artikel Blogger yang lebih profesional.

Posting Komentar