Suit Up! Простой и легкий WYSIWYG

  • Tutorial


Статья делится на три части:
UPD Критика


Вступление


Не так давно, а, точнее, года два назад, в кругу разработчиков, с которыми я имел честь общаться (почти все — новички), каждый, кому поступила задача поставить WYSIWYG, ставили монстрообразный TinyMCE. Этот редактор считался почему-то стандартом у многих веб разработчиков, хотя, мало кому нужны были то большое количество функций, которые предлагались программистам. Тут тебе и то и это. Наверно, таким образом, новички пытались сказать клиенту «смотри, мы тебе на сайт запилили Ворд».

Однажды (не помню при каких условиях), мне захотелось или понадобилось разобраться в том, как работают браузерные «рич эдиторы». Моему удивлению не было предела, когда я сам, не имея каких-либо глубоких познаний в веб разработке, сделал две кнопочки: Bold и Italic, что оказалось очень простой задачей. Мне захотелось больше узнать о том, что же делать дальше. Так я познакомился с серией статей «WYSIWYG HTML редактор в браузере» (по ссылке первая статья, советую прочесть). Но информация на тот момент мне показалась несколько сложноватой. Поэтому я решил методом тыка, наступая на уже растоптанные кем-то грабли, написать свой простой редактор.

Сделал я его в виде jQuery плагина, и, думаю, не стоит отвечать «почему». Получилось кое-как заставить работать его в разных браузерах. Тут мне пришла в голову идея написать статью на хабр, после некоторых доработок. Время шло, допиливание я откладывал, откладывал… Два года, черт, целых два года. Но я постараюсь исправиться.


Простейший редактор


Для того, чтоб дать возможность пользователю менять содержимое блока (в данном случае, обычного дива) просто задаём ему (блоку) атрибут contenteditable:
<div contenteditable></div>

Редактор готов!

Шучу :)

Для того, чтоб проделать некие изменения над текстом (например, задать цвет шрифта), существует метод document.execCommand, который применяется к текущему выделению или позиции каретки. Метод принимает три аргумента:
  1. Имя команды (например, italic).
  2. Показывать ли стандартный диалог для получения значения (поправьте, если определение неверно). Как правило, никто этим не пользуется, поэтому значение всегда будет false.
  3. Значение команды.


Добавим на страницу кнопку Bold, делающую текст жирным:
<button class="bold"></button>

Пишем простейший обработчик клика:
$( '.bold' ).on( 'click', function() {
   document.execCommand( 'bold', null, null ); 
});

Готово. Выделяем текст в диве, нажимает на кнопку: оп, текст жирный. Нажимаем еще раз: хоба, текст стал прежним.

Добавляем еще две кнопки
<button class="italic">italic</button>
<button class="red">Red</button>

Первая будет делать текст наклонным (italic, как и bold не требует значения, так как он и в Африке italic), вторая — менять цвет текста на красный.

$( '.italic' ).on( 'click', function() {
   document.execCommand( 'italic', false, null ); 
});

$( '.red' ).on( 'click', function() {
   document.execCommand( 'forecolor', false, 'red' ); 
});


Команда forecolor, очевидно, не может обойтись без значения, иначе браузер не поймет, какой именно цвет надо задать тексту.

Результат: jsfiddle.net/6BkPu

Вот и всё, теперь дело за малым:
1. Взять .innerHTML дива и делать с ним что угодно: отправить на сервер, вставить в textarea и пр.
2. По аналогии добавить еще команд, которые можно глянуть по этой ссылке.
3. Добавить иконки и поиграться со шрифтами.

Для подсветки кнопки (для проверки текущего значения команд) используются методы document.queryCommandValue и document.queryCommandState. Описывать их поведение не буду, так как оно зависит от браузера. Только производитель браузера решает, бросать исключение или нет, только производитель браузера решает, что вернуть, поэтому предлагаю самим, при желании, разобраться, что к чему. Придется, в основном, разбираться методом тыка.

Чтоб не быть голословным, приведу пример.
document.queryCommandValue( 'formatblock' ) в ФФ и Хроме возвращает следующие значения: h1, h2, p..., несмотря на то, что значения в document.execCommand( 'formatblock', ... ) были обрамлены в треугольные скобки:

,

, для корректной работы в IE, который не принимает в качестве третьего аргумента execCommand( 'formatblock' ... ) значения без скобок. Это понять можно: мы всегда можем определить "имя блока", которое будет всегда одним и тем же. А вот IE... что бы вы думалиии?.. в ответ на вызов queryCommandValue( 'formatblock' ) возвращает "Заголовок 1", "Заголовок 2", "Обычный", соответственно. Отловить имя блока просто невозможно, учитывая, что, очевидно, для каждого языка существует свой индивидуальный набор возвращаемых значений. И это всё в инновационном, быстром и славном IE10. Черт, в инновационном, быстром и славном IE10! Мазилла и Гугл крайне медленно, но уверенно работают над приведением поведения этих методов к одному виду, но Мелкософт вообще не чешется.


