heap.tech
лаборатория велосипедов
×

Native JavaScript BB-tags editor

28 декабря 2015
Порыскав в интернетах на предмет вразумительного редактора BB-тегов, обладающего с нормальным функционалом, дало парочку годных, включая один навороченный wysiwyg. Wisywyg я не хотел, предпочел обычное текстовое редактирование, без визуализации тегов на лету. Появилась тень челенджа, которая будоражила мозги до кончиков пальцев ). Порыскав еще минуты 3 – мысль окончательно взяла верх. На планете явно не хватает еще одного велосипеда, нельзя это так оставлять, нужен свой велосипед с цепью и педалями.
Задача проста – минимальный набор тегов жирный, курсив, подчеркнутый и т.д. Обязательно продвинутые теги типа цвет текста, ссылки или вставить картинку. И, конечно, обрезалкой текста до определенной границы, чтобы было похоже на превью текста c ссылкой «подробнее».
Написать плагин под фреймворк типа jQuery - слишком банально, хочется хардкора. Поэтому чистый JS.

Ну окай.


По ходу пьесы приходится изобретать много чего не нужного, например екстендера объектов типа jQuery.extend(o1,o2) увы нет.

function _extendOptions() { var baseOptions = { target: null, tags: [{ tag: "b", separator: false }, { tag: "s", separator: false }, { tag: "i", separator: false }, { tag: "u", separator: false }, { separator: true }] }; //merge options for (var bO in baseOptions) { if (!options[bO]) { options[bO] = baseOptions[bO]; } } if (!options.tags || !options.tags.length) { options.tags = baseOptions.tags; } }



options – это объект полученный от пользователя. Набор минимальных свойств и объектов определен. Теперь, даже если они не были объявлены или правильно инициализированы и с ними пытаются что-то сделать - не возникнет ошибки. К тому же избавит от кучи проверок в и ловли исключений.
Далее, нужно создавать форму редактора. На чистом JS это более затратно и менее удобно, чем в любимом jQuery.

//format bbObject function _formHTMLInput() { _target = document.getElementById(options.target); if (_target) { var tag = {}; for (var t in options.tags) { tag = document.createElement('div'); if (!options.tags[t].space) { if (options.tags[t].caption) { tag.innerHTML = options.tags[t].tag; } tag.className = 'tag'; tag.setAttribute('tag', options.tags[t].tag); //store to tag.options tag.BBEditor = options.tags[t]; } else tag.className = 'tag space'; _data.tagElements.push(tag); } _data.wrap = document.createElement('div'); _data.wrap.className = 'bbeditor__wrap'; _data.tagElements_wrap = document.createElement('div'); _data.tagElements_wrap.className = 'tags__wrap'; _data.textElement_wrap = document.createElement('div'); _data.textElement_wrap.className = 'text__wrap'; _data.textElement = document.createElement('textarea'); _data.textElement.id = _target.id; _data.textElement.name = _target.name; _data.textElement.className = _target.className; _data.textElement.innerHTML = _target.innerHTML; } }



_data и _target – это глобальные объекты, которые находятся в области видимости.

Наконец самое интересное. Вставка тегов в текстовое поле, применение дополнительных опций тега, например url для или width для image. Тут использование нативного JS дало хоть какое-то преимущество, в виде отсутствия лишнего и ненужного.

function insertBB(tag, tagOptions, justInsertTag) { var tagStart = tagOptions && tagOptions.length ? `[${tag} ${tagOptions.join(' ')}]` : `[${tag}]`, tagEnd = `[/${tag}]`; var textarea_val = _data.textElement.value; var selectionStarPos = _data.textElement.selectionStart; var selectionEndPos = _data.textElement.selectionEnd; var selectedText = textarea_val.substring(selectionStarPos, selectionEndPos); var cursorPosition = 0; if (selectedText && !justInsertTag) { cursorPosition = selectionEndPos + tagStart.length + tagEnd.length; _data.textElement.value = `${textarea_val.substring(0, selectionStarPos)}${tagStart}${selectedText}${tagEnd}${textarea_val.substring(selectionEndPos)}`; } else { cursorPosition = selectionStarPos + tagStart.length + tagEnd.length; _data.textElement.value = `${textarea_val.substring(0, selectionStarPos)}${tagStart}${tagEnd}${textarea_val.substring(selectionStarPos)}`; } _data.textElement.setSelectionRange(cursorPosition, cursorPosition); }


Я надеюсь, что уже все пользователи успели перейти на ecmascript 6. О чем намекает конструкция `${1==1}`.
Первое что делает функция это определяет открывающий и закрывающий BB-тег. В случае, если тег должен содержать определенные опции, типа url, width или что-то вроде, то происходит попытка сериализовать массив tagOptions (tagOptions.join(‘’));
Теперь нужно ловить место курсора, выделенную область текста.

var selectionStarPos = _data.textElement.selectionStart; var selectionEndPos = _data.textElement.selectionEnd; var selectedText = textarea_val.substring(selectionStarPos, selectionEndPos);


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

Плагин доступен на гите - https://github.com/zizzlezee/nativejsbb.

Форки приветствуются!


ЗЫ
Позднее я расскажу как собрать полученный bb-текст на сервере и перебросить его в html.
 
907
0

Оставлять комментарии могут только зарегистрированные пользователи

пока никто не оставлял комментариев