Content Editable Text Editors

Content Editable Text Editors

At the beginning I should mention that I'm a CKEditor core developer, thus my opinion may not be fully objective :).

The state of HTML editing AFAIK hasn't changed for the last few years. Browsers vendors have spent little time fixing bugs - so little that most of the oldest bugs on CKEditor trac that were caused by browser issues are still valid and most of hacks we've ever created are still required. And I don't only mean IE 7 or 8 which we still support, because in fact IEs may not be the worst. I haven't checked that precisely, but I think that thanks to general improvements for DOM APIs in recent IEs versions they may require less hacks than other browsers, because its editing support seems to be the most stable and complete. E.g. the ugliest hack ever is needed for Webkit browsers (see: Webkit bug and closed CKEditor ticket) and this bug is 5 year old. What's more - this isn't an edge case or unlikely scenario - this issue makes it impossible to create whole range of selections - e.g. inside an empty inline element IIRC.

So, unlike the web technologies in general, HTML editing stands where it was left 10 years ago. The same bugs, the same missing features, the same... markup. Guess what is created by the fontName command? <font face=""> - yep, not joking. At least browsers are very consistent here... But the consistency very quickly disappears.

What about the spec? There's a draft, but it does not help at all - it just standardizes the current state, which as we know does not look very well. And AFAICS, the draft is dead.

And there's one more thing that worries me - direction in which editing goes. Google uses contenteditable in Gmail (no, Google Docs is not built on contenteditable), so it does not care about HTML output. Apple reuses HTML editing component in their email app for iOS and perhaps in Mail for MacOS (because I see the same specific behaviours). Mozilla reuses Gecko in Thunderbird and I it wouldn't surprised if Microsoft does the same in Outlook. All of them do not care about HTML output. These engines are made to understand every crap rather than fix it and the HTML Editing draft is all about it.

Thanks to all of that, we can see new issues like this one. In the Blink/Webkit bug report I summarized whole range of incorrect (from our POV - devs who care about HTML) results when pressing backspace. It is designed to look nice (although it does not), but the HTML and editing APIs are not important.

I don't care about this. I need an editor!

The solution to all of that is fixing everything after browsers and/or replace their native implementations by our own. For the last 1.5 years I've been working nearly exclusively on filtering and normalizing input and output. In CKEditor 4.0 we rewrote entire pasting and HTML insertion processes and in recently released CKEditor 4.1 we introduced the Advanced Content Filter which adapts input data to the editor configuration. So the less features are enabled, the less will be allowed in HTML. Check this sample - try to paste/write/create crappy HTML.

Of course, there's still a room for improvements. E.g. we are not able to filter data immediately during editing. If browser (like Webkit) creates a mess we can fix that on output, but in fact we haven't decided to do that because filtering is really complex process and could spoil the performance. We limit input and user actions so one day we will implement our own backspace/delete handlers to prevent browsers from messing our HTML. That's the only solution and this is why there are just 2 or 3 good WYSIWYG editors.

Anyway, nearly nothing has changed in browsers' HTML editing implementations, but a lot has changed in WYSIWYG editors world. I advice you to check them again if you base on your experience from few years back.

What contenteditable editors are there?

This is a community wiki answer. You can edit me with your improvements. Alphabetically sorted.

Inline

Inline editors differ from normal editors as they can edit the content directly, i.e. not placing it inside another element, or inside an iframe.

  • Aloha Editor inactive since May 2016
  • CKEditor - starting from CKEditor 4 Beta
  • Etch
  • FresherEditor inactive since November 2012
  • Hallo
  • HTML5 Edit inactive since July 2011
  • Mercury inactive since December 2013
  • NicEdit
  • Quill - starting from v0.19
  • RedactorJS
  • TinyMCE when used with Inline mode
  • wysiwyg.js
  • medium.js (library independent)
  • ContentTools (Library independent)
  • Froala WYSIWYG (jQuery)
  • Trix
  • Trumbowyg (jQuery)
  • editable.js
  • wysihtml

Iframes

Iframe editors often load the editor or content to edit into an iframe, to avoid styling conflicts.

  • bootstrap-wysihtml5
  • CKEditor
  • Mercury inactive since December 2013
  • TinyMCE
  • WYSIHTML5 inactive since December 2014

Create rich text editor with content editable div in asp.net web forms

The problem is solved with using an editor in my project.

Rich Text Editing - designMode vs contentEditable

You probably want contentEditable. designMode applies to the document, contentEditable to a specific element and its children.

http://blog.whatwg.org/the-road-to-html-5-contenteditable

Autostyle input text in contenteditable div while typing

const SpecialWords = [
"happy",
"sad"//style word
];
const WordColors = [
"styleA",
"styleB"//style class name
];
document.getElementById('texteditor').addEventListener('keyup', function(e) {
styleCode(); //Style the text input

});

