medium-editor
Medium.com WYSIWYG editor clone. Uses contenteditable API to implement a rich text solution.
Top Related Projects
Powerful rich text editor framework with a modular architecture, modern integrations, and features like collaborative editing.
Quill is a modern WYSIWYG editor built for compatibility and extensibility
The world's #1 JavaScript library for rich text editing. Available for React, Vue and Angular
A React framework for building text editors.
The next generation Javascript WYSIWYG HTML Editor.
Super simple WYSIWYG editor
Quick Overview
Medium Editor is a lightweight, extensible, and customizable WYSIWYG (What You See Is What You Get) text editor for the web. It aims to replicate the editing experience of Medium.com, providing a clean and intuitive interface for content creation.
Pros
- Easy to integrate and use with minimal setup
- Highly customizable with a wide range of options and extensions
- Lightweight and performant, with no external dependencies
- Active community and regular updates
Cons
- Limited advanced formatting options compared to more complex editors
- May require additional plugins for certain features (e.g., tables, code blocks)
- Some users report occasional inconsistencies in behavior across different browsers
Code Examples
- Basic initialization:
var editor = new MediumEditor('.editable');
This code initializes Medium Editor on all elements with the class 'editable'.
- Customizing toolbar buttons:
var editor = new MediumEditor('.editable', {
toolbar: {
buttons: ['bold', 'italic', 'underline', 'anchor', 'h2', 'quote']
}
});
This example creates an editor with a custom toolbar containing specific formatting options.
- Using extensions:
var editor = new MediumEditor('.editable', {
extensions: {
'imageDragging': {}
}
});
This code adds the image dragging extension to the editor, allowing users to drag and drop images into the content area.
Getting Started
To get started with Medium Editor, follow these steps:
- Include the Medium Editor files in your HTML:
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/medium-editor/5.23.3/css/medium-editor.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/medium-editor/5.23.3/js/medium-editor.min.js"></script>
- Create an editable element in your HTML:
<div class="editable">
This is editable content.
</div>
- Initialize Medium Editor in your JavaScript:
var editor = new MediumEditor('.editable');
That's it! You now have a basic Medium Editor instance running on your page. You can further customize it by adding options and extensions as needed.
Competitor Comparisons
Powerful rich text editor framework with a modular architecture, modern integrations, and features like collaborative editing.
Pros of CKEditor 5
- More feature-rich and customizable, offering a wide range of plugins and tools
- Better documentation and extensive API, making it easier for developers to integrate and extend
- Stronger focus on accessibility and compliance with web standards
Cons of CKEditor 5
- Heavier and more complex, which may impact performance for simpler use cases
- Steeper learning curve due to its extensive features and configuration options
- Commercial licensing for some features, potentially increasing costs for certain projects
Code Comparison
Medium Editor:
var editor = new MediumEditor('.editable', {
toolbar: {
buttons: ['bold', 'italic', 'underline']
}
});
CKEditor 5:
ClassicEditor
.create(document.querySelector('#editor'), {
toolbar: ['bold', 'italic', 'link']
})
.catch(error => {
console.error(error);
});
Both editors allow for easy initialization and basic toolbar configuration. However, CKEditor 5 uses a more modern, promise-based approach and offers more advanced customization options out of the box.
Quill is a modern WYSIWYG editor built for compatibility and extensibility
Pros of Quill
- More comprehensive API and customization options
- Better documentation and active community support
- Built-in modules for advanced features like syntax highlighting and formula editing
Cons of Quill
- Larger file size and potentially heavier performance impact
- Steeper learning curve for basic implementations
- Less flexibility for simple, lightweight use cases
Code Comparison
Medium Editor:
var editor = new MediumEditor('.editable', {
toolbar: {
buttons: ['bold', 'italic', 'underline']
}
});
Quill:
var quill = new Quill('#editor', {
modules: {
toolbar: [
['bold', 'italic', 'underline']
]
},
theme: 'snow'
});
Summary
Medium Editor is lightweight and easy to implement for basic rich text editing needs. It's ideal for projects requiring minimal setup and customization.
Quill offers more advanced features and customization options, making it suitable for complex editing requirements. However, it comes with a larger footprint and may be overkill for simpler use cases.
Both libraries provide similar core functionality, but Quill's extensive API and module system allow for more sophisticated implementations at the cost of increased complexity and resource usage.
The world's #1 JavaScript library for rich text editing. Available for React, Vue and Angular
Pros of TinyMCE
- More feature-rich and customizable, offering a wide range of plugins and options
- Better cross-browser compatibility and support for older browsers
- Extensive documentation and community support
Cons of TinyMCE
- Larger file size and potentially slower load times
- Steeper learning curve for configuration and customization
- May be overkill for simple text editing needs
Code Comparison
TinyMCE initialization:
tinymce.init({
selector: '#myTextarea',
plugins: 'link image table',
toolbar: 'undo redo | formatselect | bold italic | alignleft aligncenter alignright | link image'
});
Medium Editor initialization:
var editor = new MediumEditor('.editable', {
toolbar: {
buttons: ['bold', 'italic', 'underline', 'anchor', 'h2', 'h3', 'quote']
},
placeholder: {
text: 'Type your text'
}
});
TinyMCE offers more built-in options and plugins, while Medium Editor provides a simpler, more lightweight approach. TinyMCE is better suited for complex editing needs, while Medium Editor is ideal for basic text formatting and a Medium-like editing experience.
A React framework for building text editors.
Pros of Draft.js
- Built on React, offering seamless integration with React applications
- Immutable data model for better performance and predictable state management
- Extensive customization options for complex editing scenarios
Cons of Draft.js
- Steeper learning curve due to its complex API and concepts
- Heavier bundle size, which may impact load times for smaller projects
- Less suitable for simple, lightweight editing needs
Code Comparison
Medium Editor:
var editor = new MediumEditor('.editable', {
toolbar: {
buttons: ['bold', 'italic', 'underline']
}
});
Draft.js:
const editorState = EditorState.createEmpty();
<Editor
editorState={editorState}
onChange={setEditorState}
plugins={[boldPlugin, italicPlugin, underlinePlugin]}
/>
Key Differences
Medium Editor is a lightweight, standalone solution ideal for simple editing needs, while Draft.js is a powerful, React-based framework suitable for complex editing scenarios. Medium Editor offers easier setup and faster implementation, whereas Draft.js provides more robust features and better integration with React ecosystems at the cost of increased complexity.
The next generation Javascript WYSIWYG HTML Editor.
Pros of wysiwyg-editor
- More extensive feature set, including image editing and file management
- Better cross-browser compatibility and mobile support
- Regular updates and active development
Cons of wysiwyg-editor
- Commercial product with licensing fees, unlike the open-source medium-editor
- Steeper learning curve due to more complex API and configuration options
- Larger file size and potential performance impact on web pages
Code Comparison
medium-editor initialization:
var editor = new MediumEditor('.editable', {
toolbar: {
buttons: ['bold', 'italic', 'underline', 'anchor', 'h2', 'h3', 'quote']
}
});
wysiwyg-editor initialization:
new FroalaEditor('#editor', {
toolbarButtons: ['bold', 'italic', 'underline', 'insertLink', 'insertImage', 'insertTable'],
imageUploadURL: '/upload_image',
fileUploadURL: '/upload_file'
});
Both editors offer customizable toolbar options, but wysiwyg-editor provides more advanced features like image and file uploading out of the box. medium-editor focuses on simplicity and ease of use, while wysiwyg-editor offers a more comprehensive set of tools at the cost of increased complexity and licensing requirements.
Super simple WYSIWYG editor
Pros of Summernote
- More feature-rich out of the box, including image uploading and video embedding
- Better support for Bootstrap integration
- Extensive plugin ecosystem for additional functionality
Cons of Summernote
- Larger file size and potentially slower performance
- Steeper learning curve due to more complex API
- Less customizable appearance compared to Medium Editor
Code Comparison
Medium Editor initialization:
var editor = new MediumEditor('.editable', {
toolbar: {
buttons: ['bold', 'italic', 'underline', 'anchor']
}
});
Summernote initialization:
$('#summernote').summernote({
toolbar: [
['style', ['bold', 'italic', 'underline', 'clear']],
['insert', ['link', 'picture', 'video']]
]
});
Both editors offer simple initialization, but Summernote's default configuration includes more advanced features like image and video insertion. Medium Editor focuses on a minimalistic approach, requiring less initial setup for basic text editing functionality.
While Medium Editor provides a lightweight solution with high customizability, Summernote offers a more comprehensive set of features out of the box. The choice between the two depends on the specific requirements of your project, balancing between simplicity and feature richness.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME

