An ember addon that provides an editor based on contenteditable.

Downloads in past


210.26.83 years ago5 years agoMinified + gzip package size for @lblod/ember-contenteditable-editor in KB


Emberjs addon that provides a content editable component. This component is the core of ember-rdfa-editor, a more feature complete component providing a toolbar and support for plugins.
ember-contenteditable-editor features: - it's fast - custom input handling through handlers - provides an interface to make selections and describe updates that can be used to interact with the content - sends out diff events (text removals and additions) through actions - sends out selection updates through actions
Supported browsers: - Chrome (latest 3) - Firefox (latest 3) - Edge (limited support)


ember install @lblod/ember-contenteditable-editor


  • Ember.js v3.4 or above
  • Ember CLI v2.13 or above
  • Node.js v8 or above


See the Contributing guide for details.


This addon provides a component content-editable that can be included in your application.
   textInsert=(action 'handleTextInsert')
   textRemove=(action 'handleTextRemove')
   handleFullContentUpdate=(action 'handleFullContentUpdate')
   selectionUpdate=(action 'selectionUpdate')
   rawEditorInit=(action 'handleRawEditorInit')
<p>initial content</p>

integrating the contenteditable component

The contenteditable component provides a number of actions and an api to interact with it's content and input. After the component is rendered it will call the rawEditorInit action with an object rawEditor that contains attributes and apis to interact with the editor. These are detailed further down
When the editor detects a change in its text content a diff is calculated and the textInsert and textRemove actions are called for each insert and removal that was detected. (note: they are not called for html updates that do not change the text content). The handleFullContentUpdate action is called when the entire diff was processed. These actions can be used to trigger your own logic on content updates.
The action selectionUpdate is called when the cursor position was changed or a selection was made.
Whether or not the content is editable can be set using the editable property, if focused is set the editor tries to autofocus itself. yieldHTML determines whether the editor should consider the yielded content as html or text (respectively a {{{content}}} or {{content}} render of the provided content).

rawEditor interface

A short summary of the object is listed here: rawEditor.textContent string current text content of the editor rawEditor.currentSelection array the current selection rawEditor.richNode Object internal representation of the editor content rawEditor.rootNode DOMElement the dom element that contains the editor content rawEditor.selectCurrentSelection() function returns a Selection for the current selected range that can be used in an update operation rawEditor.selectHighlight([start,end], options) function returns a Selection for the specified range, this selection can be used in an update operation rawEditor.selectContent([start, end], options) function returns a Selection based on an RDFa context that was specified in the options. This selection can be used in an update operation rawEditor.update(selection, { remove, add, set, desc}) function Alters a selection from the API described above rawEditor.replaceDomNode(domNode, { callback, failedCallback, motivation}) function verifies the dom node is present in the internal representation and executes callback if succesfull, calls failedCallback if not. rawEditor.applyProperty(selection, property) function applies an EditorProperty to the selection rawEditor.cancelProperty(selection, property) function cancels an EditorProperty on the selection rawEditor.toggleProperty(selection, property) function toggles an EditorProperty on the selection rawEditor.togglePropertyAtCurrentPosition(property) function toggles an EditorProperty on the current position, assumes a collapsed selection.
Other methods and attributes are available but should be considered internal or deprecated. A more complete description of this object is available in the (documented) code: raw-editor.js

Responding to input using handlers

All keyboard and mouse input is captured in the content-editable component. These events are then passed on to inputHandlers.
An input handler should implement at least the following methods:
  • isHandlerFor(event), which tests if the handler can/will handle the provided event
  • handleEvent(event), which handles the event

For example
import EmberObject from '@ember/object';
import { reads, alias } from '@ember/object/computed';
import HandlerResponse from '@lblod/ember-contenteditable-editor/utils/handler-response';
import { get } from '@ember/object';

 * Event Handler, a event handler to handle everything
 * @module contenteditable-editor
 * @class EventHandler
 * @constructor
 * @extends EmberObject
export default EmberObject.extend({
  currentNode: alias('rawEditor.currentNode'),
  currentSelection: reads('rawEditor.currentSelection'),
  richNode: reads('rawEditor.richNode'),
  rootNode: reads('rawEditor.rootNode'),
   * tests this handler can handle the specified event
   * @method isHandlerFor
   * @param {DOMEvent} event
   * @return boolean
   * @public
  isHandlerFor(event) {
    return true

   * handle the event
   * @method handleEvent
   * @param {DOMEvent} event
   * @return {HandlerResponse} response
  handleEvent() {
    return HandlerResponse.create({});

You can provide externalHandlers by providing them in a sorted array to the component.
    handleRawEditorInit(editor) {
      this.set('editor', editor);
      this.set('handlers', [MyHandler.create({rawEditor: editor })]);
# hbs
{{content-editable externalHandlers=handlers}}

editor properties

ember-contenteditable-editor provides editor properties to handle things like bold, italic and more complex cases. An editor property is considered to be active in a position in the tree based on any of its parent nodes, and which can be split/merged at will. This allows the ditor to optimize the html tree when required.
For example "bold" could be represented by the following:
import EditorProperty from '@lblod/ember-contenteditable-editor/utils/editor-property';

class BoldProperty extends EditorProperty {
  constructor({tagName = 'strong', newContext = true}) {
    super({tagName, newContext});
  enabledAt(richNode) {
    if (!richNode)
      return false;
    if (richNode.type === 'text') {
      return window.getComputedStyle(richNode.parent.domNode).fontWeight > 400;
    else if (richNode.type === 'tag') {
      return window.getComputedStyle(richNode.domNode).fontWeight > 400;
      return false;
const boldProperty = new BoldProperty({});
export default boldProperty;

boldProperty could then be used to mark a selection as bold using rawEditor.toggleProperty([start,end], boldProperty)