//Change the result of pressing Enter and Tab keys
document.getElementById('texteditor').addEventListener('keydown', function(e) {
switch (e.key) {
case 'Tab':
e.preventDefault();
document.execCommand('insertHTML', false, ' '); //Insert a 4-space tab
break;

case 'Enter':
e.preventDefault();
document.execCommand("insertLineBreak"); //Insert a new line
break;
}
});
var oldWord = "";//initialise
function styleCode() {

//Style the code in the input box
var wordList = document.getElementById('texteditor').innerText.split(" ");
/*if old word is same as now then it means we have presed arrow key or caps or so,it do not wan't to style now as no change*/
if(!(oldWord == document.getElementById('texteditor').innerText)){
var oldPos = getCaretPosition(document.getElementById('texteditor'));//backup old position of cursor
for (let n = 0; n < SpecialWords.length; n++) {
var res = replaceAll(wordList,SpecialWords[n],`<span class="${WordColors[n]}">${SpecialWords[n]}</span>`).join(" ");//style adding
}
document.getElementById('texteditorS').innerHTML=res;
setCursor(oldPos,document.getElementById('texteditor'));//set back cursor position
}

oldWord = document.getElementById('texteditor').innerText;//old word for next time's reference

}

function replaceAll(array, find, replace) {
var arr = array;
for (let i = 0; i < arr.length; i++) {
if (arr[i] == find)
arr[i] = replace;
}
return (arr);
}
function getCaretPosition(editableDiv) {
var caretPos = 0,
sel, range;
if (window.getSelection) {
sel = window.getSelection();
if (sel.rangeCount) {
range = sel.getRangeAt(0);
if (range.commonAncestorContainer.parentNode == editableDiv) {
caretPos = range.endOffset;
}
}
} else if (document.selection && document.selection.createRange) {
range = document.selection.createRange();
if (range.parentElement() == editableDiv) {
var tempEl = document.createElement("span");
editableDiv.insertBefore(tempEl, editableDiv.firstChild);
var tempRange = range.duplicate();
tempRange.moveToElementText(tempEl);
tempRange.setEndPoint("EndToEnd", range);
caretPos = tempRange.text.length;
}
}
return caretPos;
}
function setCursor(pos,editableDiv) {
if(!(pos == 0)){//if 0 it gives unwanted error
var tag = editableDiv;

// Creates range object
var setpos = document.createRange();

// Creates object for selection
var set = window.getSelection();

// Set start position of range
setpos.setStart(tag.childNodes[0], pos);

// Collapse range within its boundary points
// Returns boolean
setpos.collapse(true);

// Remove all ranges set
set.removeAllRanges();
// Add range with respect to range object.
set.addRange(setpos);

// Set cursor on focus
tag.focus();
}
}
.edit {
border: 3px solid black;
width: 100%;
height: 500px;
;
overflow: auto;
flex: 1;
word-wrap: break-word;
word-break: break-all;
white-space: pre-wrap;
padding: 5px;
font-family: Consolas, "courier new";
font-size: 14px;
}

.styleA {
color: red;
}

.styleB {
color: blue;
}
#texteditorS{
pointer-events:none; /*click through*/
position:relative;
bottom: calc(500px + 2 * (5px + 3px));/*overlay on top of editable div,500px is height,5px is padding,3px is border*/

}
<div id='texteditor'  class="edit" contenteditable></div>
<div id='texteditorS' class="edit"></div>

Javascript rich text editor, contenteditable area loses focus after button is clicked

Well the focus moves to the button so you need to cancel the click action so the focus is not lost in the content editable element.

document.querySelector(".actions").addEventListener("mousedown", function (e) {  var action = e.target.dataset.action;  if (action) {    document.execCommand(action, false)    //prevent button from actually getting focused    e.preventDefault();  }})
[contenteditable] {  width: 300px;  height: 300px;  border: 1px solid black;}
<div class="actions">  <button data-action="bold">bold</button>  <button data-action="italic">italic</button></div><div contenteditable="true"></div>

Populate and save text from contenteditable

To create your Right Text Editor you could use the contenteditable attribute on a <div> element or via JS designMode. You don't need an <iframe>.

Note: while execCommand works on all major browser with its quirks and differences - it's now deprecated (but without any sane workaround but creating your own inline content editing software or by using Input-Events) so use with care.

To let you start from somewhere,

$(function() {

var $editor = $('#textEditor');
var $btn = $('span[data-cmd]');

// EXECUTE COMMAND
function execCmd(cmd, arg) {
document.execCommand(cmd, false, arg);
}

$btn.mousedown(function(e) {
e.preventDefault();
$(this).toggleClass("selected");
execCmd(this.dataset.cmd);
});

$("#fonts").change(function() {
execCmd("FontName", this.value);
});

});
* {margin: 0; padding: 0;}

#textEditorTab span {
cursor: pointer;
display: inline-block;
padding: 0px 10px 0px 10px;
}

#textEditorTab span:hover {
background: #eee;
}

#textEditorTab span.selected {
background-color: orange;
}

#textEditor {
width: 300px;
height: 120px;
border: 1px solid #ddd;
padding: 5px;
}
<div id="textEditorTab">
<span data-cmd="bold"><b>B</b></span>
<span data-cmd="italic"><i>I</i></span>

<select id="fonts">
<option value="Arial">Arial</option>
<option value="Comic Sans MS">Comic Sans MS</option>
<option value="Courier New">Courier New</option>
<option value="Monotype Corsiva">Monotype</option>
<option value="Tahoma">Tahoma</option>
<option value="Times">Times</option>
</select>
</div>

<div id="textEditor" contenteditable>Lorem ipsum dolor sit amet</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>


Related Topics



Leave a reply



Submit