Редактор SuitUp


Демо
Итак, хочу показать свою версию легковесного редактора, который, конечно, не так крут, как многофункциональные фено-морозилко-будильнико-утюги, но неплохо справляется с задачами нетребовательного разработчика. Хотя, количество поддерживаемых методов велико.

Задачей было предоставить удобную прослойку между командами и программистом :)

Запускается это дело, как и все плагины таким образом:
$( '.my-textarea' ).suitUp();

В качестве аргументов можно передать список команд:
$( '.my-textarea' ).suitUp( 'italic', 'bold', '|', 'formatblock#<h1>' );

Или один аргумент в виде массива
$( '.my-textarea' ).suitUp([ 'italic', 'bold', '|', 'formatblock#<h1>' ]);


«|» в списке означает обычный разделитель, как элемент дизайна. Элементы, в которых присутствует решетка, говорят нам о том, что перед решеткой находится команда, после — значение. Просто пишете название_команды#значение, получив при этом обычную кнопку. Остальные элементы — это команды, которые следует определить, иначе не известно, как мы должны получать значение. Если поведение не определено, то, по умолчанию, значением будет считаться null. Ниже я попробую привести примеры для большей наглядности.

Определение команд (как должны вести себя команды)

Для определения команд был создан объект jQuery.suitUp.commands, содержащий пары ключ-значение (ваш кэп), где ключём является имя команды, например, 'forecolor', а значение может иметь три типа:

1. Собственно, само значение: для forecolor это может быть red:
$.suitUp.commands = {
  forecolor: 'red'
}

или
$.extend( jQuery.suitUp.commands, {
  forecolor: 'red'
})

или
$.suitUp.commands.forecolor = 'red' // (просто помните, что commands - обычный объект)

Подключаем:
$( '.suitup-textarea' ).suitUp( 'forecolor' );

2. Словарь значений (объект)
$.suitUp.commands.forecolor = {
   'Make Red': 'red',
   'Make Green': 'green'
}

Он преобразуется в обычный тег select, опции которого выглядят как ключ словаря, при выборе которых задействуется значение словаря.

Подключаем аналогично:
$( '.suitup-textarea' ).suitUp( 'forecolor' );



3. Функция, которая отвечает за то, как мы должны получить значение команды. Она асинхронна, то есть, в функции можно создать модальное окно с запросом значения. Для примера приведу обычный propmt:

jQuery.suitUp.commands.forecolor = function( callback ) {
   var color = prompt( 'Введите цвет', '' ); // произвольным образом получаем значение
  callback( color ); //передаём в коллбек
}

И снова:
$( '.suitup-textarea' ).suitUp( 'forecolor' );


Часть команд определена в самом скрипте, часть — в отдельном файле, список команд достаточно велик для того, чтоб не заморачиваться с определением. То есть, можно просто передать аргументы в метод suitUp и это будет просто работать:
$( 'textarea' ).suitUp( 'italic', 'bold', '|', 'link', 'formatblock#<h1>' );

Попробуем добавить несколько кнопочек. Смотрим команды на сайте мозиллы. Скажем, нам хочется добавить:
1. Опять Bold.
2. Выбор шрифта.
3. Диалог, в котором пользователю нужно ввести значение цвета в кастомное окошко и нажать "ок".

И представим, что ни одна из этих команд не определена.

$.extend( $.suitUp.commands, {
	bold: null, // строка для наглядности, не определенные команды по умолчанию имеют значение null
	fontname: {
		Arial: 'arial',
		Times: 'times',
		Verdana: 'verdana'
	},
	forecolor: function( callback ){
		var blackBackground = $( '<div/>' ).css({
				background: 'black',
				position: 'absolute',
				top: 0,
				left:0,
				opacity: .5,
				width: '100%',
				height: '100%'
			}).on( 'click', function(){
				popup.add( this ).remove();
			}).appendTo('body'),
			
			popup = $( '<div/>' ).css({
				padding: '10px 20px',
				width: 200,
				position: 'absolute',
				background: 'white',
				top: 200,
				left: $( window ).width()/2 - 110,
				zIndex: 100
			}).appendTo('body');
		
		$( '<input/>' ).appendTo( popup );
		
		$( '<button/>' ).appendTo( popup ).text( 'Go!' ).on( 'click', function() {
			var val = popup.children( 'input' ).val();
			blackBackground.add( popup ).remove()
			callback( val );
		});
	}
});


Запускаем
$( '.suitup-textarea' ).suitUp( 'bold', 'fontname', 'forecolor' );








