Text Editor
Framework7 comes with a touch-friendly Rich Text Editor component. It is based on modern "contenteditable" API so it should work everywhere as is.
It comes with the basic set of formatting features. But its functionality can be easily extended and customized to fit any requirements.
Text Editor Layout
<div class="text-editor">
<div class="text-editor-content" contenteditable></div>
</div>
To make editor resizable (when its height will fit to its content), we need to add text-editor-resizable
class to editor element:
<!-- additional "text-editor-resizable" class -->
<div class="text-editor text-editor-resizable">
<div class="text-editor-content" contenteditable></div>
</div>
Text Editor App Methods
Let's look at related App methods to work with Text Editor:
app.textEditor.create(parameters)- create Text Editor instance
- parameters - object. Object with Text Editor parameters
Method returns created Text Editor instance
app.textEditor.destroy(el)- destroy Text Editor instance
- el - HTMLElement or string (with CSS Selector) or object. Text Editor element or Text Editor instance to destroy.
app.textEditor.get(el)- get Text Editor instance by HTML element
- el - HTMLElement or string (with CSS Selector). Text Editor element.
Method returns Text Editor's instance
For example:
var textEditor = app.textEditor.create({
el: '#my-text-editor',
value: <code><p>Hello</p></code>,
});
Text Editor Parameters
Let's look on list of all available Text Editor parameters:
Parameter | Type | Default | Description |
---|---|---|---|
el | HTMLElement string | Text Editor element. HTMLElement or string with CSS selector of editor element | |
value | string | Text editor initial html content value. Initial value can also be placed as inner HTML content of
| |
placeholder | string | Editor placeholder content displayed when it is empty. By default it is not specified | |
mode | string | toolbar | Text editor buttons mode. Can be:
|
buttons | array | Array with editor buttons, or array of arrays (groups) with editor buttons. By default all buttons enabled and its default value is:
| |
dividers | boolean | true | Adds visual divider between buttons group |
imageUrlText | string | Insert image URL | Prompt text that appears on image url request |
linkUrlText | string | Insert link URL | Prompt text that appears on link url request |
clearFormattingOnPaste | boolean | true | When enabled it will clear any formatting on paste from clipboard |
customButtons | object | Object with custom buttons. Object property key is the button id that should be used in For example to specify custom button that will add
| |
on | object | Object with events handlers. For example:
|
Note that all following parameters can be used in global app parameters under textEditor
property to set defaults for all text editors. For example:
var app = new Framework7({
textEditor: {
buttons: ['bold', 'italic'],
}
});
Text Editor Methods & Properties
After we initialize Text Editor we have its initialized instance in variable (like textEditor
variable in examples above) with helpful methods and properties:
Properties | |
---|---|
textEditor.app | Link to global app instance |
textEditor.el | Text Editor container HTML element |
textEditor.$el | Dom7 instance with Text Editor container HTML element |
textEditor.contentEl | Text Editor content (contenteditalbe ) HTML element |
textEditor.$contentEl | Dom7 instance with Text Editor content (contenteditalbe ) HTML element |
textEditor.value | HTML value of the Text Editor |
textEditor.params | Object with initialization parameters |
Methods | |
textEditor.setValue(value) | Set new Text Editor value. value is the HTML string. |
textEditor.getValue() | Returns current Text Editor value |
textEditor.clearValue() | Clear Text Editor value |
textEditor.getSelectionRange() | Returns current selection Range |
textEditor.setSelectionRange(range) | Set selection based on passed Range |
textEditor.destroy() | Destroy Text Editor instance and remove all events |
textEditor.on(event, handler) | Add event handler |
textEditor.once(event, handler) | Add event handler that will be removed after it was fired |
textEditor.off(event, handler) | Remove event handler |
textEditor.off(event) | Remove all handlers for specified event |
textEditor.emit(event, ...args) | Fire event on instance |
Text Editor Events
Text Editor will fire the following DOM events on text editor element and events on app andtext editor instance:
DOM Events
Event | Description |
---|---|
texteditor:init | Event will be triggered when on editor initialization |
texteditor:change | Event will be triggered when editor value has been changed |
texteditor:input | Event will be triggered on editor's content "input" event |
texteditor:focus | Event will be triggered on editor's content focus |
texteditor:blur | Event will be triggered on editor's content blur |
texteditor:buttonclick | Event will be triggered on editor button click |
texteditor:keyboardopen | Event will be triggered when editor keyboard toolbar appears |
texteditor:keyboardclose | Event will be triggered when editor keyboard toolbar disappears |
texteditor:popoveropen | Event will be triggered on editor popover open |
texteditor:popoverclose | Event will be triggered on editor popover close |
texteditor:beforedestroy | Event will be triggered right before Text Editor instance will be destroyed |
App and Text Editor Instance Events
Text Editor instance emits events on both self instance and app instance. App instance events has same names prefixed with textEditor
.
Event | Target | Arguments | Description |
---|---|---|---|
init | textEditor | (editor) | Event will be triggered when on editor initialization. |
textEditorInit | app | ||
change | textEditor | (editor) | Event will be triggered when on editor initialization. |
textEditorChange | app | ||
input | textEditor | (editor) | Event will be triggered on editor's content "input" event. |
textEditorInput | app | ||
focus | textEditor | (editor) | Event will be triggered on editor's content focus. |
textEditorFocus | app | ||
blur | textEditor | (editor) | Event will be triggered on editor's content blur. |
textEditorBlur | app | ||
buttonClick | textEditor | (editor, button) | Event will be triggered on editor button click. As second argument event handler receives id of the clicked button, e.g. bold |
textEditorButtonClick | app | ||
keyboardOpen | textEditor | (editor) | Event will be triggered when editor keyboard toolbar appears. |
textEditorKeyboardOpen | app | ||
keyboardClose | textEditor | (editor) | Event will be triggered when editor keyboard toolbar disappears. |
textEditorKeyboardClose | app | ||
popoverOpen | textEditor | (editor) | Event will be triggered on editor popover open. |
textEditorPopoverOpen | app | ||
popoverClose | textEditor | (editor) | Event will be triggered on editor popover close. |
textEditorPopoverClose | app | ||
beforeDestroy | textEditor | (editor) | Event will be triggered right before Text Editor instance will be destroyed. |
textEditorBeforeDestroy | app |
Text Editor Auto Initialization
If you don't need to use Text Editor API and your Text Editor is inside of the page and presented in DOM on moment of page initialization then it can be auto initialized with just adding additional text-editor-init
class:
<!-- Add text-editor-init class -->
<div class="text-editor text-editor-init">
<div class="text-editor-content" contenteditable></div>
</div>
In this case if you need to access created Text Editor instance you can use the app.textEditor.get
app method:
var textEditor = app.textEditor.get('.my-text-editor');
if (!textEditor.value) {
// do something
}
When using auto init you may need to pass additional parameters. It can be done with data-
attributes on panel element.
<!-- parameters set via data- attributes -->
<div
class="text-editor text-editor-init"
data-mode="popover"
data-placeholder="Description"
>
...
</div>
Parameters used in camelCase, for example imageUrlText, in data- attributes should be used in kebab-case as data-image-url-text
CSS Variables
Below is the list of related CSS variables (CSS custom properties).
:root {
--f7-text-editor-font-size: inherit;
--f7-text-editor-font-weight: inherit;
--f7-text-editor-border-width: 1px;
--f7-text-editor-height: 250px;
--f7-text-editor-margin: 16px;
--f7-text-editor-padding: 8px;
--f7-text-editor-button-bg-color: transparent;
--f7-text-editor-button-size: 28px;
--f7-text-editor-button-icon-size: 20px;
--f7-text-editor-button-margin: 2px;
--f7-text-editor-text-color: #000;
--f7-text-editor-bg-color: #fff;
--f7-text-editor-button-divider-color: rgba(0, 0, 0, 0.15);
}
:root .dark,
:root.dark {
--f7-text-editor-bg-color: #121212;
--f7-text-editor-text-color: #fff;
--f7-text-editor-button-divider-color: rgba(255, 255, 255, 0.15);
}
.ios {
--f7-text-editor-toolbar-padding: 6px;
--f7-text-editor-button-border-radius: 2px;
--f7-text-editor-placeholder-color: rgba(0, 0, 0, 0.35);
--f7-text-editor-toolbar-border-color: rgba(0, 0, 0, 0.25);
--f7-text-editor-toolbar-bg-color: #fff;
--f7-text-editor-border-color: rgba(0, 0, 0, 0.1);
--f7-text-editor-button-text-color: #333;
}
.ios .dark,
.ios.dark {
--f7-text-editor-placeholder-color: rgba(255, 255, 255, 0.35);
--f7-text-editor-toolbar-bg-color: #121212;
--f7-text-editor-toolbar-border-color: rgba(255, 255, 255, 0.1);
--f7-text-editor-toolbar-bg-color: #202020;
--f7-text-editor-border-color: rgba(255, 255, 255, 0.1);
--f7-text-editor-button-text-color: #fff;
}
.md {
--f7-text-editor-button-border-radius: 8px;
--f7-text-editor-toolbar-padding: 8px;
}
.md,
.md .dark,
.md [class*='color-'] {
--f7-text-editor-placeholder-color: var(--f7-md-on-surface-variant);
--f7-text-editor-toolbar-bg-color: var(--f7-md-surface-1);
--f7-text-editor-border-color: var(--f7-md-outline);
--f7-text-editor-button-text-color: var(--f7-md-on-surface);
}
Examples
<template>
<div class="page">
<div class="navbar">
<div class="navbar-bg"></div>
<div class="navbar-inner sliding">
<div class="title">Text Editor</div>
</div>
</div>
<div class="page-content">
<div class="block">
<p>Framework7 comes with a touch-friendly Rich Text Editor component. It is based on modern "contenteditable"
API so it should work everywhere as is.</p>
<p>It comes with the basic set of formatting features. But its functionality can be easily extended and
customized to fit any requirements.</p>
</div>
<div class="block-title">Default Setup</div>
<div class="text-editor text-editor-init">
<div class="text-editor-content" contenteditable></div>
</div>
<div class="block-title">With Placeholder</div>
<div class="text-editor text-editor-init" data-placeholder="Enter text...">
<div class="text-editor-content" contenteditable></div>
</div>
<div class="block-title">With Default Value</div>
<div class="text-editor text-editor-init" data-placeholder="Enter text...">
<div class="text-editor-content" contenteditable>
<p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Consequatur sunt, sapiente quis eligendi
consectetur hic asperiores assumenda quidem dolore quasi iusto tenetur commodi qui ullam sint sed alias!
Consequatur, dolor!</p>
<p>Provident reiciendis exercitationem reprehenderit amet repellat laborum, sequi id quam quis quo quos facere
veniam ad libero dolorum animi. Nobis, illum culpa explicabo dolorem vitae ut dolor at reprehenderit magnam?
</p>
<p>Qui, animi. Dolores dicta, nobis aut expedita enim eum assumenda modi, blanditiis voluptatibus excepturi
non pariatur. Facilis fugit facere sequi molestias nemo in, suscipit inventore consequuntur, repellat
perferendis, voluptas odit.</p>
<p>Tempora voluptates, doloribus architecto eligendi numquam facilis perspiciatis autem quam voluptas maxime
ratione harum laudantium cum deleniti. In, alias deserunt voluptatibus eligendi libero nobis est unde et
perspiciatis cumque voluptatum.</p>
<p>Quam error doloribus qui laboriosam eligendi. Aspernatur quam pariatur perspiciatis reprehenderit atque
dicta culpa, aut rem? Assumenda, quibusdam? Reprehenderit necessitatibus facere nemo iure maiores porro
voluptates accusamus quibusdam. Nesciunt, assumenda?</p>
</div>
</div>
<div class="block-title">Specific Buttons</div>
<div class="block-header">It is possible to customize which buttons (commands) to show.</div>
<div class="text-editor text-editor-init" data-placeholder="Enter text..."
data-buttons='[["bold", "italic", "underline", "strikeThrough"], ["orderedList", "unorderedList"]]'>
<div class="text-editor-content" contenteditable></div>
</div>
<div class="block-title">Custom Button</div>
<div class="block-header">It is possible to create custom editor buttons. Here is the custom "hr" button that adds
horizontal rule:</div>
<div class="text-editor text-editor-custom-buttons">
<div class="text-editor-content" contenteditable></div>
</div>
<div class="block-title">Resizable</div>
<div class="block-header">Editor will be resized based on its content.</div>
<div class="text-editor text-editor-init text-editor-resizable" data-placeholder="Enter text..."
data-buttons='["bold", "italic", "underline", "strikeThrough"]'>
<div class="text-editor-content" contenteditable></div>
</div>
<div class="block-title">Popover Mode</div>
<div class="block-header">In this mode, there is no toolbar with buttons, but they appear as popover when you
select any text in editor.</div>
<div class="text-editor text-editor-init" data-placeholder="Enter text..."
data-buttons='["bold", "italic", "underline", "strikeThrough"]' data-mode="popover"
style="--f7-text-editor-height: 150px">
<div class="text-editor-content" contenteditable></div>
</div>
<div class="block-title">Keyboard Toolbar Mode</div>
<div class="block-header">In this mode, toolbar with buttons will appear on top of virtual keyboard when editor is
in the focus. It is supported only in iOS, Android cordova apps and in Android Chrome. When not supported it
will fallback to "popover" mode.</div>
<div class="text-editor text-editor-init" data-placeholder="Enter text..." data-mode="keyboard-toolbar"
style="--f7-text-editor-height: 150px">
<div class="text-editor-content" contenteditable></div>
</div>
<div class="block-title">As List Input</div>
<div class="block-header">Text editor can be used in list with other inputs. In this example it is enabled with
"keyboard-toolbar"/"popover" type for "About" field.</div>
<div class="list list-strong-ios list-dividers-ios list-outline-ios">
<ul>
<li class="item-content item-input">
<div class="item-media">
<i class="icon demo-list-icon"></i>
</div>
<div class="item-inner">
<div class="item-title item-label">Name</div>
<div class="item-input-wrap">
<input type="text" placeholder="Your name" />
</div>
</div>
</li>
<li class="item-content item-input">
<div class="item-media">
<i class="icon demo-list-icon"></i>
</div>
<div class="item-inner">
<div class="item-title item-label">About</div>
<div class="item-input-wrap">
<div class="text-editor text-editor-init text-editor-resizable" data-placeholder="About"
data-buttons='["bold", "italic", "underline", "strikeThrough"]' data-mode="popover">
<div class="text-editor-content" contenteditable></div>
</div>
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
</template>
<script>
export default (props, { $f7, $el, $on }) => {
let textEditorCustomButtons;
$on('pageInit', () => {
textEditorCustomButtons = $f7.textEditor.create({
el: $el.value.find('.text-editor-custom-buttons'),
// define custom "hr" button
customButtons: {
hr: {
content: '<hr>',
onClick(editor, buttonEl) {
document.execCommand('insertHorizontalRule', false);
},
},
},
buttons: [["bold", "italic", "underline", "strikeThrough"], "hr"],
});
});
$on('pageBeforeRemove', () => {
textEditorCustomButtons.destroy()
});
return $render;
};
</script>