Building a Highlighted Text Editor in the Browser

Trying to get highlighted code to be editable on your website can be tricky to implement. The idea behind successfully doing it is to display a code tag and highlight the code in there, then overlay a textarea with the font color and background set to transparent.

Now that you know what we will build, let's learn how to implement it.

Code Highlighting

We will need to highlight the code in the editor we are building, so for this, we will use the library I use, and the most popular, highlight.js.

Importing

All of the code we will go over here will be in the browser, but highlight.js is an NPM package, so we can use unpkg to host our files. Highlight.js needs at least two files, the javascript and the CSS styling. If you want to use a non-default CSS layout, you can pick one from here.

JS

1<script src="https://unpkg.com/@highlightjs/[email protected]/highlight.min.js"></script>
Default CSS
1<link
2  rel="stylesheet"
3  href="//unpkg.com/@highlightjs/[email protected]/styles/default.min.css"
4/>
Custom CSS
1<link
2  rel="stylesheet"
3  href="https://unpkg.com/@highlightjs/[email protected]/styles/atom-one-dark.min.css"
4/>

Custom Bundles

If you know what languages you want to highlight, you can reduce the amount of javascript shipped to the browser by using highlight.js custom tool to assemble a bundle. Go here and select which languages you will be using, then download the zip file and extract the highlight.min.js file.

Structure

For reference, this tutorial is based on this tutorial, so we will carry over some of the same concepts like adding the data-lang attribute and using a custom attribute to define our component (editor).

1<div editor>
2  <textarea data-lang="html"></textarea>
3  <pre>
4  <code></code>
5 </pre>
6</div>

Binding the textarea and the code block

Now that we have our foundation, let's build the code that will bind our textarea and code block. The code below has comments explaining each step, so you can skip the rest of this paragraph if you don't want to read how it works. Once the spawnEditors function is called, it grabs all of the textarea's that have the data-lang property. Then it loops over those, firstly setting the value of the code tag to be the contents of the textarea highlighted with highlight.js. Next, it binds the scroll of the pre tag to the textarea to keep them synced up. Lastly, it adds an event listener to update the code as it changes.

 1function spawnEditors() {
 2  // Select textarea's
 3  let ta = document.querySelectorAll("[data-lang]");
 4  for (let i = 0; i < ta.length; i++) {
 5    // Popular the code tags with the initial highlighted code
 6    ta[i].parentNode.querySelector("code").innerHTML = hljs
 7      .highlight(ta[i].value, { language: ta[i].dataset.lang })
 8      // Replace all newlines with br tags
 9      .value.replace(/\n/g, "<br/>");
10    // Bind the scroll of the textarea to the pre tag
11    ta[i].addEventListener("scroll", (e) => {
12      e.target.parentNode.querySelector("pre").scrollTo(0, ta[i].scrollTop);
13    });
14    // Update the code tag with updated HTML after user input
15    ta[i].addEventListener("keyup", (e) => {
16      e.target.parentNode.querySelector("code").innerHTML = hljs
17        .highlight(e.target.value, {
18          language: e.target.dataset.lang,
19        })
20        .value.replace(/\n/g, "<br/>");
21    });
22  }
23}

Styling

Normally I don't like to talk too much about styling and CSS, but for this project, it is one of the most important parts. We will use CSS to turn the text area and the code block into one editor. We will need to make the code tag and textarea line up with the same font type and size. Then set the z-index of the textarea higher than the code block and change the cursor's color.

 1[code] {
 2  position: relative;
 3  width: 100%;
 4  height: 100%;
 5}
 6[code] > * {
 7  position: absolute;
 8  top: 0;
 9  left: 0;
10  width: calc(100% - 20px);
11  height: calc(100% - 20px);
12  white-space: normal;
13  margin: 0;
14  padding: 10px;
15  border: none;
16  font-size: 12px;
17  font-family: monospace;
18  border-radius: 10px;
19}
20[code] > textarea {
21  z-index: 1;
22  color: transparent;
23  background: transparent;
24  caret-color: #000;
25  width: 100%;
26  height: 100%;
27}
28[code] > pre {
29  z-index: 0;
30  background: #263541;
31  color: #b8b8b8;
32}
33
34[code] > pre > code {
35  display: block;
36  white-space: pre-wrap;
37}

Demo

This is a demo of a Codepen Like Editor I build a few days ago. In the linked tutorial, I did not cover the code highlighting, but here it is with it working. !{If the code is not showing, try refreshing the page. This is still a WIP, but I felt it was appropriate to show it here}

1<h1>Hello</h1>
1h1 {
2  color: red;
3}
1document.querySelector("h1").innerText += " World";