Quill, why not, and rich text editors in general: Getting started notes

This post was written by eli on December 8, 2021
Posted Under: JavaScript,Rich text editors

Introduction

These are some notes I made while selecting a browser-based rich text editor for my own personal use. Internet and web tech is not my expertise, so I had to catch up some stuff, just to make educated decisions.

I’ll reiterate: I take notes as I work, so this explains why there are several posts about a tool I eventually opted out.

As the title implies, I chose Quill in the beginning, and gave it up after a month. As I had fun getting my hands dirty with JavaScript (for a change, in my case) I was fine with the time spent to learn Quill’s internals (I’ve written several other posts on that), but after three weeks I felt I understood what’s going on, and a week after that I realized any change is going to be difficult regardless.  Looking at how much progress I had achieved (that is, none) I also realized how far I was from finishing, should I remain with Quill. So that was a moment to ignore sunk costs, and pick something else (TinyMCE). More on this below.

Having considered a few editors out there, it seems like they all want to appear to be flexible, modular and customizable. Unfortunately, the only way to tell whether an editor is really flexible is to try to change something that is beyond where the buttons are and what they look like. So it’s requires quite some efforts to reach the point where one can tell if the choice of editor was correct.

Regardless, there’s a rather amusing thing about how many editors present their browser compatibility: It often says “supports latest versions of X, Y ,Z”. Do that convince anyone out there? Is it obvious to everyone that it’s pointless to support the latest browsers? It’s the oldest browsers that any random user out there may have that one need to support.

Editing rich text in general

The old school method for edit regions is by virtue of something like:

<textarea name="content" id="content" class="mceEditor" rows="3" cols="15" tabindex="2"></textarea>

However for rich text, the common trick is to set the contenteditable attribute to an HTML element, hence making its content editable by the browser itself with something like

<div contenteditable="true">This is rich text editable</div>

This sets the isContentEditable property for that element, and also opens for calls to execCommand() for making changes in the text (italics, bold etc.). But even as is, copy-pasting formatted text ends up formatted, and that can be fetched as the innerHTML property. Or the DOM can be traveled down by the inspecting script. This is how common rich editors implement the control buttons for requesting different type faces.

As the text is edited, the DOM tree of that element is updated.

There are however drawbacks with this method. The most commonly mentioned is that different browsers generate different HTML (i.e. a different DOM) in response to user edits. For example, a simple newline can result in the insertion of a <br>, a <p> or a <div>. Different browsers do different things.

But what’s even worse, is that the editing possibilities are limited to what the browser considers reasonable formatting. The fact that the underlying data structure for storing the edited text is the DOM, makes it significantly more difficult to inject information that isn’t DOM related, for example side information that isn’t displayed.

For this reason, there are “block styled editors” which maintain a separate data structure for the text and its edits. The HTML / DOM representation is just for viewing.

Selecting the editor

These are the candidates I looked at:

Block styled editors (don’t rely on the contenteditable element):

  • Quill looked right on the spot, as it has its own data structure for representing text, hence insertion of custom content types (which is exactly what I need) comes naturally. Their git repo looks senseible, and runs back to 2012. It’s apparently the most popular.
  • Trix. For better or for worse, it contains a lot of CoffeeScript.
  • Editor.js. Git repo is sometimes clear, but messy most of the time. Goes back to 2015. Lots of .ts files.
  • Slate. Currently Beta, thought. Basically written in .ts and .tsx. Git repo fairly organized, going back to 2016.
Traditional editors:
  • CKEditor
  • TinyMCE (used by to write this post in WordPress)
  • Draft
  • ProseMirror
  • Redactor

Why I tried Quill

Since I expected a need to add all kinds of unforeseen extra information into the documents, I had the idea that a block styled editor was required. In other words, an editor that keeps the info in some separate data structure.

As far as I could tell, that left me with four candidates: Quill, Slate, Editor.js and Trix.

Another thing is that I intend to keep the edited documents in a git repository, so it’s a bonus if changes play nicely along with plain text diffs. This isn’t all that bad with HTML, but the fact that Quill relies on Delta makes it appear to be better in that respect.

On the other hand, there’s a post written by someone who went from Quill to Slate, but since I don’t expect to write very long pages (which was the main problem presented there) and assuming that the related bug was probably fixed since, it didn’t turn me off Quill. Aside from those specific bugs, that post actually praises Quill.

A complaint regarding Quill is the lack of support for Shift-Enter, for the sake of inserting a <br>. This goes against Quill’s design principles, which is why the author insists on not supporting it. As it turned out, this is just the tip of the iceberg.

Why I went away from Quill

Quill has a very restrictive view on how things should be done. As long as you’re fine with it as is, Quill is great. As long as your customization needs match those envisioned when Quill was designed, it’s a piece of cake. But try something else, and you’re in for bitter fight against the machine. As for developing further, I quite doubt it ever will. It looks like it has reached the point of deadlock where it’s so hacked that every little change is an adventure.

I should also mention that the JavaScript coding style in Quill is exceptionally difficult to read. In particular, there’s a lot of games with the reduce() method and similar tricks to to avoid plain for loops (to keep the code short and concise?), and unless you’re used to those tricks, it’s quite difficult to figure out what the code does. Or with a positive attitude: Quill is a good exercise in advanced JavaScript. Actually, if there’s any reason I’m glad that I spent time learning Quill, it’s the great exercise it was learning JavaScript and how editing works.

The main thing with Quill is that it maintains two extra parallel representations of the edited document, which must be in sync at all times. It’s a nice idea theoretically, however reality brings a lot of corner cases, which haven’t always been solved elegantly. Or can be. So there are all kinds of special-case hacks around to handle special cases. The fact that the editor works appears to rely on a battery of tests that are performed automatically. So the code isn’t stable in the sense that you can make logical changes and expect the logical outcome. Things will break.

Quill is in fact quite radical about its Delta representation of the document. The thought behind the editor is that the document should be stored in Delta format only, and that the browser that displays the content to the end user should load Quill’s JavaScript code, and obtain this Delta representation (in JSON, I guess) for producing the DOM structure. Sticking to the Delta paradigm makes it increasingly difficult to support anything but a simple document structure. That can be seen as an advantage, because documents should be kept simple…? But for people like me, who don’t want the tools to tell me what’s right and what’s wrong, it’s quite a disadvantage.

This paradigm is probably why there’s no official method to obtain the HTML code directly from Quill’s own machinery.

Let’s just mention a few options that won’t necessarily work as expected:

  • Get the editor window’s innerHTML property from the DOM. The problem is that Quill inserts a Cursor blot object into the DOM under certain conditions, which is rubbish when shown to the end user. The best workaround I’ve seen for this is to generate a second instance of a Quill editor, with a hidden <div>, push the Delta there, and obtain its innerHTML. This avoids the Cursor object problem.
  • Generate the HTML with some external tool. Well, that works unless there are some customized blot classes added to the editor. They will appear in the Delta representation as “hey, put a blot of this class here” and the external tool will have no idea what to do with it.

I don’t know how common it is in the web development industry to hack around and ensure that things work by virtue of heavy testing. For someone who just wants an editor for personal use, this is a big no-no.

So if there’s anything I learned from my month of trying to figure out Quill, it’s not to pick a block styled editor, but go for a traditional one. And if the HTML gets messy because of that, fix it manually. As for meta-data in the document, let a post-processing script wipe them away from the HTML, rather than avoiding their production by virtue of special blots.

Actually, after a couple of days adopting TinyMCE instead, I went back to my TODO list, and it turned out that the majority of tasks were about tweaking Quill into doing things that are there out of the box in TinyMCE. So I literally reduced the TODO list by half by migrating to TinyMCE.

Getting started

The confusing question is what to start with. The official suggestion seems to be to include a link to something like https://cdn.quilljs.com/1.0.0/quill.js in the website itself, so the JavaScript is downloaded by all websites from Quill’s servers. Nothing I would consider in any scenario, as my website would become dependent on some server I have no control over.

So what then? Build from the git repo? From the Webpack?

So first, the important note: It’s quite difficult to set up a working environment for building Quill from its git repo. There are a lot of dependencies on different tools, spanning from Ruby, Python, Node.js and whatnot, each with its own set off packages. And apparently, it’s a matter of having just the right version of some of these software elements (not necessarily the latest). So unless you want to dedicate some significant time on developing Quill’s core itself, the git repo’s only use is to see the sources, and not to build anything from them.

Which might be a hint not to try to hack the core code, but rather extend it by virtue of adding modules and such.

I ultimately chose to download quill.tar.gz from the release download page for the release version of v1.3.7. Bonus: It’s compact, and contains a few nice examples (that is, HTML that actually makes an editor).

The HTML examples all rely on quill.min.js (fetched locally). Some also rely on external sources for KaTeX (mathematical typing) and higlight.js (for syntax highlighting). The recommended example is examples/full.html, as it presents an editor with (apparently) all modules released with quill.js appearing in the toolbar.

The quill.min.js (216 kB) is the result of minification of quill.js (440 kB).

Now, it’s possible to obtain the exact same files from the Webpack offered from the site, as I show below. After trying that, I found that all files in the node_modules/quill/dist/ directory (after building, of course), were diff-identical with the files in the release version I had downloaded (which was the last release at that time). A quill.min.js.map (which can help debugging the minified version) was present only in the dist/ directory.

However building the Webpack turned out useful as a quick way to get access to other sources, in particular those for Delta and the Parchment. Which can be obtained separately, but why not get all in one?

Quill’s full repo can be fetched with

$ git clone https://github.com/quilljs/quill.git

Some initial sources to look at (except for my own posts on the matter):

Building the Webpack

Although most likely unnecessary, the Webpack can be cloned from Quickjs’ github page:

$ git clone https://github.com/quilljs/webpack-example.git

There might be a need to go “sudo apt install npm” before going on, which may also install a lot of dependencies (Node.js and a lot of other things). Then simply follow the instructions in the repo’s README.md file:

$ cd webpack-example
$ npm install
$ npm run build

“npm install” runs without root privileges downloads a lot of stuff into .npm (some 33 MB) and then extracts that into node_modules. Took less than 3 minutes, with some warnings about deprecatd packages. OK. The second command took 25 seconds, and it just created dist/bundle.js at the root directory, which is practically useless.

Trying it out

This is a good post on how to generally get started, including adding custom styles.

A simple page with an editor could read:

<!-- Add the theme's stylesheet -->
<link rel="stylesheet" href="quill.snow.css">

<!-- Create the editor container -->
<div id="editor">
  <p>Hello World!</p>
  <p>Some initial <strong>bold</strong> text</p>
  <p><br></p>
</div>

<script src="quill.js"></script>
<script>
var quill = new Quill('#editor', {
  theme: 'snow'   // Specify theme in configuration
});
</script>

Note that two files are mentioned: quill.snow.css and quill.js. Only these two are required for this to work in this specific example.

The files to copy are in node_modules/quill/dist/. Note that there are other files with the same name in other directories (in particular quill.core.js). It’s those in quill/dist that should be used.

Add a Comment

required, use real name
required, will not be published
optional, your blog address