С первой и второй командой, думаю, всё понятно. В третьей большую часть кода занимает создание элементов, стоит только обратить внимание на обработчик клика на button. В нем, после получения значения цвета и удаления попапа, вызывается callback, в который и передаётся полученный цвет. Callback возвращает редактору фокус, восстанавливает выделение, сделанное до нажатия кнопки и применяет команду forecolor к выделению.

document.execCommand( 'forecolor', false, 'введенное значение' );

Внимание! Несмотря не то, что команды из разных источников обозначены в виде camelCase, для корректной работы плагина, все символы должны быть в нижнем регистре. Это касается и значений команд.

Кастомные команды

Кроме возможности использования стандартных команд, плагин поддерживает создание собственных элементов. Дабы не мудрить, я решил сделать добавление кастомных контролов обычными функциями, возвращающими элемент.

Как и в случае с командами, для кастомных элементов используется обычный объект:
$.suitUp.custom = {};

Добавим элемент, при нажатии на который вызывается обычный алерт с приветствием.

$.extend( jQuery.suitUp.custom, {
	helloWorld: function() {
		return $( '<span/>', {
			'class': 'suitup-control' // задаёт размеры кнопки
		}).css( 'backgroundColor', 'red' ).on( 'click', function() {
			alert( 'Hello World!' );
		});
	}
});


Теперь вызываем:
$( '.suitup-textarea' ).suitUp( 'bold', 'fontname', 'forecolor', 'helloWorld' );



Обратите внимание, что в кастомных командах регистр не имеет значения.

По дефолту, в списке кастомных элементов наличествует элемент "link", который кроме добавления ссылки (команда "createlink"), убирает ссылку в текущем выделении (команда "unlink").

... {
	link: function() {
		return jQuery._createElement( 'a', {
			className: 'suitup-control',
			href: '#'
		}).attr({
			'data-command': 'createlink' // добавляет такой же стиль как и у команды createlink
		}).on( 'click', function() {
			if( !$.suitUp.hasSelectedNodeParent( 'a' ) ) {
				document.execCommand( 'createlink', false, window.prompt( 'URL:', '' ) );
			} else {
				document.execCommand( 'unlink', false, null );
			}
		});			
	}
}

Как видно, кастомные элементы нужны не только для создания необычных контролов (как в случае с helloWorld), но и позволяет решить проблемы со стандартными командами.

