04/20/2023

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
html
<script src="https://unpkg.com/@highlightjs/[email protected]/highlight.min.js"></script>
Default CSS
html
<link rel="stylesheet" href="//unpkg.com/@highlightjs/[email protected]/styles/default.min.css">
Custom CSS

atom-one-dark

html
<link rel="stylesheet" href="https://unpkg.com/@highlightjs/[email protected]/styles/@{formattedName}.min.css"/>

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).

<div editor>
    <textarea data-lang="html"></textarea>
    <pre>
        <code></code>
    </pre>
</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.

javascript
function spawnEditors() {
  // Select textarea's
  let ta = document.querySelectorAll("[data-lang]");
  for (let i = 0; i < ta.length; i++) {
    // Popular the code tags with the initial highlighted code
    ta[i].parentNode.querySelector("code").innerHTML = hljs
      .highlight(ta[i].value, { language: ta[i].dataset.lang })
      // Replace all newlines with br tags
      .value.replace(/\n/g, "<br/>");
    // Bind the scroll of the textarea to the pre tag
    ta[i].addEventListener("scroll", (e) => {
      e.target.parentNode.querySelector("pre").scrollTo(0, ta[i].scrollTop);
    });
    // Update the code tag with updated HTML after user input
    ta[i].addEventListener("keyup", (e) => {
      e.target.parentNode.querySelector("code").innerHTML = hljs
        .highlight(e.target.value, {
          language: e.target.dataset.lang,
        })
        .value.replace(/\n/g, "<br/>");
    });
  }
}

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.

css
[code] {
    position: relative;
    width: 100%;
    height: 100%;
}
[code] > * {
    position: absolute;
    top: 0;
    left: 0;
    width: calc(100% - 20px);
    height: calc(100% - 20px);
    white-space: normal;
    margin: 0;
    padding: 10px;
    border: none;
    font-size: 12px;
    font-family: monospace;
    border-radius: 10px;
}
[code] > textarea {
    z-index: 1;
    color: transparent;
    background: transparent;
    caret-color: #000;
    width: 100%;
    height: 100%;
}
[code] > pre {
    z-index: 0;
    background: #263541;
    color: #b8b8b8;
}

[code] > pre > code {
    display: block;
    white-space: pre-wrap;
}

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. ...

Back

Comments


Be the first to comment!

Read More

Lazy Loading Images

Lazy loading is a technique that defers the loading of images until they are needed, rather than loading them all at once when the page first loads. This can help improve th...

Node JS Logo
Node JS Logo

Setting up Node.JS for production

Node.js is a popular JavaScript runtime that allows developers to build server-side applications with JavaScript. PM2 is a process manager for Node.js applications that helps to keep your applications running smoothly and automatically restart th...

Node JS Express Server

Even though Node JS has a built in HTTP request handler using a web server framework can reduce the amount of code and increase the reliability of your site. There are many options to ...

Node JS HTTP Webserver

Node JS comes out of the box with a built in HTTP module that can handle Hypertext Transfer Protocol requests. You can use this to build a web server without installing anything.