Content to edit
Good support: Chrome, Safari, Firefox, Opera, IE (>=5.5), iOS Safari, Opera Mobile and so on.
These allow us to programmatically access information about the text a user has selected on the page. Typically you'll deal with a selection, which contains a single range (although you can have multiple).
// Grabbing the selection
var selection = window.getSelection();
// Grabbing the range
var range = selection.getRangeAt(0);
// We can now grab some useful information via properties
range.collapsed;
range.commonAncestorContainer;
range.startContainer;
range.startOffset;
range.endContainer;
range.endOffset;
// Or manipulate the range in some way with the many methods
range.selectNode();
range.setStart();
range.setEnd();
Ranges give us a lot of power, they can just be a little clunky to work with sometimes. Here's an example that would insert text at the cursor position:
function insertTextAtCaret(text) {
var sel, range;
sel = window.getSelection();
if (sel.rangeCount) {
range = sel.getRangeAt(0);
range.deleteContents();
var textNode = document.createTextNode(text);
range.insertNode(textNode);
// Show the selection
range = range.cloneRange();
range.setStartAfter(textNode);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
}
}
The following selection would return us the following information when calling window.getSelection().getRangeAt(0);
:
{
collapsed: false // If collapsed there is no selection (highlighted text), just the cursor
commonAncestorContainer: span.st // Reference to a regular DOM node
endContainer: text // node type
endOffset: 113 // character offset
startContainer: text // node type
startOffset: 0 // character offset
}
When used in conjunction with contenteditable
execCommand()
will apply formatting to the currently selected text within the focussed editable element. There's plenty of options, but beware browser inconsistencies.
document.execCommand('bold');
document.execCommand('italic');
document.execCommand('undo');
document.execCommand('redo');
document.execCommand('strikeThrough');
document.execCommand('insertUnorderedList');
document.execCommand('insertOrderedList');
document.execCommand('subscript');
document.execCommand('superscript');
document.execCommand('createLink', false, url); // Sometimes we need to pass some extra parameters
document.execCommand('unlink');
// Allows us freedom to 'fill the gaps' for block level elements. But, this is also inconsistent between browsers.
document.execCommand('formatBlock', false, 'blockquote');
To know if a certain type of formatting is applied to the currently selected text you can use document.queryCommandState('bold');
which will return a boolean to work with.
Great for those toolbars etc, but this gets a bit wonky with block level elements.
execCommand()
ctrl + a
and pressing backspaceHere's some examples of each browsers way of handling the enter key
// Chrome
Text
// Firefox
Text
// IE
Text
Some editors fully override the editable area.
The problem with manually doing everything, i.e. using selections, ranges and DOM manipulation rather than using document.execCommand()
to format text, is that the browsers undo / redo stack will be rendered useless. You could roll your own, of course, but this is quite tricky to do.
There is a spec for a proper, custom undo / redo API. Some parts are already in Webkit and Firefox.
IE11 already has ms-beginUndoUnit
and ms-endUndoUnit
By default content will be pasted in with some sad panda HTML and inline styles. Unless you are truly pasting plain text.
We can always listen out for the paste event like so:
element.addEventListener('paste', handlePaste);
Now we can use clipboardData()
to access the clipboard's contents
function handlePaste(e) {
if (e.clipboardData) {
var plainText = e.clipboardData.getData('text/plain');
var html = e.clipboardData.getData('text/html');
}
else {
// IE
if (window.clipboardData) {
var plainText = window.clipboardData.getData('Text');
var url = window.clipboardData.getData('URL');
}
}
}