В итоге имеем следующие способы создания кнопок:
  • В перечислении аргументов suitUp указать не определенную команду (которая не определена в commands), при этом значение по умолчанию будет null.
  • Указать не определенную команду задав значение через решетку (forecolor#red)
  • Определить команду тремя способами:
    1. Задать значение.
    2. Задать список значений.
    3. Задать функцию, "добывающую" значение (например, какой-нибудь colorpicker).

    И указать её в качестве одного из аргументов suitUp.
  • Добавить кастомный контрол.


Список команд по умолчанию

Вместо постоянной передачи аргументов в метод suitUp можно объявить набор кнопок лишь однажды:
$.suitUp.controls = [ 'bold', 'italic' ];
$( '.suitup-textarea' ).suitUp();
// почти то же самое, что и
$( '.suitup-textarea' ).suitUp( 'bold', 'italic' );

В массиве controls содержится список кнопок, которые подключаются по умолчанию. Это самый обычный массив. Например, можно добавить к списку по умолчанию еще один элемент:

$.suitUp.controls.push( 'forecolor' );
$( '.suitup-textarea' ).suitUp();

Или сделать так:
$( '.suitup-textarea' ).suitUp( $.suitUp.controls.concat([ 'forecolor' ]) );
для того, чтоб добавить кнопки для конкретного редактора (если их несколько на странице).

Несколько дополнительных функций

Для упрощения некоторых действий, в том числе, создания альтернативных контролов, в объект $.suitUp добавлено несколько функций. Сразу предупреждаю, они писались для использования внутри плагина и в этих рамках работают неплохо. Если вы собираетесь их юзать отдельно, они могут повести себя не так, как расчитывалось.

$.suitUp.getSelection
Возвращает текущее выделение. Для нормальных браузеров и для ослов возвращаемые значения будут разными.

$.suitUp.restoreSelection
Восстанавливает выделение. В качестве единственного аргумента передается значение, полученное в getSelection.

var sel = $.suitUp.getSelection();
// делаем что-то, при этом теряя фокус

$( '.suitup-editor' ).focus(); // возвращаем фокус
jQuery.suitUp.restoreSelection( sel ); // восстанавливаем выделение


$.suitUp.getSelectedNode
Возвращает ноду текущего выделения (это может быть как тег так и текстовая нода)

$.suitUp.hasSelectedNodeParent
Малозначимая функция, используется только в кастомном элементе "link". Проверяет, есть ли у ноды текущего выделения родитель с тегом, определенным в единственном аргументе.
jQuery.suitUp.hasSelectedNodeParent( 'a' ); // true/false


Скрипт тестировался в Chrome, Firefox, IE10 (+IE7 Mode)
На скорую руку создал репозиторий, пользуйтесь: github.com/finom/Suitup

В файле extended-commands.suitup.jquery.js содержится несколько команд для тестов. Раскомментируйте код с 50 строки и оцените изобилие далеко не всех команд, которые поддерживаются плагином.



С опечатками и не точностями, пожалуйте в личку.

Лучей бобра.

UPD

Критика из комментариев

Наверное, стоит уведомить потенциальных пользователей, с какими проблемами они могут столкнуться при работе с редактором. Вся критика из комментариев сводится к особенностям браузеров, которые могут быть неподобающими, если проект требует унифицированного форматирования.
1. Для разных браузеров некоторые команды обрамляют текст в разные теги. Например, команда bold в Chrome обрамляет текст в тег b, а в IE в тег strong.
2. Обработка пользовательских действий может быть разной в разных браузерах (примеров пока что не приходит в голову).
3. Юзер может использовать форматирование, которое не подразумевалось. Например, может скопировать текст из ворда и вставить в редактор.

Всё это — дань компактности плагина. Если желаете использовать редактор для серьезного публичного проекта, я, как автор, советую воспользоваться альтернативными решениями. Редактор подойдет для небольших проектов и внутреннего использования, например, в админках. Впечатление от использования в таком ключе — исключительно положительные.

Еще раз добра.
Поделиться публикацией

Комментарии 88

    +4
    Спасибо за описание своего труда. Я в своём проекте пользуюсь: jsRedactor, работает и на iOS и во всех браузерах, в том числе IE. Очень нравится и код открыт для модификации.
      0
      Пожалуйста :)
      suitUp пока что не тестировался на мобильных. jsRedactor был одним из редакторов, которыми я вдохновлялся.
        0
        Спасибо, было интересно посмотреть вашу реализацию. Что касается jsRedactor, хотел вас обоих спросить, были случаи с клиентами когда им чего-то в нем не хватало?
          0
          Я им не пользовался в проектах, ничего не скажу.
            0
            На этот случай там есть возможность написать плагин. Но увы сам редактор платный.
              +3
              Это понятно. Мне интересно или большинство пользователей это устраивает. У меня был случай когда заказчик захотел Changes Tracker + comments. Вот это было что-то, я долго пытался отговорить его от этой затеи, но он не хотел слушать.

              Добрый человек Chesnovich ниже сообщил приятную новость:
              Для Yii-фреймворка команда скинулась и купила лицензию. Можно пользоваться бесплатно :)
                0
                это стоит повесить на сабмит и это задача не редактора, а системы.
                  0
                  Не совсем так. Когда вы пишите текст в редакторе, он должен выделяться каким-то цветом и к каждой измененной строке нужно повесить коммент. Очень похоже на то что есть в ворде. И поверьте, не очень просто следить за курсором и выделять именно ту область что нужно когда там уже есть куча тегов и текста.
                0
                Когда-то он был опенсорс, до какой-то старой версии. Есть мнение, что старую версию можно использовать как основу для форка бесплатно.
              0
              Под андроидом демо пример, вроде как, работает.
              +8
              Цена 100-400 долларов. ИМХО, немного дороговато за WYSIWYG.
                +5
                Единственный минус этого редактора что нельзя вынести панель. То бишь можно, но только если у вас панель для одного инстанса. Да и код он генерит иногда так себе.

                для себя просто переписал nicEdit, заставил его использовать jQuery, убрал устаревший код и т.д, Словом вышло не хуже решения от imperavi. Если кому интересно, могу выложить это дело на gitHub.
                  +4
                  Было бы интересно ознакомиться
                    +1
                    Ближе к выходным выложу, там рефакторинг небольшой нужно сделать. Могу потом описать, чем лучше/хуже других решений.
                      +1
                      Выложили? Сообщите, пожалуйста.
                  +5
                  Для Yii-фреймворка команда скинулась и купила лицензию. Можно пользоваться бесплатно :)
                  +4
                  Есть бесплатная старая версия для некоммерческого использования
                  github.com/dybskiy/redactor-js
                  0
                  о боже! дяденька, вы где были раньше! я искал это целую вечность! лучей добра вам много!
                    0
                    Могу сказать, что сломать им разметку лишь ненамного сложнее, чем любым другим редактором. Хотя если включить режим paragraphy и регэкспы чуть подправить то уже сложно что-то сильно поломать.
                  0
                  оффтоп, а нет ли такого расширения для форм в Chrome?
                    +1
                    В опере слетает фокус при переходе на кнопки — видать тот же баг что и в большинстве редакторов при работе с выделением текстов.

                    Операции вставки текста ломают структуру параграфов.

                    в webkit-ах не фиксится переход на новую строку. Должен создаваться параграф, а вставляется br. Словом в моих проектах использовать эту штуку не буду. А учитывая что подобных мини-редакторов пруд пруди, то выбор есть. Хотя я уже отчаялся и просто написал под себя.

                      +1
                      Проблема с параграфом решается, если каретка находится в параграфе (а так она в диве). Исправил, обычным обрамлением текста в <p>.
                      +1
                      jQuery._createElement = function( tagName, props ) {
                      	return $( $.extend( document.createElement( tagName ), props ) );
                      };
                      

                      О ужас! Зачем же Вы так? Грешновато, грешновато.
                        –3
                        Поясните. Мне совершенно не нравится встроенная в jQuery возможность создания элементов.
                          +6
                          Тоесть Ваш вариант

                          $._createElement('a', {
                              'className': 'link'
                              'href': '#'
                          } );
                          

                          чем-то лучше чем

                          $('<a>', {
                              'class': 'link'
                              'href': '#'
                          } );
                          

                          ?
                            –2
                            Тем, что в первом случае указываются свойства, во втором — атрибуты (моя прихоть; и да, я знаю о методе .props). $._createElement в самом плагине ссылается на функцию create (то бишь с возможностью минимизации).
                              +4
                              У Вас какие-то странные представления о минимизации, если бы Вы и заботились о ней, то написали бы совсем другой код.
                              В чем выигрыш задания свойств — я тоже не понимаю. Тем более что в случае boolean-атрибутов jQuery значение свойства тоже меняет. Что contentEditable, что value, text, даже html можно задать с помощью указанного выше мной способа. Даже если не хотите избавляться от метода, то не стоит его писать в jQuery, у Вас же есть свой jQuery.suitUp, почему бы туда не положить?
                                –3
                                Объясняю: ваш вариант парсится регуляркой, создаётся тег, добавляются атрибуты, которые внутренними механизмами браузера меняют свойства, тег обрамляется в jQuery.

                                У меня: создаётся тег, добавляются свойства, тег обрамляется в jQuery.

                                Да и не зачем использовать жикверь, если можно воспользоваться встроенными функциями, что за мода?
                                  +6
                                  Вы странный, используете jQuery и, тем не менее не хотите им пользоваться. Если бы Вас волновало быстродействие, то Вы бы пробежались по объекту for'ом и задали атрибутам значения через setAttribute, потому что extend с ним тоже не сравнится :) Выбрали jQuery — пишите на нем и пишите красиво, чего возмущаться. Вы же даже data-атрибуты вешаете через attr, разве это ок?

                                  А вообще, милости прошу в мою серию статей о jQuery, возможно Вы узнаете что-то новенькое.
                                    –4
                                    Во-первых атрибуты использовать не нужно, если есть свойства. Во-вторых возмущаюсь здесь не я. Как решил правильным сделать, так и сделал.

                                    И милости прошу форкнуть проект и сделать так, как нравится.
                                      +2
                                      Точно, опечатался с setAttribute. Хотел написать «Пробежались бы по объекту for'ом и задали свойства напрямую», конечно.

                                      Извините что потревожил, я не хотел обидеть, просто у меня возникло ощущение того, что Вы так пишете, потому что не представляете как работает jQuery. Оказалось, это неправда.
                                        –4
                                        Более того, у меня где-то есть недописанный аналог jQuery, но после сообщения о jQuery 2.0 затея была заброшена. Вот, если интересно: javascript.ru/forum/project/24387-prototip-biblioteki-jnext.html
                                          +1
                                          Вы серьезно думаете что это претендует на звание аналога jQuery?
                                            –3
                                            Что это за троллинг?
                        0
                        Очень нравится редактор WysiBB. Посмотрите его, он вам понравится. Тоже легкий. А вообще основная проблема всех WYSIWYG-редакторов в том, что на выходе получается говнокод.
                          0
                          Да, от говнокода можно исправиться только используя огромные по количеству строк редакторы. Этого частично можно избежать, не используя команды forecolor, fontname и подобных, добавляющих тег font.
                          +4
                          Я извиняюсь, но название надо было поаккуратнее делать — я два раза читал и Shit Up! получалось) Ну спасибо за работу)
                            +1
                            Хах, спасибо :)
                            +4
                            Всем, кто не в курсе — сильно обновился Ckeditor — стал прям конфетка. И внутри, и снаружи.
                              0
                              Такие дела при скролле в демке на этой странице



                              Google Chrome 23.0.1271.101, Mac OS X 10.8.2
                                0
                                Это беда конкретно хрома.
                                  0
                                  Не думаю… это скорее беда конкретного компьютера.
                                    +1
                                    Получается, уже как минимум двух конкретных компьютеров, т.к. у меня этот баг повторился. Правда, не в такой сильной мере, но всё же. А там, где два, там и n… Так что баг неприемлем, я считаю.
                                      0
                                      UPD: обновил хром до версии 24.0.1312.52 m и баг благополучно скончался.
                                        0
                                        Вот и славно
                                          0
                                          Скорее всего это баг при отрисовке контента фрейма.
                                  0
                                  Мой друг научил меня одной простой вещи, как надо тестировать визуальные редакторы. Надо сделать так:
                                  1. Написать три-чтере слова
                                  2. Выделить два слова — первое и последнее — жирным
                                  3. Выделить от половины первого слова до половины последнего
                                  4. Вставить ссылку
                                  5. Посмотреть HTML

                                  Это универсальный тест, который отсеивает… ну не знаю… почти все «легкие» визуальные редакторы. С ckeditor этого не случалось и раньше, а теперь этого вообще быть не может. Помимо этого, совершенно замечательный API редактора позволяет дописывать собственные плагины. Это я уже не говорю о конкретно своих собственных фишках этого замечательного редактора — поработайте, например, с таблицами. Или с изображениями. И, конечно, на выходе получаем качественный (у меня не получилось испортить HTML) код. Опять же, давайте не забывать, что сайты делаются не для программистов, а им чем интуитивнее и больше возможностей — тем лучше.

                                  Я к чему — к тому, что, конечно, это серьезно нагружает браузер. А как вы хотели.
                                    0
                                    Тут скорее отрисовка во фрейме виновата. А нагружают эти все плюшки не сильно к слову. Да и кода поддержание чистоты занимает не много.

                                    в кашим тестам стоит еще вставку добавить — тупо сделать два параграфа, скопировать часть от одного и вставить в другой. И посмотреть что оно сгенерило.
                                      0
                                      Вы, наверное, издеваетесь, да? Не подумайте, что я против валидного HTML и т.п., но сейчас вам не понравится то, что скажу — юзеру или человеку, который пользуется редактором в общем случае абсолютно всё равно, что там будет «под капотом» на выходе, ему надо, чтобы это выглядело так как он хочет без всяких проблем, таких как эта. Я сам за чистоту кода, стандарты и другие замечательные вещи, но такое поведение редактора неприемлимо. И все равно — проблема ли это редактора или конкретного браузера, могли бы и redraw после скролла поставить персонально для хрома, пока не появится нормального решения проблемы.

                                      P.S. Выше говорят, что после обновления хрома проблема вроде исчезает, ну и хорошо :)
                                        0
                                        Нет, я совершенно не издеваюсь. Я пишу с точки зрения разработчика, а мое «редактором не только программисты пользуются» — это просто пожелание меня как разработчика. Да там, на самом деле, много всего, что у них есть хорошего — такого, что в сумме перевешивает косяки с отрисовкой… Но это тема, скорее, отдельной статьи.

                                        И потом — нам, обычным, ни в чем не повинным людям потом смотреть на сайтах этот HTML. Так что я не понимаю причину вашей реакции.
                                          0
                                          То что редакторы генерят невалидную разметку частенько выливается в очень противные баги. Скажем в одном браузере, в котором пользователь что-то менял, добавился пустой p тег, или вложенный p тег, и у него выглядит все ок. А в другом браузере уже не все ок.
                                    0
                                    Стоит также заметить, что выходные данные такого редактора обязательно нужно чистить от всяких гадостей вроде яваскрипта, iframe-ов, embed-ов и прочих прелестей.

                                    Чтобы продвинутые пользователи не вставили на ваш сайт ничего, чего вам не хотелось бы видеть.
                                      0
                                      Это уже задача серверной стороны.
                                        0
                                        Да, вы совершенно правы =)
                                          0
                                          Отчего-же? Банально при сохранении пройтись регэкспами. Реализации методов stripTags для JS валом.
                                            0
                                            Валидация на сервере обязательна, при любых условиях.
                                              0
                                              Это то да
                                        0
                                        В хроме есть баг небольшой:
                                        1) Выделяем в редакторе весь текст, нажимаем кнопку delete (удаляем весь тескт)
                                        2) Нажимаем BackSpace (удаляются все теги)
                                        3) Начинаем ввод текста и в итоге имеет неофрмленный текст с divавми вместо абзацев

                                        habrastorage.org/storage2/d73/483/855/d73483855d21b851bf2c5bddd41e79b0.jpg
                                          0
                                          Угу, такие проблемы иногда встречаются. Их решают сложные редакторы, которые следят за тем, чтоб заставить работать код во всех браузерах одинаково. Моё решение — лишь простое взаимодействие с execCommand, а дальше дело за браузером.
                                          0
                                          Супер!
                                          Молодец, уважаю таких, кто создает.
                                            0
                                            Спасибо :)
                                            0
                                            Уважаемый автор спасибо за обзор…

                                            Но мне больше всего интересно следующее: есть так называемый плагин support.skype.com/ru/category/SKYPE_CALLS_FROM_BROWSERS/
                                            Еще есть плагин ADBLOCK.

                                            Skypetocall типа внедряет свой код в DOM.
                                            Можно ли похожим принципом внедрить ваш плагин в редактор хабра (плагин для хрома). ???

                                            Меня жутко достало тегить текст все время, когда пишешь статьи (хотя сам написал всего 3)

                                            Сообщество бы не осталось у вас в долгу…
                                              0
                                              Можно, но не все теги будут работать. Например, о специфических, таких, как anchor или hh вообще можно забыть. Только простое форматирование: жирный, наклонный, зачеркнутый, подчеркнутый, заголовки…
                                              0
                                              > мало кому нужны были то большое количество функций, которые предлагались программистам.

                                              Так не обязательно их использовать. Хотя соглашусь что весит он многовато.

                                              > Моему удивлению не было предела, когда я сам, не имея каких-либо глубоких познаний в веб разработке, сделал две кнопочки: Bold и Italic, что оказалось очень простой задачей.

                                              А заставить все браузеры использовать одинаковые теги для оформления тоже просто? :) TinyMCE кстати позволяет вообще любые теги использовать для всех стилей, очень пригодилось на одном из проектов.
                                                0
                                                Хм. Дак вроде-бы все теги одинаковые (мозилла только балуется с атрибутами). А обрамление в теги записал в ToDo.
                                                  0
                                                  А как же b и strong? (в IE «древних» точно были с этим проблемы)
                                                    –1
                                                    Точно, вы правы. Да, такая проблема наличествует.
                                                    0
                                                    Проблема решается своим враппером над execCommand. Мол в зависимости от команды и браузера что-то делать по другому. Скажем вместо Bold можно обернуть выделенный текст в теги, вставить заместо выделенного фрагмента.

                                                    По сути основная часть любого визивига это API для работы с положением коретки или выделенными фрагментами. А дальше все сводится к банальным проверкам, обрамлению кода, парсингу кода и т.д. Это по большей части рутина, и это нужно хорошо тестить.
                                                      +2
                                                      github.com/tcr/selection.js — вот неплохая библиотека для работы с выделением текста. Очень даже полезная.
                                                        0
                                                        Круто, давно такую штуку искал, спасибо.
                                                          0
                                                          Именно эта библиотека, только немного переделанная, есть и в редакторе от imperavi.
                                                            0
                                                            Да почти все реализации подобных библиотек похожи. Тут на самом деле сложно сделать что-то другое.
                                                    0
                                                    Такой редактор сложно назвать жизнеспособным. Если элементарно скопировать что-то из Ворда, то получится весьма занятный код на выходе.
                                                      –1
                                                      банально делаем обработчик события paste (а он вызывается до того как контент будет вставлен), в нем забираем весь контент редактора в буфер, запоминаем позицию каретки и делаем setTimeout ненадолго (на сколько ненадолго значения не имеет, так как даже если поставить 0, обработчик отработает уже после вставки). Далее забирается содержимое редактора, вставляется из буфера, восстанавливается позиция каретки и отчищенный регэкспами или любым другим способом HTML вставляется в редактор.

                                                      А уже вариантов очистки кода можно сделать множество. От работы над фрагментом DOM, до банальных регэкспов (сложнее, но отработает в разы быстрее).

                                                      Словом я бы вообще выносил такой функционал в плагин. Месяц назад для старого проекта делал подобное.
                                                        0
                                                        Ну, если в качестве развлекухи, то да. Я лет 7 назад писал что-то подобное этому. Но для реальных проектов проще использовать TinyMCE в итоге.
                                                          0
                                                          Случается что функционал WYSIWYG Редактора приходится существенно переделывать. Да и зачем мне редактор который тянет за собой тучу кода для работы в designMode? TinyMCE, CKEditor и прочие это монстры, которые лично мне неприятны. Ибо что-то изменить в них, не сильно вникая в архитектуру не выйдет. Разве что мелкий плагин какой реализовать.

                                                          Мне же от визивига нужна гибкость. Скажем хочу что бы панель редактора можно было бы вынести (скажем на страница n инстансов и для них одна панель). Если без извращений и кастылей, то с этой задачей увы кроме nicEdit я ничего не нашел. Но он страшно долго не поддерживался, использовал устаревшие фичи как javascript так и html, ну и словом пришлось переписать половину.

                                                          На проектах где редактор используется только в ключе редактирования контента, а не как компонент более сложной системы, я лично использую CKEditor с небольшими изменениями (мелкие плагины для работы с картинками, интеграция в Symfony).
                                                            0
                                                            Я согласен полностью. До сих пор удивляюсь, что никто не сверг этих динозавров с их пьедесталов. Хочется чего-то современного, легкого и простого. Но пилить редактор пока не хочется, задач более серьезных хватает.
                                                        0
                                                        Дополнил статью критикой.
                                                          0
                                                          В плане использования редактора в правильных руках — полностью поддерживаю. Просто, как всегда, хотелось универсальности и выкинуть, наконец, этого динозавра TinyMCE. :-)
                                                        0
                                                        Я тоже друзья для себя пишу твой редактор. Тема мне эта интересна. Но не все так просто.

                                                        Вдохновлен был редактором Камерона Адамса. У него там было всего пару кнопок, и мне что у него понравилось так то что пользователь не мог изуродовать текст. Или это было сложно сделать. Ничего лишнего, но все работало. Люди клацали и не ломали сайты. Но там мне много чего не нравилось и я решил написать свое. Все иначе.

                                                        Покажу пару скриншотов, возможно кому-то будет интересно.

                                                        d.pr/i/DXdR

                                                        Это общий вид редактора. Пару кнопок всего. Но я это сделал не просто так. У меня родилась идея не сувать как в некоторых редакторах кнопки абы какие, а только те что нужны пользователю. Например выбирая картинку у клиента появляются кнопки соответствующие типу данных которые сейчас в фокусе.

                                                        d.pr/i/9yaf

                                                        Вот например клиент кликнул по картинке — и все лишние кнопки спрятались. Появились только кнопки по работе с картинками. Тут еще не все реализовано, есть идеи которые еще не воплощены, но все же идея ясна. Можно просто поменять положение картинки, слева, по-центру, справа, обтекание текста слева и справа, стрелки — откроется ли при клики на сайте картинка а-ля лайтбокс — в большом размере, хотя в самом ХТМЛ обрезанная картинка.

                                                        Например выбирая заголовок мы видим всего пару кнопкок. Тип и стиль.

                                                        d.pr/i/Chw7

                                                        Это сделано для того чтоб пользователь не мог сделать заголовок жирным, или курсив, вставить в него ссылку и так далее, потому как заголовок это заголовок. Но его можно стилизировать выбирая соответствующий стиль, внутри подставляется класс для объекта и готово.

                                                        Само главное что в настройках редактора можно самому прописать какой тип данных какие стили принимает. Например заголовок 2 уровня можно покрасить в 2 вида стилей, а в других — иная ситуация.

                                                        d.pr/i/rHKh

                                                        Тоже касается и списков, таблиц, оформление видео и так далее. Даже для картинок можно задавать стиль обрамления, если это понадобится.

                                                        Еще очень важный момент. В своей работе я хотел сделать так чтоб исходный код всегда был валиден. Чтоб в нем небыло косяков. И потому код постоянно в процессе набора валидируется. Расставляются правильно точки и запятые, убераются пробелы и прочее что портит код. И на выходе у тебя всегда все чистенько d.pr/i/OzlC

                                                        d.pr/i/5Igo

                                                        Но работы конечно дофига.
                                                          0
                                                          Из простеньких есть еще bootstrap-wysihtml5, весьма симпатичный.
                                                          Там кстати внутри используется iframe. Предполагаю, что это связано с безопасностью, но детально не разбирался. Если кто знает, расскажите.
                                                            0
                                                            За счет этого получаем изолированность стилей (к фрейму можно подключить те же стили, что будут использоваться при выводе контента, и тогда конфликтовать со стилями страницы на которой находится редактор ничего не будет). Ну и да, из фрейма получить доступ к чему-бы то ни было на основной странице уже сложнее, так что и по поводу безопасности есть профит.
                                                              0
                                                              Какой конкретно профит? Все визивиги создают фрейм самостоятельно, то бишь, он будет находиться на том же домене.
                                                                0
                                                                Да, как-то не подумал. Самая главная причина, пожалуй, это факт того что при вставке определенных эллементов в редактируемую область она может разделиться. Если же областью будет являться целый фрейм, то таки проблем быть не модет. Сам сталкивался с такой проблемой, правда без фреймов решилось.
                                                            0
                                                            Прямо бальзам на душу. Со времен середины нулевых WYSIWYG-редакторы — одно из немногого что стало проще.
                                                              0
                                                              Вот ещё довольно обширный список: github.com/cheeaun/mooeditable/wiki/Alternative-Javascript-WYSIWYG-editors
                                                                +1
                                                                когда-то делал большой ресерч визивигов, после него и решил переписать какой-то более мение вменяемый под себя. Из этого списка наиболее достойными оказались NicEdit, Redactor.js ну и Aloha мне показался интересным. к сожалению первый давно серьезно не обновлялся, в нем куча багов и т.д. а последние два мне не подошли пока ни на одном из проектов. Все поддержка IE7 которая у меня последние пол года почти на каждом проекте.

                                                              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                              Самое читаемое