If you would be interested in helping to maintain one of the most successful WYSIWYG text editors on github, let us know! (See issue #1503)
MediumEditor
This is a clone of medium.com inline editor toolbar.
MediumEditor has been written using vanilla JavaScript, no additional frameworks required.
Browser Support
Basic usage
Demo
demo: http://yabwe.github.io/medium-editor/
Installation
Via npm:
Run in your console: npm install medium-editor
Via bower:
bower install medium-editor
Via an external CDN
- Using jsDelivr.
For the latest version:
<script src="//cdn.jsdelivr.net/npm/medium-editor@latest/dist/js/medium-editor.min.js"></script>
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/medium-editor@latest/dist/css/medium-editor.min.css" type="text/css" media="screen" charset="utf-8">
For a custom one:
<script src="//cdn.jsdelivr.net/npm/medium-editor@5.23.2/dist/js/medium-editor.min.js"></script>
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/medium-editor@5.23.2/dist/css/medium-editor.min.css" type="text/css" media="screen" charset="utf-8">
- Using CDNJS.
Manual installation:
Download the latest release and attach medium editor's stylesheets to your page:
Find the files to below mentioned linking in the dist folder. (./medium-editor/dist/...)
<link rel="stylesheet" href="css/medium-editor.css"> <!-- Core -->
<link rel="stylesheet" href="css/themes/default.css"> <!-- or any other theme -->
Usage
The next step is to reference the editor's script
<script src="js/medium-editor.js"></script>
You can now instantiate a new MediumEditor object:
<script>var editor = new MediumEditor('.editable');</script>
The above code will transform all the elements with the .editable class into HTML5 editable contents and add the medium editor toolbar to them.
You can also pass a list of HTML elements:
var elements = document.querySelectorAll('.editable'),
editor = new MediumEditor(elements);
MediumEditor also supports textarea. If you provide a textarea element, the script will create a new div with contentEditable=true
, hide the textarea and link the textarea value to the div HTML content.
Integrating with various frameworks
People have contributed wrappers around MediumEditor for integrating with different frameworks and tech stacks. Take a look at the list of existing Wrappers and Integrations that have already been written for MediumEditor!
MediumEditor Options
View the MediumEditor Options documentation on all the various options for MediumEditor.
Options to customize medium-editor are passed as the second argument to the MediumEditor constructor. Example:
var editor = new MediumEditor('.editor', {
// options go here
});
Core options
- activeButtonClass: CSS class added to active buttons in the toolbar. Default:
'medium-editor-button-active'
- buttonLabels: type of labels on the buttons. Values:
false
| 'fontawesome'. Default:false
NOTE:
Using 'fontawesome'
as the buttonLabels requires version 4.1.0 of the fontawesome css to be on the page to ensure all icons will be displayed correctly
- delay: time in milliseconds to show the toolbar or anchor tag preview. Default:
0
- disableReturn: enables/disables the use of the return-key. You can also set specific element behavior by using setting a data-disable-return attribute. Default:
false
- disableDoubleReturn: allows/disallows two (or more) empty new lines. You can also set specific element behavior by using setting a data-disable-double-return attribute. Default:
false
- disableExtraSpaces: when set to true, it disallows spaces at the beginning and end of the element. Also it disallows entering 2 consecutive spaces between 2 words. Default:
false
- disableEditing: enables/disables adding the contenteditable behavior. Useful for using the toolbar with customized buttons/actions. You can also set specific element behavior by using setting a data-disable-editing attribute. Default:
false
- elementsContainer: specifies a DOM node to contain MediumEditor's toolbar and anchor preview elements. Default:
document.body
- extensions: extension to use (see Custom Buttons and Extensions) for more. Default:
{}
- spellcheck: Enable/disable native contentEditable automatic spellcheck. Default:
true
- targetBlank: enables/disables target="_blank" for anchor tags. Default:
false
Toolbar options
The toolbar for MediumEditor is implemented as a built-in extension which automatically displays whenever the user selects some text. The toolbar can hold any set of defined built-in buttons, but can also hold any custom buttons passed in as extensions.
Options for the toolbar are passed as an object that is a member of the outer options object. Example:
var editor = new MediumEditor('.editable', {
toolbar: {
/* These are the default options for the toolbar,
if nothing is passed this is what is used */
allowMultiParagraphSelection: true,
buttons: ['bold', 'italic', 'underline', 'anchor', 'h2', 'h3', 'quote'],
diffLeft: 0,
diffTop: -10,
firstButtonClass: 'medium-editor-button-first',
lastButtonClass: 'medium-editor-button-last',
relativeContainer: null,
standardizeSelectionStart: false,
static: false,
/* options which only apply when static is true */
align: 'center',
sticky: false,
updateOnEmptySelection: false
}
});
- allowMultiParagraphSelection: enables/disables whether the toolbar should be displayed when selecting multiple paragraphs/block elements. Default:
true
- buttons: the set of buttons to display on the toolbar. Default:
['bold', 'italic', 'underline', 'anchor', 'h2', 'h3', 'quote']
- See Button Options for details on more button options
- diffLeft: value in pixels to be added to the X axis positioning of the toolbar. Default:
0
- diffTop: value in pixels to be added to the Y axis positioning of the toolbar. Default:
-10
- firstButtonClass: CSS class added to the first button in the toolbar. Default:
'medium-editor-button-first'
- lastButtonClass: CSS class added to the last button in the toolbar. Default:
'medium-editor-button-last'
- relativeContainer: DOMElement to append the toolbar to instead of the body. When passed, the toolbar will also be positioned
relative
instead ofabsolute
. Default:null
- standardizeSelectionStart: enables/disables standardizing how the beginning of a range is decided between browsers whenever the selected text is analyzed for updating toolbar buttons status. Default:
false
- static: enable/disable the toolbar always displaying in the same location relative to the medium-editor element. Default:
false
Options which only apply when the static
option is being used:
- align:
left
|center
|right
- When the static option istrue
, this aligns the static toolbar relative to the medium-editor element. Default:center
- sticky: When the static option is
true
, this enables/disables the toolbar "sticking" to the viewport and staying visible on the screen while the page scrolls. Default:false
- updateOnEmptySelection: When the static option is
true
, this enables/disables updating the state of the toolbar buttons even when the selection is collapsed (there is no selection, just a cursor). Default:false
To disable the toolbar (which also disables the anchor-preview extension), set the value of the toolbar
option to false
:
var editor = new MediumEditor('.editable', {
toolbar: false
});
Button Options
Button behavior can be modified by passing an object into the buttons array instead of a string. This allow for overriding some of the default behavior of buttons. The following options are some of the basic parts of buttons that you may override, but any part of the MediumEditor.Extension.prototype
can be overridden via these button options. (Check out the source code for buttons to see what all can be overridden).
- name: name of the button being overridden
- action: argument to pass to
MediumEditor.execAction()
when the button is clicked. - aria: value to add as the aria-label attribute of the button element displayed in the toolbar. This is also used as the tooltip for the button.
- tagNames: array of element tag names that would indicate that this button has already been applied. If this action has already been applied, the button will be displayed as 'active' in the toolbar.
- Example: For 'bold', if the text is ever within a
<b>
or<strong>
tag that indicates the text is already bold. So the array of tagNames for bold would be:['b', 'strong']
- NOTE: This is not used if
useQueryState
is set totrue
.
- Example: For 'bold', if the text is ever within a
- style: A pair of css property & value(s) that indicate that this button has already been applied. If this action has already been applied, the button will be displayed as 'active' in the toolbar.
- Example: For 'bold', if the text is ever within an element with a
'font-weight'
style property set to700
or'bold'
, that indicates the text is already bold. So the style object for bold would be{ prop: 'font-weight', value: '700|bold' }
- NOTE: This is not used if
useQueryState
is set totrue
. - Properties of the style object:
- prop: name of the css property
- value: value(s) of the css property (multiple values can be separated by a
'|'
)
- Example: For 'bold', if the text is ever within an element with a
- useQueryState: Enables/disables whether this button should use the built-in
document.queryCommandState()
method to determine whether the action has already been applied. If the action has already been applied, the button will be displayed as 'active' in the toolbar- Example: For 'bold', if this is set to true, the code will call
document.queryCommandState('bold')
which will return true if the browser thinks the text is already bold, and false otherwise
- Example: For 'bold', if this is set to true, the code will call
- contentDefault: Default
innerHTML
to put inside the button - contentFA: The
innerHTML
to use for the content of the button if the buttonLabels option for MediumEditor is set to'fontawesome'
- classList: An array of classNames (strings) to be added to the button
- attrs: A set of key-value pairs to add to the button as custom attributes to the button element.
Example of overriding buttons (here, the goal is to mimic medium by having H1 and H2 buttons which actually produce <h2>
and <h3>
tags respectively):
var editor = new MediumEditor('.editable', {
toolbar: {
buttons: [
'bold',
'italic',
{
name: 'h1',
action: 'append-h2',
aria: 'header type 1',
tagNames: ['h2'],
contentDefault: '<b>H1</b>',
classList: ['custom-class-h1'],
attrs: {
'data-custom-attr': 'attr-value-h1'
}
},
{
name: 'h2',
action: 'append-h3',
aria: 'header type 2',
tagNames: ['h3'],
contentDefault: '<b>H2</b>',
classList: ['custom-class-h2'],
attrs: {
'data-custom-attr': 'attr-value-h2'
}
},
'justifyCenter',
'quote',
'anchor'
]
}
});
Anchor Preview options
The anchor preview is a built-in extension which automatically displays a 'tooltip' when the user is hovering over a link in the editor. The tooltip will display the href
of the link, and when clicked, will open the anchor editing form in the toolbar.
Options for the anchor preview 'tooltip' are passed as an object that is a member of the outer options object. Example:
var editor = new MediumEditor('.editable', {
anchorPreview: {
/* These are the default options for anchor preview,
if nothing is passed this is what it used */
hideDelay: 500,
previewValueSelector: 'a'
}
}
});
- hideDelay: time in milliseconds to show the anchor tag preview after the mouse has left the anchor tag. Default:
500
- previewValueSelector: the default selector to locate where to put the activeAnchor value in the preview. You should only need to override this if you've modified the way in which the anchor-preview extension renders. Default:
'a'
- showWhenToolbarIsVisible: determines whether the anchor tag preview shows up when the toolbar is visible. You should set this value to true if the static option for the toolbar is true and you want the preview to show at the same time. Default:
false
- showOnEmptyLinks: determines whether the anchor tag preview shows up on link with href as '' or '#something'. You should set this value to false if you do not want the preview to show up in such use cases. Default:
true
To disable the anchor preview, set the value of the anchorPreview
option to false
:
var editor = new MediumEditor('.editable', {
anchorPreview: false
});
NOTE:
- If the toolbar is disabled (via
toolbar: false
option ordata-disable-toolbar
attribute) the anchor-preview is automatically disabled. - If the anchor editing form is not enabled, clicking on the anchor-preview will not allow the href of the link to be edited
Placeholder Options
The placeholder handler is a built-in extension which displays placeholder text when the editor is empty.
Options for placeholder are passed as an object that is a member of the outer options object. Example:
var editor = new MediumEditor('.editable', {
placeholder: {
/* This example includes the default options for placeholder,
if nothing is passed this is what it used */
text: 'Type your text',
hideOnClick: true
}
});
-
text: Defines the default placeholder for empty contenteditables when placeholder is not set to false. You can overwrite it by setting a
data-placeholder
attribute on the editor elements. Default:'Type your text'
-
hideOnClick: Causes the placeholder to disappear as soon as the field gains focus. Default:
true
. To hide the placeholder only after starting to type, and to show it again as soon as field is empty, set this option tofalse
.
To disable the placeholder, set the value of the placeholder
option to false
:
var editor = new MediumEditor('.editable', {
placeholder: false
});
Anchor Form options
The anchor form is a built-in button extension which allows the user to add/edit/remove links from within the editor. When 'anchor' is passed in as a button in the list of buttons, this extension will be enabled and can be triggered by clicking the corresponding button in the toolbar.
Options for the anchor form are passed as an object that is a member of the outer options object. Example:
var editor = new MediumEditor('.editable', {
toolbar: {
buttons: ['bold', 'italic', 'underline', 'anchor']
},
anchor: {
/* These are the default options for anchor form,
if nothing is passed this is what it used */
customClassOption: null,
customClassOptionText: 'Button',
linkValidation: false,
placeholderText: 'Paste or type a link',
targetCheckbox: false,
targetCheckboxText: 'Open in new window'
}
}
});
- customClassOption: custom class name the user can optionally have added to their created links (ie 'button'). If passed as a non-empty string, a checkbox will be displayed allowing the user to choose whether to have the class added to the created link or not. Default:
null
- customClassOptionText: text to be shown in the checkbox when the customClassOption is being used. Default:
'Button'
- linkValidation: enables/disables check for common URL protocols on anchor links. Converts invalid url characters (ie spaces) to valid characters using
encodeURI
. Default:false
- placeholderText: text to be shown as placeholder of the anchor input. Default:
'Paste or type a link'
- targetCheckbox: enables/disables displaying a "Open in new window" checkbox, which when checked changes the
target
attribute of the created link. Default:false
- targetCheckboxText: text to be shown in the checkbox enabled via the targetCheckbox option. Default:
'Open in new window'
Paste Options
The paste handler is a built-in extension which attempts to filter the content when the user pastes. How the paste handler filters is configurable via specific options.
Options for paste handling are passed as an object that is a member of the outer options object. Example:
var editor = new MediumEditor('.editable', {
paste: {
/* This example includes the default options for paste,
if nothing is passed this is what it used */
forcePlainText: true,
cleanPastedHTML: false,
cleanReplacements: [],
cleanAttrs: ['class', 'style', 'dir'],
cleanTags: ['meta'],
unwrapTags: []
}
});
- forcePlainText: Forces pasting as plain text. Default:
true
- cleanPastedHTML: cleans pasted content from different sources, like google docs etc. Default:
false
- preCleanReplacements: custom pairs (2 element arrays) of RegExp and replacement text to use during paste when forcePlainText or cleanPastedHTML are
true
OR when callingcleanPaste(text)
helper method. These replacements are executed before builtin replacements. Default:[]
- cleanReplacements: custom pairs (2 element arrays) of RegExp and replacement text to use during paste when forcePlainText or cleanPastedHTML are
true
OR when callingcleanPaste(text)
helper method. These replacements are executed after builtin replacements. Default:[]
- cleanAttrs: list of element attributes to remove during paste when cleanPastedHTML is
true
or when callingcleanPaste(text)
orpasteHTML(html,options)
helper methods. Default:['class', 'style', 'dir']
- cleanTags: list of element tag names to remove during paste when cleanPastedHTML is
true
or when callingcleanPaste(text)
orpasteHTML(html,options)
helper methods. Default:['meta']
- unwrapTags: list of element tag names to unwrap (remove the element tag but retain its child elements) during paste when cleanPastedHTML is
true
or when callingcleanPaste(text)
orpasteHTML(html,options)
helper methods. Default:[]
KeyboardCommands Options
The keyboard commands handler is a built-in extension for mapping key-combinations to actions to execute in the editor.
Options for KeyboardCommands are passed as an object that is a member of the outer options object. Example:
var editor = new MediumEditor('.editable', {
keyboardCommands: {
/* This example includes the default options for keyboardCommands,
if nothing is passed this is what it used */
commands: [
{
command: 'bold',
key: 'B',
meta: true,
shift: false,
alt: false
},
{
command: 'italic',
key: 'I',
meta: true,
shift: false,
alt: false
},
{
command: 'underline',
key: 'U',
meta: true,
shift: false,
alt: false
}
],
}
});
- commands: Array of objects describing each command and the combination of keys that will trigger it. Required for each object:
- command: argument passed to
editor.execAction()
when key-combination is used- if defined as
false
, the shortcut will be disabled
- if defined as
- key: keyboard character that triggers this command
- meta: whether the ctrl/meta key has to be active or inactive
- shift: whether the shift key has to be active or inactive
- alt: whether the alt key has to be active or inactive
- command: argument passed to
To disable the keyboard commands, set the value of the keyboardCommands
option to false
:
var editor = new MediumEditor('.editable', {
keyboardCommands: false
});
Auto Link Options
The auto-link handler is a built-in extension which automatically turns URLs entered into the text field into HTML anchor tags (similar to the functionality of Markdown). This feature is OFF by default.
To enable built-in auto-link support, set the value of the autoLink
option to true
:
var editor = new MediumEditor('.editable', {
autoLink: true
});
Image Dragging Options
The image dragging handler is a built-in extension for handling dragging & dropping images into the contenteditable. This feature is ON by default.
To disable built-in image dragging, set the value of the imageDragging
option to false
:
var editor = new MediumEditor('.editable', {
imageDragging: false
});
Disable File Dragging
To stop preventing drag & drop events and disable file dragging in general, provide a dummy ImageDragging extension.
var editor = new MediumEditor('.editor', {
extensions: {
'imageDragging': {}
}
});
Due to the state of code in 5.0.0, the editor ALWAYS prevented any drag and drop actions. We will have a better way to disable file dragging in 6.*
Options Example:
var editor = new MediumEditor('.editable', {
delay: 1000,
targetBlank: true,
toolbar: {
buttons: ['bold', 'italic', 'quote'],
diffLeft: 25,
diffTop: 10,
},
anchor: {
placeholderText: 'Type a link',
customClassOption: 'btn',
customClassOptionText: 'Create Button'
},
paste: {
cleanPastedHTML: true,
cleanAttrs: ['style', 'dir'],
cleanTags: ['label', 'meta'],
unwrapTags: ['sub', 'sup']
},
anchorPreview: {
hideDelay: 300
},
placeholder: {
text: 'Click to edit'
}
});
Buttons
By default, MediumEditor supports buttons for most of the commands for document.execCommand()
that are well-supported across all its supported browsers.
Default buttons.
MediumEditor, by default, will show only the buttons listed here to avoid a huge toolbar:
- bold
- italic
- underline
- anchor (built-in support for collecting a URL via the anchor extension)
- h2
- h3
- quote
All buttons.
These are all the built-in buttons supported by MediumEditor.
- bold
- italic
- underline
- strikethrough
- subscript
- superscript
- anchor
- image (this simply converts selected text to an image tag)
- quote
- pre
- orderedlist
- unorderedlist
- indent (moves the selected text up one level)
- outdent (moves the selected text down one level)
- justifyLeft
- justifyCenter
- justifyRight
- justifyFull
- h1
- h2
- h3
- h4
- h5
- h6
- removeFormat (clears inline style formatting, preserves blocks)
- html (parses selected html and converts into actual html elements)
Themes
Check out the Wiki page for a list of available themes: https://github.com/yabwe/medium-editor/wiki/Themes
API
View the MediumEditor Object API documentation on the Wiki for details on all the methods supported on the MediumEditor object.
Initialization methods
- MediumEditor(elements, options): Creates an instance of MediumEditor
- .destroy(): tears down the editor if already setup, removing all DOM elements and event handlers
- .setup(): rebuilds the editor if it has already been destroyed, recreating DOM elements and attaching event handlers
- .addElements(): add elements to an already initialized instance of MediumEditor
- .removeElements(): remove elements from an already initialized instance of MediumEditor
Event Methods
- .on(target, event, listener, useCapture): attach a listener to a DOM event which will be detached when MediumEditor is deactivated
- .off(target, event, listener, useCapture): detach a listener to a DOM event that was attached via
on()
- .subscribe(event, listener): attaches a listener to a custom medium-editor event
- .unsubscribe(event, listener): detaches a listener from a custom medium-editor event
- .trigger(name, data, editable): manually triggers a custom medium-editor event
Selection Methods
- .checkSelection(): manually trigger an update of the toolbar and extensions based on the current selection
- .exportSelection(): return a data representation of the selected text, which can be applied via
importSelection()
- .importSelection(selectionState): restore the selection using a data representation of previously selected text (ie value returned by
exportSelection()
) - .getFocusedElement(): returns an element if any contenteditable element monitored by MediumEditor currently has focused
- .getSelectedParentElement(range): get the parent contenteditable element that contains the current selection
- .restoreSelection(): restore the selection to what was selected when
saveSelection()
was called - .saveSelection(): internally store the set of selected text
- .selectAllContents(): expands the selection to contain all text within the focused contenteditable
- .selectElement(element): change selection to be a specific element and update the toolbar to reflect the selection
- .stopSelectionUpdates(): stop the toolbar from updating to reflect the state of the selected text
- .startSelectionUpdates(): put the toolbar back into its normal updating state
Editor Action Methods
- .cleanPaste(text): convert text to plaintext and replace current selection with result
- .createLink(opts): creates a link via the native
document.execCommand('createLink')
command - .execAction(action, opts): executes an built-in action via
document.execCommand
- .pasteHTML(html, options): replace the current selection with html
- .queryCommandState(action): wrapper around the browser's built in
document.queryCommandState(action)
for checking whether a specific action has already been applied to the selection.
Helper Methods
- .delay(fn): delay any function from being executed by the amount of time passed as the
delay
option - .getContent(index): gets the trimmed
innerHTML
of the element atindex
- .getExtensionByName(name): get a reference to an extension with the specified name
- .resetContent(element): reset the content of all elements or a specific element to its value when added to the editor initially
- .serialize(): returns a JSON object with elements contents
- .setContent(html, index): sets the
innerHTML
tohtml
of the element atindex
Static Methods/Properties
- .getEditorFromElement(element): retrieve the instance of MediumEditor that is monitoring the provided editor element
- .version: the version information for the MediumEditor library
Dynamically add/remove elements to your instance
It is possible to dynamically add new elements to your existing MediumEditor instance:
var editor = new MediumEditor('.editable');
editor.subscribe('editableInput', this._handleEditableInput.bind(this));
// imagine an ajax fetch/any other dynamic functionality which will add new '.editable' elements to the DOM
editor.addElements('.editable');
// OR editor.addElements(document.getElementsByClassName('editable'));
// OR editor.addElements(document.querySelectorAll('.editable'));
Passing an elements or array of elements to addElements(elements)
will:
- Add the given element or array of elements to the internal
this.elements
array. - Ensure the element(s) are initialized with the proper attributes and event handlers as if the element had been passed during instantiation of the editor.
- For any
<textarea>
elements:- Hide the
<textarea>
- Create a new
<div contenteditable=true>
element and add it to the elements array. - Ensure the 2 elements remain sync'd.
- Hide the
- Be intelligent enough to run the necessary code only once per element, no matter how often you will call it.
Removing elements dynamically
Straight forward, just call removeElements
with the element or array of elements you to want to tear down. Each element itself will remain a contenteditable - it will just remove all event handlers and all references to it so you can safely remove it from DOM.
editor.removeElements(document.querySelector('#myElement'));
// OR editor.removeElements(document.getElementById('myElement'));
// OR editor.removeElements('#myElement');
// in case you have jQuery and don't exactly know when an element was removed, for example after routing state change
var removedElements = [];
editor.elements.forEach(function (element) {
// check if the element is still available in current DOM
if (!$(element).parents('body').length) {
removedElements.push(element);
}
});
editor.removeElements(removedElements);
Capturing DOM changes
For observing any changes on contentEditable, use the custom 'editableInput'
event exposed via the subscribe()
method:
var editor = new MediumEditor('.editable');
editor.subscribe('editableInput', function (event, editable) {
// Do some work
});
This event is supported in all browsers supported by MediumEditor (including IE9+ and Edge)! To help with cases when one instance of MediumEditor is monitoring multiple elements, the 2nd argument passed to the event handler (editable
in the example above) will be a reference to the contenteditable element that has actually changed.
This is handy when you need to capture any modifications to the contenteditable element including:
- Typing
- Cutting/Pasting
- Changes from clicking on buttons in the toolbar
- Undo/Redo
Why is this interesting and why should you use this event instead of just attaching to the input
event on the contenteditable element?
So for most modern browsers (Chrome, Firefox, Safari, etc.), the input
event works just fine. In fact, editableInput
is just a proxy for the input
event in those browsers. However, the input
event is not supported for contenteditable elements in IE 9-11 and is mostly supported in Microsoft Edge, but not fully.
So, to properly support the editableInput
event in Internet Explorer and Microsoft Edge, MediumEditor uses a combination of the selectionchange
and keypress
events, as well as monitoring calls to document.execCommand
.
Extensions & Plugins
Check the documentation in order to learn how to develop extensions for MediumEditor.
A list of existing extensions and plugins, such as Images and Media embeds, Tables and Markdown can be found here.
Development
To run the demo locally:
- Clone this repo locally
- Run
npm install
from your console at the root - Run
node index.js
from the root - Navigate to
http://localhost:8088/demo/index.html
to view the demo
MediumEditor development tasks are managed by Grunt. To install all the necessary packages, just invoke:
npm install
To run all the test and build the dist files for testing on demo pages, just invoke:
grunt
These are the other available grunt tasks:
- js: runs jslint and jasmine tests and creates minified and concatenated versions of the script;
- css: runs autoprefixer and csslint
- test: runs jasmine tests, jslint and csslint
- watch: watch for modifications on script/scss files
- spec: runs a task against a specified file
The source files are located inside the src directory. Be sure to make changes to these files and not files in the dist directory.
Contributing
- Fork it
- Create your feature branch (
git checkout -b my-new-feature
) - Test your changes to the best of your ability.
- Update the documentation to reflect your changes if they add or changes current functionality.
- Commit your changes (
git commit -am 'Added some feature'
) without files from the dist directory. - Push to the branch (
git push origin my-new-feature
) - Create a new Pull Request
Code Consistency
To help create consistent looking code throughout the project, we use a few tools to help us. They have plugins for most popular editors/IDEs to make coding for our project, but you should use them in your project as well!
JSHint
We use JSHint on each build to find easy-to-catch errors and potential problems in our js. You can find our JSHint settings in the .jshintrc
file in the root of the project.
jscs
We use jscs on each build to enforce some code style rules we have for our project. You can find our jscs settings in the .jscsrc
file in the root of the project.
EditorConfig
We use EditorConfig to maintain consistent coding styles between various editors and IDEs. You can find our settings in the .editorconfig
file in the root of the project.
Easy First Bugs
Looking for something simple for a first contribution? Try fixing an easy first bug!
Contributors (100+ and counting!)
https://github.com/yabwe/medium-editor/graphs/contributors
Is Your Org Using MediumEditor?
Add your org here and we can add you to our landing page!
License
MIT: https://github.com/yabwe/medium-editor/blob/master/LICENSE
Top Related Projects
Powerful rich text editor framework with a modular architecture, modern integrations, and features like collaborative editing.
Quill is a modern WYSIWYG editor built for compatibility and extensibility
The world's #1 JavaScript library for rich text editing. Available for React, Vue and Angular
A React framework for building text editors.
The next generation Javascript WYSIWYG HTML Editor.
Super simple WYSIWYG editor
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot