The Wiki Weekend Part 2: The Page Editor Idea

Back to HTML WARDen.
-
The Wiki Weekend Part 1: Motivation and Other Wikis
-
The Wiki Weekend Part 2
You are here
-
The Wiki Weekend Part 3: The Storage
-
The Wiki Weekend Part 4: Users, Locking
-
The Wiki Weekend Part 5: Finishing Touches, Conclusion
The first thing I wanted to do was prototype a page editing interface idea I’ve been mulling over for a while.
(Update: A week later, I ended up
scrapping the WYSIWYG editor!
See Death to WYSIWYG!
To get a copy of HTML WARDen as described on this
page, clone the
repo
and checkout commit
801ab2cd0839fc150061fccc1f71ee1e360cf3eb
.)
An HTML editor with WYSIWYG editing?
I’ve been having this radical thought lately: The most expressive language for describing HTML is…HTML.
(This is part of a much bigger line of thought I’ve had regarding lightweight markup languages (e.g. Markdown, Asciidoc) as a temporary editing interface, HTML as a storage medium, static site generation, etc. But that whole thing really needs to wait until I get some other projects off my stack!)
I’ve been writing HTML since the late 1990s and I still do it all the time in small doses. The only problem I have with hand-writing HTML is the tedium of typing the tags in normal "prose" sections. I want to just write.
For this wiki, specifically, I wanted the easiest possible text entry interface. I needed it to have a completely friendly and familiar feeling for a technophobe who associates writing on computers with word processing software.
WYSIWYG (WTWYWON): What You See Is What You Get (Whether That’s What You Wanted Or Not).
I pretty much universally loathe WYSIWYG interfaces! I just cannot stand how they make you guess what’s going on under the hood when you’re trying to figure out where to put the cursor to stop the text from continuing to be bold or to get the line height between paragraphs to stop being so weird in that one part of the document. You know what I’m talking about?
So I want to be able to edit the underlying formatting directly. In this case, edit the raw HTML.
Okay, so, "This needs to work like a word processor," and, "HTML is the best HTML." That’s the two things I need. How do I bridge the gap between these two things?
Let’s tackle them one at a time.
First of all, I need to have some sort of "rich text editing" to accomplish the word processing part.
I’ve always been fascinated by the contentEditable
attribute which turns your
browser into a rich text HTML editor.
Rich text editing with contentEditable
The idea of browsers as editors goes way back to the very beginning.
But it was Internet Explorer 5.5 that added the contentEditable
property to enable this as part of the HTML source itself.
(Like all features that came from early IE (and there’s a lot of them,
so-called "AJAX" is another), the way contentEditable
and
document.execCommand()
is nuts. I would love to get in a time machine and
go listen in on the conversations they were having in whatever demon-infested
code-dungeon created those interfaces. Or maybe not. Maybe it’s best not to
know too much.)
Mark Pilgrim has a write-up on where the history went from there:
The Road to HTML 5: contentEditable (whatwg.org)
"There was some superficial documentation on how to use them (so developers could develop rich text editors), but little thought of interoperability. So, no details on all the nitty gritty details of exactly what markup is generated when you press ENTER right here, or what the DOM looks like as you backspace your way through a start tag. Much of this sort of information was later reverse-engineered, and cross-browser support for basic operations is actually quite good. (Browsers still vary widely on the details.)"
Arguably, what we ended up with is absolutely terrible…but it works! There’s an HTML editor just sitting there, completely free for you to use in every major browser! You are surely using one right now.
Look, I'll show you how easy it is to make something editable. Just click on the Edit button below and then this paragraph will become editable and you can...just start editing! Have fun!
Here’s the whole script and only one line does all the work:
document.getElementById('make_editable') .addEventListener('click', function(){ var p = document.getElementById('edit_me'); p.setAttribute('contentEditable', 'true'); // <---- This one! p.focus(); });
So that’s the rich text editor done. Well, mostly.
How about that second thing I need?
Split pane editing
Well, as for editing raw HTML, that’s easy, you just put the source
to be edited in a <textarea>
.
The thing I was wondering was: If you change a document with contentEditable
,
can you just grab the edited source with innerHTML
and put that in the
<textarea>
?
It seemed like it should be fine. I couldn’t think of any reason why edits to
the <textarea>
couldn’t be synced back up with the WYSIWYG rendered display.
Here goes nothing…
Try it:
HTML Editor Live Demo
This is a live demo of the split-pane editor used in HTML Warden. It is made with just a handful of HTML tags and a few lines of JS.
Click on either the right or left side of this editor to edit the HTML code, or the rendered "rich text" view directly.
The principle couldn’t be simpler:
-
Listen for
input
changes on either side. -
Update the other side with the new contents. (Literally just a=b or b=a.)
This is the heart of it:
var html_view = document.getElementById('html_view'); var code_area = document.querySelector('#code_view textarea'); code_area.addEventListener('input', c2h); html_view.addEventListener('input', h2c); function h2c(){ code_area.value = cformat(html_view.innerHTML); } function c2h(){ html_view.innerHTML = code_area.value; }
(The input
event is the way to go rather than, say, keyUp
, because it
catches paste events as well as typing).
The only additional thing I’m doing in HTML WARDen is debouncing the input to a half second because I don’t want to re-render a huge article worth of stuff with every single keypress while the user is actively typing.
Tidy up the HTML source
Two things help keep the HTML source from becoming a total mess
when you type in the contentEditable
area.
First, I tell the browser to insert paragraph tags when the user makes a new paragraph like so:
document.execCommand('DefaultParagraphSeparator', false, 'p');
(Supposedly DefaultParagraphSeparator
is deprecated and will be leaving
someday, but there’s no replacement for its functionality, and it seems to
still work in all the browsers.)
Second, I’m inserting some blank lines in the generated source so it doesn’t become one giant block of HTML tags.
This is my silly quick-and-dirty solution that is surprisingly effective:
// Format Code - Just ensure each block element gets a line. var blocktags = ['p','div','img','h1','h2','h3','h4']; var needs_nl = new RegExp('([^\n])(<(?:' + blocktags.join('|') + ')>)', 'g'); function cformat(c){ return c.replace(needs_nl, '$1\n\n$2'); }
(The name cformat()
is short for "code format," not anything to do
with the C programming language or sorting locale.)
The h2c()
function in this demo calls it like this:
function h2c(){ code_area.value = cformat(html_view.innerHTML); }
Put it all together with some buttons and you’ve got an "unreasonably effective" and very wiki two-pane editor.
Screenshot from HTML WARDen
Here’s the result of the final result. The only missing planned feature (at this time) is the ability to upload files and embed images.

The "Link Creator"
There was another interface-related thing motivating me to make my own wiki. When I tried to explain to my "users" how DocuWiki makes new pages and links to existing pages, I found myself saying, "Uh, you’ll get the hang of this eventually," several times.
Linking to an existing page requires knowing the name of that page.
Creating a new page is the same in most wikis ever since the original: you create a link to a page that doesn’t yet exist and then you start editing it.
I really wanted an interface to assist in both tasks, especially fuzzy-searching for existing pages. So the "Link Creator" was born:

The tool is a PHP page that reads the pages/
directory to get all of the
existing page filenames and displays the shown user interface.
You brink it up by clicking that Link button. I display the page in an
<iframe>
and use CSS absolute positioning plus JavaScript to put the tool
below the button. A drop-shadow helps make it clear that you’re interacting
with something "on top of" the page. The link creator only works when you’re
editing on the "rich text" side of the editor.
If you type the title of a new page in the field at the top, a path-safe file name is suggested below it.
If you fuzzy-search for an existing page, the list of buttons at the bottom is filtered to your search. You can see that in the screenshot above: I’ve typed "awe page" and the two closest matches are displayed.
If you click on a page button, the link name and title are populated in the fields.
Finally, you click the Create! button to insert the newly-minted link at your cursor position in the rich-text side of the editor.
It’s brutally simple, but highly effective. I was pretty surprised how quickly I was able to make this. Especially since I also took the time to experiment with a couple fuzzy-search methods.
My favorite was this four-line beauty I adapted from a Stack Overflow answer:
function fuzzy_match2(haystack, needle){ // Adapted from https://stackoverflow.com/a/15252131 var hay = haystack.toLowerCase(), i = 0, n = -1, l; s = needle.toLowerCase(); for (; l = s[i++] ;) if (!~(n = hay.indexOf(l, n + 1))) return false; return true; }
It’s a bit "golfed," but this fuzzy matcher simply checks that the characters in the needle can be found in the haystack in the same order. It’s amazing how well it works! I think the results are actually more intuitively "correct" than just about any other method this compact, including ones based on more sophisticated methods like measuring Levenshtein distance.
Embracing the rich text editing weirdness
Everything about the contentEditable
attribute and the
document.execCommand()
method is weird and janky. But making peace with it
has been surprisingly easy.
I’ve embraced the built-in functionality. My interface buttons mostly just use what’s available.
The bold, italic, and list buttons are literally just buttons with a <b>B</b>
and <i>I</i>
on them. They call the "bold" and "italic" commands like so:
document.execCommand('bold', false); document.execCommand('italic', false);
My list button is especially funny. Check out how I made the "graphic" of a list with ASCII art:
<button id="tool_ul"> <div style="font-size: 7px; line-height:-5px;"> o ---<br> o --- </div> </button>
And it inserts a new list in the document like so:
document.execCommand('insertHTML', false, '<ul><li>One</li><li>Two</li></ul>');
Simple, right?
The key is to just relax and embrace how it works. The browser is giving you a huge amount of functionality. Don’t fight it.
Just never forget: WYSIWYG rich text editing is fast and convenient and mostly comfortable, but in my experience, it will always let you down when things get complicated.
My escape hatch is being able to access the raw HTML at all times.
And the split-pane editor lets me work on a page of any complexity and still be able to type paragraphs of text like a word processor. I can do both things at the same time without "switching modes".
In short, it’s a Beauty and Beast combo that works really well.
Speaking of beasts, onward, to The Wiki Weekend Part 3: The Storage!