Если добавить атрибут contenteditable к элементу, его содержимое становится доступно для редактирования пользователю, а с использованием JS и JQuery можно сделать простой WYSIWYG редактор.
JS-метод document.execCommand(сommand, showDefaultUI, аrgument)
применяет команды к элементу с атрибутом contenteditable
, который находится в фокусе. Т.к. нет единого стандарта, логика команд может отличатся в разных браузерах так и их поддержка, описание команд на https://developer.mozilla.org.
Проверить доступность команды можно методом queryCommandSupported(сommand)
.
Поддержка команд в браузере:
Жирный
Включает и отключает у выделенного текста жирное начертание тегом <b>
, IE использует тег <strong>
.
Курсив
Тег <i>
, IE использует <em>
.
Подчеркнутый
Тег <u>
.
Перечёркнутый
Тег <strike>
.
Верхний индекс
Тег <sup>
.
Нижний индекс
Тег <sub>
.
Маркированный список
Тег <ul>
.
Нумерованный список
Тег <ol>
.
Заголовки, параграф, цитата и т.д.
formatBlock
добавляет блочный тег вокруг выделенного текста.
document.execCommand('formatBlock', false, 'h1');
document.execCommand('formatBlock', false, 'h2');
document.execCommand('formatBlock', false, 'h3');
document.execCommand('formatBlock', false, 'p');
document.execCommand('formatBlock', false, 'blockquote');
document.execCommand('formatBlock', false, 'div');
Горизонтальная линия
Тег <hr>
.
Изображение
var url = prompt('Введите адрес изображения', '');
document.execCommand('insertImage', false, url);
Ссылка
Удаление ссылки:
Вставка текста
Вставка HTML
С помощью insertHTML
можно обернурнуть выделенный текст тегом, например <pre>
:
document.execCommand('insertHTML', false, '<pre>' + document.getSelection().toString() + '</pre>');
Выравнивание текста
Добавляет родительскому блоку CSS-свойство text-align
.
По левому краю:
По центру:
По правому краю:
По ширине:
Шрифты
По умолчанию команды установки шрифта добавляются тегом <font>
c атрибутами face
, size
, color
, что не очень хорошо.
Команда styleWithCSS
переключает режим, в место семантических тегов добавляются <span style="...">
.
После установки шрифта нужно переключить styleWithCSS
обратно т.к. он распространяется на другие теги (<b>
, <i>
, <s>
и т.д.).
Название шрифта
document.execCommand('styleWithCSS', false, true);
document.execCommand('fontName', false, 'monospace');
document.execCommand('styleWithCSS', false, false);
Размер:
Размер шрифта задаётся в условных единицах (1-7), в режиме styleWithCSS
некоторые браузеры используют font-size: xx-small
, x-small
, small
, medium
, large
, x-large
, xx-large
.
document.execCommand('styleWithCSS', false, true);
document.execCommand('fontSize', false, '5');
document.execCommand('styleWithCSS', false, false);
Цвет:
document.execCommand('styleWithCSS', false, true);
document.execCommand('foreColor', false, '#eeeeee');
document.execCommand('styleWithCSS', false, false);
Фон:
document.execCommand('styleWithCSS', false, true);
document.execCommand('hiliteColor', false, 'orange');
document.execCommand('styleWithCSS', false, false);
Undo и Redo
Отмена последнего действия, Ctrl + z.
Повтор последнего действия, Ctrl + y.
Выделить всё
Удаление выделенного
Очистить стили
Удаляет инлайновые теги и атрибуты style
, все блочные теги остаются.
Вырезать
Копировать
<link href="/font-awesome.min.css" rel="stylesheet">
<script src="/jquery.min.js"></script>
<div class="toolbar">
<a href="#" class="toolbar-b fas fa-bold" title="Жирный"></a>
<a href="#" class="toolbar-i fas fa-italic" title="Курсив"></a>
<a href="#" class="toolbar-u fas fa-underline" title="Подчёркнутый"></a>
<a href="#" class="toolbar-s fas fa-strikethrough" title="Зачёркнутый"></a>
<a href="#" class="toolbar-sup fas fa-superscript" title="Верхний индекс"></a>
<a href="#" class="toolbar-sub fas fa-subscript" title="Нижний индекс"></a>
<a href="#" class="toolbar-ul fas fa-list-ul" title="Маркированный список"></a>
<a href="#" class="toolbar-ol fas fa-list-ol" title="Нумерованный список"></a>
<a href="#" class="toolbar-p" title="Параграф">p</a>
<a href="#" class="toolbar-h1" title="Заголовок">H1</a>
<a href="#" class="toolbar-hr" title="Горизонтальная линия">hr</a>
<a href="#" class="toolbar-blockquote fas fa-quote-right" title="Цитата"></a>
<a href="#" class="toolbar-img far fa-image" title="Изображение"></a>
<a href="#" class="toolbar-a fas fa-link" title="Ссылка"></a>
<a href="#" class="toolbar-unlink fas fa-unlink" title="Удаление ссылки"></a>
<a href="#" class="toolbar-html" title="Вставить html">HTML</a>
<a href="#" class="toolbar-text" title="Вставить текст">Text</a>
<br>
<a href="#" class="toolbar-left fas fa-align-left" title="по левому краю"></a>
<a href="#" class="toolbar-center fas fa-align-center" title="по центру"></a>
<a href="#" class="toolbar-right fas fa-align-right" title="по правому краю"></a>
<a href="#" class="toolbar-justify fas fa-align-justify" title="по ширине"></a>
<select class="toolbar-font">
<option selected="selected" disabled="disabled">Шрифт</option>
<option value="arial">Arial</option>
<option value="Courier New">Courier New</option>
<option value="georgia">Georgia</option>
<option value="impact">Impact</option>
<option value="roboto">Tahoma</option>
<option value="Times New Roman">Times New Roman</option>
<option value="verdana">Verdana</option>
</select>
<select class="toolbar-size">
<option selected="selected" disabled="disabled">Размер</option>
<option value="1">10px</option>
<option value="2">12px</option>
<option value="3">14px</option>
<option value="4">16px</option>
<option value="5">18px</option>
<option value="6">21px</option>
<option value="7">26px</option>
</select>
<span>Цвет</span> <input class="toolbar-color" type="color" value="#ff0000">
<span>Фон</span> <input class="toolbar-bg" type="color" value="#ffff00">
<br>
<a href="#" class="toolbar-undo fas fa-undo" title="Отмена"></a>
<a href="#" class="toolbar-redo fas fa-redo" title="Повтор"></a>
<a href="#" class="toolbar-delete far fa-trash-alt" title="Удалить"></a>
<a href="#" class="toolbar-selectAll">Выделить всё</a>
<a href="#" class="toolbar-removeFormat">Очистить стили</a>
<a href="#" class="toolbar-cut fas fa-cut" title="Вырезать"></a>
<a href="#" class="toolbar-copy fas fa-copy" title="Копировать"></a>
</div>
<div class="editor" contenteditable="true">...</div>
.toolbar a {
display: inline-block;
border: 1px solid #888;
padding: 5px 8px;
margin: 0 5px 10px 0;
color: #000;
border-radius: 3px;
font-size: 12px;
box-shadow: 1px 1px 2px #ddd;
background: #fff;
vertical-align: top;
text-decoration: none;
}
.toolbar select {
display: inline-block;
height: 28px;
line-height: 28px;
background: #fff;
padding: 0;
margin: 0 5px 10px 0;
color: #000;
box-shadow: 1px 1px 2px #ddd;
border-radius: 3px;
vertical-align: top;
font-size: 12px;
}
.toolbar input {
display: inline-block;
height: 28px;
line-height: 28px;
background: #fff;
padding: 0;
margin: 0 5px 10px 0;
color: #000;
box-shadow: 1px 1px 2px #ddd;
border-radius: 3px;
vertical-align: top;
font-size: 12px;
}
.toolbar span {
display: inline-block;
height: 30px;
line-height: 30px;
padding: 0;
margin: 0 0 10px 0;
color: #000;
vertical-align: top;
font-size: 12px;
}
.editor {
min-height: 150px;
border: 1px solid #ddd;
padding: 10px;
border-radius: 2px;
box-shadow: 1px 1px 2px #ddd;
background: #fff;
}
// Жирный (b)
$('body').on('click', '.toolbar-b', function(){
document.execCommand('bold', false, null);
return false;
});
// Курсив (i)
$('body').on('click', '.toolbar-i', function(){
document.execCommand('italic', false, null);
return false;
});
// Подчёркнутый текст (u)
$('body').on('click', '.toolbar-u', function(){
document.execCommand('underline', false, null);
return false;
});
// Зачёркнутый текст (strike)
$('body').on('click', '.toolbar-s', function(){
document.execCommand('strikethrough', false, null);
return false;
});
// Верхний индекс (sup)
$('body').on('click', '.toolbar-sup', function(){
document.execCommand('superscript', false, null);
return false;
});
// Нижний индекс (sub)
$('body').on('click', '.toolbar-sub', function(){
document.execCommand('subscript', false, null);
return false;
});
// Маркированный список (ul)
$('body').on('click', '.toolbar-ul', function(){
document.execCommand('insertUnorderedList', false, null);
return false;
});
// Нумерованный список (ol)
$('body').on('click', '.toolbar-ol', function(){
document.execCommand('insertOrderedList', false, null);
return false;
});
// Параграф (p)
$('body').on('click', '.toolbar-p', function(){
document.execCommand('formatBlock', false, 'p');
return false;
});
// Заголовок (h1)
$('body').on('click', '.toolbar-h1', function(){
document.execCommand('formatBlock', false, 'h1');
return false;
});
// Горизонтальная линия (hr)
$('body').on('click', '.toolbar-hr', function(){
document.execCommand('insertHorizontalRule', false, null);
return false;
});
// Цитата (blockquote)
$('body').on('click', '.toolbar-blockquote', function(){
document.execCommand('formatBlock', false, 'blockquote');
return false;
});
// Изображение (img)
$('body').on('click', '.toolbar-img', function(){
var url = prompt('Введите адрес изображения', 'https://snipp.ru/demo/526/image.jpg');
document.execCommand('insertImage', false, url);
return false;
});
// Ссылка (a)
$('body').on('click', '.toolbar-a', function(){
var url = prompt('Введите URL', '');
document.execCommand('CreateLink', false, url);
return false;
});
// Удаление ссылки
$('body').on('click', '.toolbar-unlink', function(){
document.execCommand('unlink', false, null);
return false;
});
// Вставить html
$('body').on('click', '.toolbar-html', function(){
var html = prompt('Введите HTML код', '');
document.execCommand('insertHTML', false, html);
return false;
});
// Вставить текст
$('body').on('click', '.toolbar-text', function(){
var text = prompt('Введите текст', '');
document.execCommand('insertText', false, text);
return false;
});
// Выравнивание текста по левому краю
$('body').on('click', '.toolbar-left', function(){
document.execCommand('justifyLeft', false, null);
return false;
});
// Выравнивание текста по центру
$('body').on('click', '.toolbar-center', function(){
document.execCommand('justifyCenter', false, null);
return false;
});
// Выравнивание текста по правому краю
$('body').on('click', '.toolbar-right', function(){
document.execCommand('justifyRight', false, null);
return false;
});
// Выравнивание по ширине
$('body').on('click', '.toolbar-justify', function(){
document.execCommand('justifyFull', false, null);
return false;
});
// Шрифт
$('body').on('input', '.toolbar-font', function(){
var val = $(this).val();
document.execCommand('styleWithCSS', false, true);
document.execCommand('fontName', false, val);
document.execCommand('styleWithCSS', false, false);
});
// Размер шрифта
$('body').on('input', '.toolbar-size', function(){
var val = $(this).val();
document.execCommand('styleWithCSS', false, true);
document.execCommand('fontSize', false, val);
document.execCommand('styleWithCSS', false, false);
});
// Цвет шрифта
$('body').on('input', '.toolbar-color', function(){
var val = $(this).val();
document.execCommand('styleWithCSS', false, true);
document.execCommand('foreColor', false, val);
document.execCommand('styleWithCSS', false, false);
});
// Цвет фона
$('body').on('input', '.toolbar-bg', function(){
var val = $(this).val();
document.execCommand('styleWithCSS', false, true);
document.execCommand('hiliteColor', false, val);
document.execCommand('styleWithCSS', false, false);
});
// Отмена
$('body').on('click', '.toolbar-undo', function(){
document.execCommand('undo', false, null);
return false;
});
// Повтор
$('body').on('click', '.toolbar-redo', function(){
document.execCommand('redo', false, null);
return false;
});
// Удалить
$('body').on('click', '.toolbar-delete', function(){
document.execCommand('delete', false, null);
return false;
});
// Выделить всё
$('body').on('click', '.toolbar-selectAll', function(){
document.execCommand('selectAll', false, null);
return false;
});
// Очистить стили
$('body').on('click', '.toolbar-removeFormat', function(){
document.execCommand('removeFormat', false, null);
return false;
});
// Вырезать
$('body').on('click', '.toolbar-cut', function(){
document.execCommand('cut', false, null);
return false;
});
// Копировать
$('body').on('click', '.toolbar-copy', function(){
document.execCommand('copy', false, null);
return false;
});
В большинстве браузерах при нажатии на Enter, новая строка начнется с преведущего блочного элемента (p, li, blockquote).
Если редактор был пуст, то вставится <div>
, в место него можно использовать <p>
вызвав команду:
Если редактору сделать display: inline-block
, то будут вставляться только <br>
.
В contenteditable клавиша Tab не добавляет отступ, а переключает фокус на следующий элемент. Включить табуляцию можно, добавив CSS-свойство white-space: pre-wrap
чтобы начали выводится пробельные символы, но при этом переносы строк тоже заработают.
И обработчик нажатия клавиши на JQuery:
$('body').on('keydown', '.editor', function(e){
if (e.keyCode === 9) {
e.preventDefault();
var editor = this;
var doc = editor.ownerDocument.defaultView;
var sel = doc.getSelection();
var range = sel.getRangeAt(0);
var tabNode = document.createTextNode("\t");
range.insertNode(tabNode);
range.setStartAfter(tabNode);
range.setEndAfter(tabNode);
sel.removeAllRanges();
sel.addRange(range);
}
});
Результат:
При вставки текста из буфера переносятся все стили и теги скопированные из HTML страницы или Word файла, это можно предотвратить очисткой:
function escape_text(text) {
var map = {'&': '&', '<': '<', '>': '>', '"': '"', "'": '''};
return text.replace(/[&<>"']/g, function(m) {
return map[m];
});
}
$('body').on('paste', '.editor', function(e){
e.preventDefault();
var text = (e.originalEvent || e).clipboardData.getData('text/plain');
document.execCommand('insertHtml', false, escape_text(text));
});
.editor[placeholder]:empty:before {
content: attr(placeholder);
color: #555;
}
.editor[placeholder]:empty:focus:before {
content: '';
}
$('body').on('focusout', '.editor', function() {
var element = $(this);
if (!element.text().replace(' ', '').length) {
element.empty();
}
});
Показать исходный код можно заменив <div>
на <textarea>
.
<input type="checkbox" id="toggle-editor"> Показать исходный код
<div contenteditable="true" class="editor">
...
</div>
$('#toggle-editor').click(function(){
var target = $('.editor');
if ($(this).is(':checked')){
target.replaceWith('<textarea class="editor">' + target.html() + '</textarea>');
} else {
target.replaceWith('<div contenteditable="true" class="editor">' + target.val() + '</div>');
}
});
Для отправки текста вместе с формой нужно добавить скрытый <textarea>
, при событии отправки формы скидывать в него содержимое редактора.
<form id="form" method="post" action="">
<div class="editor" contenteditable="true">...</div>
<textarea id="textarea" name="text" style="dispalay: none;"></textarea>
<input type="supmit" value="Отправить">
</form>
Особенно за 5 пункт.
Авторизуйтесь, чтобы добавить комментарий.