garann means > totes profesh

a defunct web development blog

making a really simple text editor

Sat, 11 Dec 2010 00:53:13 +0000

A few months ago I started thinking I should write my own text editor. Not because the existing options were bad, mind you, but because they were too much. At work, we take a lot of user content. This content gets displayed on pages that aren't designed to support font colors, images, tables, etc. Yet it can get long, and it's nice to provide people the ability to bold or italicize their text, to break it up with numbered or bulleted lists, and to add links instead of pasting in URLs. All the existing options handle that use case just fine. But the overhead - both in terms of file size and in terms of time to configure - was starting to get to me.

This week, I finally went and wrote my own solution. It's still a work in progress but, having waded through the code for a bunch of RTEs, it was a hell of a lot easier than I expected to get it to the point it's at now.

Sometimes there are reasons to avoid a custom solution, but in this case, I feel a custom solution was warranted. We use text editors at work in a variety of applications, and none of those are applications that we're trying to turn into MySpace. This means, as a company, we have two options with regard to an existing editor. We can either download it, customize it, and then try to make the same updates in each new application, or we can "fork" whatever's available at the moment and start passing around a customized plugin that needs minimal setup. I should be clear that by "customize," in most cases, I mean "turn a bunch of shit off". Not having to turn the same shit off over and over or risk changing the plugin itself so much that it can't be updated make writing a custom solution worthwhile to me. Especially considering how fucking easy it is.

The editor I came up is really just a div that makes use of contenteditable. There's some trickier shit, and I'll discuss that toward the end, but by and large it's doing what browsers support natively. To my mind, that means this sort of tool is edging toward the threshold of "writing markup" and away from "writing JavaScript plugins". To anyone lucky enough to only be supporting modern browsers and IEs 7+, who doesn't need word processing in a web page, who's still downloading and customizing the shit out of someone else's generic use case text editor, I would ask why? The tools required to do this job are now pretty standard. Yes, they generate slightly different markup and, yes, there are still more complicated things to deal with when it comes to working with selected text. But those challenges are surmountable.

If you haven't yet fooled around with contenteditable and you take any sort of user content, I'd encourage you to. If you need convincing that this is powerful and very handy stuff, please consider Mozilla's rundown of designMode commands. You can do the basic stuff in all modern and semi-modern browsers. You can do the cool stuff in browsers that aren't IE. You can go beyond editing text if you need to. And it's as simple as:

document.execCommand("bold",null,null);

What you get with contenteditable is probably not everything you need, but it's close. I wrote my own function to strip out HTML, because I wanted to be able to white-list some tags. Spell-check is something I'm still trying to figure out how to handle. And even defining a URL for a link requires you to look into ranges and selections a little bit.

What I found a little odd about all the designMode commands was that they don't operate on a DOM node as I'd normally think of it, but on whatever's currently selected in the document. This kind of makes sense, because otherwise it would be tricky to define a text node of arbitrary size and content to make bold, for example. And I guess it's just an artifact of dealing with text rather than the tidy hierarchies of elements you'd generally establish to style something or be able to wrap it in a link, like wrapping a span around the numeric portion of a character counter. On the positive side, it doesn't require a whole ton of code. It seems like I've got selections and ranges in modern browsers and IE supported with just these functions (knock wood):

function getRange() {
	return window.getSelection ? 
		window.getSelection().getRangeAt(0) : 
		document.selection.createRange();
}
function setSelection(range) {
	if (range.select) {
		range.select();
	} else {
		var selection = window.getSelection();
		selection.removeAllRanges();
		selection.addRange(range);
	}
}

As you can see, there are two different ways of handling this stuff. Unfortunately, that's not the end of it. When you select a range, you may or may not get a text node you can work with. Modern browsers will let you take that node and make it a jQuery object (or whatever else you want to do to it). In IE, you need to get the node's parent before you can work with it, then add that parent to the set of elements you're working with. That probably doesn't sound that onerous, right? It's really not. Little feature sniffing, no big deal. What worries me is how I'm going to handle spell-checking. It seems you could use ranges to wrap misspelled words in some tag to message that to the user, but ranges don't come from a string of HTML you ran a regular expression on to figure out what's a word and what's a tag. If you get just the text in the editor, it's going to be tricky to find the offset and starting/ending nodes that define a range. Maybe I'm just looking at it wrong and, although ranges are the obvious way to handle other text editing functions, they're no good for that scenario.

But like I said, don't let that dissuade you. Lots of browsers have native spell-checking and it works fine in an element with contenteditable applied. If you need a simple text editor, you can probably write your own pretty easily. I think it's worth considering that a text editor may not need to be a special fancy plugin but might be able to be just another form field you use JavaScript to add a little bling to. It really seems to me that it's getting close.