(Art by http://www.simonstalenhag.se/)
Предыстория / Дисклеймер
Всем привет, данная статья является по факту материалом для моего выступления в понедельник на марафоне в Киеве. Но не думайте, что вы аудитория на которой я отрабатываю текст, просто мне так проще подготовиться.
На данный момент я Front-end разработчик в Conductor/WeWork. Мы не занимаемся разработкой редакторов, как собственно и я, по вечерам не разрабатываю такие вещи. Это больше об опыте который я получил на прошлом проекте в компании УТИ(сокращенно), утипути, ути-…. Это было достаточно давно, но как оказывается, тема актуальна и по сей день, как и косяки связанные с ней.
Речь идет как правило о сайт визитках, лендингах, и более менее функциональных сайтах, но разумеется не о бек офисах или админках.
UTIEditor special for you <3
Зачем нам нужны такие редакторы?
Тут достаточно много ответов на этот вопрос, многие из вас, я уверен догадываются и так.
Я отвечу на этот вопрос с точки зрения УТИ, компании которая предоставляет услуги по разработке сайтов.
УТИ по сути своей — компания ширпотреб. Уверен в свое время в таких поработал каждый, десяток сайт визиток на человека, десяток интернет магазинов и фирменная вишенка — своя CMS.
Вот вы сделали очередной лендинг, сайт визитку и отдали клиенту, а он прибегает через день и просит поменять текст. Через еще день, поменять цвет фона и добавить всего одну картинку. Для клиента это выглядит, как нечто простое и быстрое. С этим трудно поспорить, оно так и есть. Но для вас это означает, что вам нужно выдернуть разработчика/дизайнера с его текущих задач. Не говоря уже о работе менеджера который тратит время на коммуникации. Соответственно, вы выставляете счет и он не маленький. Клиент негодует и жалуется. В следующий раз вы уже думаете наперед о таких вещах и начинаете задавать вопросы, закладывать в ТЗ функционал редактирования.
Тут пожалуй стоит перейти к обсуждению этого функционала, ведь он очень разный.
Хотелось бы добавить, что я не буду включать в этот разговор такие CMS как WrodPress, Drupal, мы сконцентрируемся на рынке редакторов.
Дебри UTI
Первым, что приходит на ум, сделать форму, где пользователь может изменить, то, что он хочет:
Немного не из реальной жизни название полей, но суть вы уловили.
С точки зрения компании, она выполнила свой долг и даже позаботилась об "кастомизации". Но у этого подхода по прежнему ряд ограничений.
Плюсы такого подхода:
- Легко реализовать
- Легко использовать
- Решает проблему
Минусы такого подхода:
- Нет возможности изменить дизайн
- Нет возможности изменить поведение
- Нет позиционирования
- Можно редактировать, только, то, что было заложено в форму
Вы еще не подумали о СKEeditor? Звезда всех времен и народов. По жалуй это следующий шаг улучшить редактируемость контента на сайте.
Тут своего рода возможность форматировать текст и даже вставлять какие-то дополнительные элементы. Где-то на этом месте, до того как я пришел в компанию, у ребят возникла идея, сделать редактор страниц с drag and drop прямо в CKEditor. У меня к сожалению не осталось чего либо, что бы я мог показать, но вы попробуйте представить этот ужас :)
Но на текущий момент, 2018 год так сказать, CKEditor немного продвинулся в этом направлении, что достаточно, таки забавно. Чем не полноценная страница?
Но все это было не удобно как в разработке так и в использовании. И зная только backbone и jquery, я уже смело называл себя фронт-енд разработчиком и решил предложить им разработать полноценный редактор, на подобие Wix, Squarespace, Tilda, LPGenerator… Тогда они были по факту моими конкурентами. Но так как УТИ компания no-name, они об этом не знали. И так и не узнали и не узнают. Хотя спонсором марафона выступает Wix. Ох, это только совпадение.
Не узнали они, потому, что все по классике — компании было пофигу на проект.
Это два скринкаста, они не идеальные, с косяками, но я уверен общее представление о редакторе они предоставят.
На этом проекте я был один. Дизайнером, продукт менеджером, фронт и бек, а тестов не было и нет. НО правда были мануал тестировщики, которые приходя в компанию, закалялись в огне реактора. Тестировщики конечно, были веселые. Что бы вы понимали уровень — как-то раз, одному из них выбило:
И мне зафайлили баг, мол сайт не работает. У парня просто выпал шнур интернета из системника :)
Каждый раз по новой мне приходилось объяснять, что можно сделать в редакторе, что нельзя и почему это работает так, а это вот так. На самом деле, для меня это было прикольно. Своего рода первая линия фидбека. Клиентов практически не было и редактор эволюционировал по большей части благодаря им.
Есть у кого-то предположения какой стек я использовал? Приложение выглядит большим, солидным так сказать. Оно не глючит как и не глючило 5ть лет назад.
Ох а какой шикарнйы CI/CD процесс у меня. В прод уходил сколько хочешь раз в день.
Под кодовым названием Filezilla, drag and drop, repeat. Сперва конектишся по фтп используя файлзиллу, делаешь билд, выкачиваешь текущий билд, ложишь в папку с названием backup, закачиваешь новый билд, и провереяшь, не умер ли там кто. Если все таки помер, то заливаешь файл из папки бекапа. Но честно сказать, момент с бекапом, я делал не всегда, зависело от набора нового функционала. А учитывая, что редактор был в нескольких проектах, говорить, что я заходил в каждый и проверял не упало ли там, что — я тоже не буду. А мы еще использовали SVN, придает вкуса?
Что же по поводу стека?
Backbone + jquery + 0 тестов.
Модуля, асинхронная подгрузка аля код сплитинг и серверсайд рендеринг. Тогда такие термины особо то и не использовались, это было дефакто простая оптимизация. Но сейчас время маркетинга в опенсорсе и итс революшин!
Что бы вы понимали, Wix того времени, от Wix редактора сейчас особо сильно не отличается. Разве, что в нем больше шаблонов, заготовок и готовых компонентов. Они мигрировали реакт, полностью или нет, я не знаю. Глючит он абсолютно так же, только уже на макбуках с оперативкой от 16гигов, с более крутой системой рендерига под названием виртуал дом и крутых веб движках. В проектах такого масштаба, инструменты особой роли не играют, это больше разговор о архитектуре и UI/UX. Причем UI/UX намного важней. От него зависит и сложность реализации, перфоманс и уровень желания убить себя при создании веб сайтов вашим редактором. Клиенты как правило будут далекие от разработки, далекие от всех этих CSS правил, свойств, DOM атрибутов и параметров. Среди них будут и более менее разбирающиеся люди которые будут обслуживать ваших клиентов и в дальнейшем уводить их. Но не будем о грустном, давайте попробуем рассмотреть поколения редакторов чуть дальше.
Клиенты, если им дай идеальный инструмент редактирования, все равно не смогут им пользоваться. Их поделки будут выглядеть примерно вот так:
Это и правда и не правда, так как веб он просто сложный для гуманитариев, а с другой стороны где-то есть идеальный UI/UX который позволит нажатием кнопки — сделать сайт, сделать, то, что вы хотите.
Но порой это невозможно и приходиться искать золотую середину.
Визуальный HTML редактор
Первоначальная задача была такой — сделай так же как в CKEditor, что бы можно было двигать элементы(текст, картинки) и как-то их редактировать/форматировать.
Так же одним из главных требований была возможность загрузки пользовательского HTML, для дальнейшей работы.
Начал я с того, что сделал элементы на странице draggable. То есть, пользователь мог их двигать. По началу все казалось хорошо, но чем больше различных заготовок я пробовал, тем хуже все выглядело. Дело в том, что пользовательский HTML, может быть совершенно любым и не предсказуемым. Это ведет и к проблемам элементарной задачи drag and drop, невозможность понять, что за элемент выбрал пользователь и даже банально иметь контроль над страницей. Это был хаос.
Grapesjs позволяет загружать свой HTML. Текст обернутый в очевидные теги он видит, а текст который находится просто в body
, он никак не чувствует:
Ограничения
Дать возможность загружать свой HTML, и редактировать его, это конечно смелый ход. С точки зрения юзера это большая свобода. Но вот есть чудесный пример, того как обстоят дела в вебе — A Blue Box
Я уверен, там даже не все варианты реализации. Дело в том, что одни и те же по семантике вещи могут быть реализованы совершенно по разному, с использованием совершенно разных подходов. А представьте, на странице, что-то посложней чем синий квадрат. Та же картинка может быть добавлена на страницу с помощью тега или быть задана с помощью background. Кнопка может быть , а может быть сверстана или той же картинкой. И как понять, какой инструмент редактирования показывать?
Никак.
Ограничение 00
Выбор следующий — запретить ту или иную верстку, сделать стайлгайд или же запретить загружать пользовательский HTML. Разумеется мы выбрали второе :)
Ограничение 01
Раз уж мы запретили загружать пользовательский HTML, то нам нужны свои компоненты/элементы, и это первый шаг к настоящему редактору.
Ограничение 02
Так как пользователь будет работать с рядом наших элементов, мы должны запретить редактору работать с не "нашими элементами" и научить работать с ними.
Рабочее окружение
Не самая интересная тема, но важная. Мне очень нравится интерфейс фотошопа. Есть ряд панелей, и рабочая область. Рабочей областью вы можете манипулировать как хотите, она по центру и в зависимости от размеров, вы можете скролится как по горизонтали, так и по вертикали.
Так же немало меня задел своим дизайном Google Web Designer:
Кстате, это своего рода редактор от Google, кто-то использовал его?
Самое сложное, это разбить страницу таким образом. Я не эксперт CSS, и в этот раз мне это далось намного легче, но тогда это были танцы с табличной версткой. Прежде чем мы пойдем дальше, давайте посмотрим какие вообще лаяуты рабочей области есть.
Фотошоп
LPGenerator
Мое творение
Grapesjs
По факту мы уже обсудили этот лаяут выше. Слева часто используемые инструменты, справа параметры, сверху что-то дополнительное, снизу информационная полоса. Самый очевидный вариант, но в тоже время перегруженный для пользователя. Он боится всех этих кнопочек, будто он в кабине самолета и ему нужно сделать экстренную посадку.
Так перегружен, что надо все прятать
Wix
За 5ть лет, дизайн викса мало поменялся. Зайдя в LPGenerator, вы сразу понимаете, что можно перетянуть какой-то элемент на страницу, видите настройки итд. В случае с Wix, все изначально спрятано. Это хороший подход. Дело в том, что пользователю не нужно видеть, то, что ему скорей всего и не понадобится на данный момент. Но тут нужно соблюсти баланс. У Wix.com уж слишком много элементов которые вы можете добавить на страницу и по этому они сделали целую галлерею элементов. Но конечно очевидные вещи как текст или кнопка, можно было бы и вынести на одну из панелей для быстрого доступа.
При клике на элемент, над ним появляется дополнительный функционал, который относится к элементу.
Реализовать такую штуку тот еще гемор, об этом мы поговорим чуть позже.
Минималист
Squarespace
Минимальный дизайн, рабочая область занимает большую часть экрана, из интерфейса самый необходимый минимум. Набор инструментов очень странный. Например вот так редактируется первый экран с кнопкой:
Кнопка тут представлена текстом.
Сам по себе режим редактирования очень похож на предпросмотр и только при двойном клике на некоторые экраны, получается перейти в режим редактирования.
При этом редактор нас как бы перемещает в другую часть приложения.
Я есть грут
Tilda
Редактируя сайт, вы словно находитесь на нем. Очень похоже на WordPress и его панель сверху с функционалом редактирования/публикаций и прочего. Но при этом, функционал не такой стопорный как у Squarespace и тот же текст поддается редактированию куда проще.
Что бы, что-то добавить, необходимо кликнуть на +. Перенести кнопку в любое удобное для вас место, так просто не выйдет. Все ограниченно.
Следствием этого являются такие вот галлереи с кучей различных вариантов одного и того же элемента.
Как вы уже могли понять я фанат фотошопного варианта. Когда-то я мучился с табличной версткой, но сейчас 2018 и в целях познавательных, мы можем наплевать на поддержку и реализовать лаяут на CSS Grids.
Если кто не знаком с CSS гридами, то это один из последних способов верстать лаяут страниц. Если Flexbox предпочтительно использовать для меню, формочек и прочего набора элементов, то CSS Grids, отлично себя чувствуют именно в глобальных вещах.
Это не туториал по CSS Grids, по этому я объясню по ходу дела в двух словах, но без мега деталей.
И так наша задача сделать левую и правую панель, а по середине рабочую область где будет отображаться контент веб сайта.
Работать с гридами немного проще в FireFox, это странно, но хром в этом деле отстает. В одной из прошлых статей я уже говорил об этом
Как выглядит инспекция гридов в хроме:
В то же время в мазиле:
Есть мини представление грида, показываются семантические лейблы колонок/строк
По этому если хотите побольше информативности, используйте пока FireFox. Странно я себя чувствую рекомендуя этот браузер. Время Bugzilla уже прошли.
Давайте начнем с простой разметки
<div class="main">
<div class="left"></div>
<div class="content">
<div class="site"></div>
</div>
<div class="right"></div>
</div>
.left — левая панель
.content — середина, где будет отображаться сайт
.right — правая панель
Пока это мало на, что похоже. Мы хотим, что бы наш интерфейс был выстой равной Viewport. Сделать это можно с помощью height: 100%
, но есть и вьюпорт юниты vh
и vw
. Их мы и будем использовать.
.main {
height: 100vw;
}
По умолчанию дочерние элементы превращаются в строки. Это своего рода оси flexbox.
Нам необходимо задать колонки, сделать это можно следующим способом:
.main {
display: grid;
/* grid-template-columns: 1fr 2fr 1fr; */
grid-template-columns: minmax(200px, .4fr) minmax(560px, 2fr) minmax(200px, .4fr);
}
Методом проб и ошибок я пришел к достаточно сложному темлейту. Функция minmax
, позволяет задавать максимальные и минимальные размеры. fr
это универсальная единица, которая избавляет нас от подсчетов размеров в пикселях или процентах. Дело в том, что такие свойства как grid-gap
влияют на размер подобно border
. fr
призван избавить нас от этой боли.
Немного подкрасим и уберем margin
на body
, он нам немного мешает:
body {
margin: 0px;
}
.left, .content, .right {
box-sizing: border-box;
border: solid 1px;
}
Супер. Но это была самая простая часть. Теперь нам необходимо, сделать рабочую область.
.site
это наш мнимый сайт. Давайте сделаем его в стандартные 960px и подсветим.
.site {
height: 2000px;
width: 960px;
background: rebeccapurple;
}
Если это дело сжать, то все еще веселей:
Но
.content {
overflow: scroll;
}
Решает проблему. Остается только эстетическая часть. Отступы.
Сделать это можно с помощью margin
:
margin: auto;
margin-top: 20px;
margin-bottom: 20px;
20px
мало или много решать уже вам. Но тут есть другая проблема. Если экран слишком маленький, отступов слева и справа не будет. Так как maring: auto
будет тесно. Первое, что приходит на ум это задать margin-left
и margin-right
, но тут другой прикол. Отступ будет только у левой части. Если проскролить в право, то там его не будет. Это такая вот особенность. Не вдаваясь в подробности, пофиксить это можно с помощью display: inline-block
. Ага. Но при этом у нас поломается margin: auto
который очень хорошо себя ведет при больших размерах экрана. Следующее самое очевидное решение это использовать Media Queries. На больших экранах display: block + margin: auto
, на мелких display: inline-block; margin-left/right: 20px
.
.site{
height: 2000px;
width: 960px;
margin: auto;
margin-top: 20px;
margin-bottom: 20px;
background: rebeccapurple;
}
@media(max-width: 1350px) {
.site {
display: inline-block;
margin: 20px;
}
}
Вот как-то так. Если у кого-то есть идеи получше, добро пожаловать в комментарии.
Отображение сайта
Лаяут готов, теперь необходимо отобразить сайт, который пользователь будет редактировать. Это очень важная вещь. От нее зависит ряд решений. Отобразить сайт на редактирования по факту, можно двумя способами. В document
, где находится интерфейс вашего редактора или же в iframe
. Оба варианта обладают положительными и не очень положительными свойствами.
Тот же document
Это проще, как ментально так и технически. Но гораздо проблематичней в будущем. Дело в том, что если вы добавите, а вы добавите, возможность вставки пользовательского CSS и JS, ваше редактор будет взорван спустя несколько минут.
Пользователи которым не объяснили как пользоваться кружкой:
Инкапсулированный CSS пока еще не реальность как и 5 лет назад. Варианты есть, но они либо слишком тяжелые, либо полностью не защищают. Когда пользователь перекосит весь интерфейс и случайно удалит всю свою работу, винить в этом будут вас. Забегая немного вперед, когда вы решите добавить ряд своих готовых шаблонов, ваше же разработчики будут с этим мучаться. Как бы не переопределить какой-то стиль. Вот если посмотреть на Tilda, у них с этим беда.
Возьмем например первый попавшийся под руку UI этого редактора. Кнопочки форматирования текста:
Вы видите эти стили? !important
на цвете? Эти стили вовсе не нужны, если их отключить то нечего не поменяется. В любом случае, переопределить их очень просто вставкой пользовательского HTML в страницу сайта:
Признаюсь, я вставил этот код в ручную. Но я нашел халявный 2х недельный период с возможностью вставки пользовательского HTML и CSS, и достиг ровно того же эффекта.
Как так можно? Вот представьте, у вас купили услуг разработать сайт в Tilda за n тысяч долларов, и вы захотели задать собственный стиль ссылкам. Все, сайт редактировать пользователь уже не сможет. Ну с бордерами я утрирую, но вы можете представить любой другой стиль, не говоря уже о пользовательском JS. Причем коллизия, распространяется на даже какой-то вложенный интерфейс, хотя это очевидно.
Не дать пользователям вставлять собственный код, будет решением этой проблемы. Но в реальном мире это практически невозможно. Не будете же вы делать кастомные интеграции под каждого пользователя. Это возможно с жирными клиентами. Но не когда у вас 90% функционала стоит 5-10 баксов.
Iframe
Практически полностью инкапсулировать страницу, нам поможет iframe
. Наверное это единственное где я использовал iframe
так долго. Мы можем достучаться до контента iframe
и даже наоборот, если они на одном домене. По этому у нас все под контролем. Но все такие инкапсуляция нас немного озадачивает. Например выделение элементов, которое мы обсудим чуть позже. Выделять его элементами на уровне интерфейса позиционируя над iframe
, или в самом iframe
?
Разумеется проще это будет делать в самом iframe
. Дело в том, что при скролинги контента, вам не придется обновлять позицию выделения/думать о хитрой верстке (хотя лучше так), и дополнительного интерфейса. Но вычисления того перекрывается ли ваш интерфейс, интерфейсом редактора будут проще если вы будете рендерить подобные вещи над iframe
в одном document
. По этому например Grapesjs комбинирует два этих подхода:
Выделение происходит элементами в iframe
, а дополнительный функционал, чувствительный к перекрытию, рендерится поверх iframe
.
Но для поддержки ваших стилей, вам придется импортить в iframe
все необходимое. Тут же вам придется приделать экран загрузки, что бы пользователь не видел сайт/интерфейс не накрашенным.
Wix <head>
Следовательно вам стоит понимать, что делать это необходимо только при редактировании. И не забывайте про пользовательский стиль или у вас опять будут проблемы.
Grapesjs класс который я "случайно" обновил бордером:
Grapesjs добавляет класс gjs-comp-selected
при выделении, который при инспекции хорошо виден и уж поверьте это будет первое, что пользователь скопирует для кастомизации.
Выделение элементов
Мы немножко забегаем наперед, ведь мы пока не поняли как научить редактор видеть элементы на странице, но это нам не помешает. Прежде чем углубится в более технологически сложную тему, давайте посмотрим, насколько же технологически сложно, сделать выделение элементов на странице :) Как вы думаете, это сложно? Если вы очень самоуверенны, то вот как с этим справляются все выше упомянутые редакторы на примере текста:
Мой редактор
Tilda
Grapesjs
Google Web Designer
Wix
А при редактировании
Часть текст не видно вообще. Ну ладно. С точки зрения фронт-енд разработчика это фича.
Дело в том, что редакторы выделяют по большей части DOM модель. Но вот посудите сами, вы как пользователь, далекий от разработки сайтов, думаете это ок? Нет это не ок. Контент больше того, что вам показывает редактор. В случае с некоторыми, есть вероятность потерять возможность выбрать элемент. Это нормально для браузера выделять таким образом для разработчиков, это верно по спецификации.
Что видит пользователь (синий, текст сделал прозрачным, для очевидности проблемы), что выделяется редакторами, браузерами (красный):
Chrome
FireFox
При выделении текста в FF, хром так не умеет.
Так почему такой косяк во всех редакторах? По моему опыт скажу, что это лапша на ушах продукт менеджеров и/или тестировщиков. Все кто работает над редактором, как правило вряд ли будут ставить line-height: 1px
, или пробовать играться с различными стилями и размерами текста. У них замыленные глаза и такие косяки на первый взгляд элементарные остаются в слепой зоне. А клиенты поверьте, сталкиваются с ними на протяжении первых минут.
С точки зрения UI/UX, это может быть технически не понятная вещь, и такие детали реализации как правило остаются на усмотрение разработчика.
Так можно ли сделать выделение юзерфрендли? Есть идеи, как выделить элемент?
В выделение необходимо включать такие вещи как margin, padding, border. Модель размеров не простая:
Из моего сандбокса:
Пожалуй самая полезная информация, которую я извлек из msdn:
Но и тут нет ответа на наш вопрос.
Давайте попробуем то, что нам предоставляет DOM в первую очередь. Имеем такую вот верстку:
font-size: 54px;
line-height: 0px; /* сделайте сколько хотите, суть не в этом */
border: 10px solid;
padding: 10px;
Размер видимого контента 62px, браузер нам показывает 40px. Как получить 62px?
getBoundingClientRect возвращает нам — 40px
clientHeight — 20px
scrollHeight — 41px
Можно очень долго играться с математикой, но решения используя такой вот подход, я не нашел.
Но как и ~5ть лет назад, я знал, что разгадка в выделении текста. Ну вот посмотрите, если выделить текст, то выделение точь в точь такое как нам надо:
Не удачный пример, но по высоте с которой у нас основная проблема — все круто. Но как же выделять так, как это делает какая-то системная магия?
Спустя столько лет я погуглил (научился!), к сожаления как я гуглил и линка утеряна, но вот решение:
const range = document.createRange();
let rangeRect;
range.selectNode(elm);
rangeRect = range.getBoundingClientRect();
Создаем range
, выделяем ноду, и берем от range
ClientRect
. Вуаля! Причем выделение работает на все. Можно выделять целые куски страницы (https://developer.mozilla.org/en-US/docs/Web/API/Range).
И по высоте и по ширине, идеальное выделение.
(Выделением в редакторе будет синий бордер, никакого фона. Выделил текст, что бы показать системное выделение.)
Другая менее важная часть выделение, то это как его показывать? Есть идеи? Способов куча. Начиная от бордера, заканчивая дополнительным плавающим элементом.
В первую очередь решить делать это в iframe
или над iframe
, о плюсах и минусах мы говорили выше.
Бордер не подходит, так как он добавляет размера элементу и, что если пользователь задаст свой бордер элементу? То же самое и outline
, Grapesjs грешит тем, что делает выделение через него.
В моем случае, я использовал ::before, но это тоже не круто. Самый надежный способ это сделать либо 4 полоски, и позиционировать их по краям (которые в дальнейшем и будут элементами резайсинга элемента), или же блоком поверх. Выбирать лучше из этих двух вариантов, максимум комбинируя их. Слишком много позиционирования на странице не есть помощью в оптимизации и без того перегруженного UI. Так, что чем больше многофункциональных вещей которые вы позиционируете, тем лучше.
Это не все
Мы только, что реализовали выделение. Но это выделение на уровне "навел и увидел". С ним еще связаны две проблемы которые я оставлю вам на подумать самим. Они не такие противные.
- Размеры элемента меняются при редактировании. Вам необходимо следить за этим и обновлять выделение. Именно этим болеет мой редактор и Google Web Designer. Не всегда, но частенько.
- Что бы сделать лейбл/выделение над элементом, давая понять пользователю, что это за элемент, вам необходимо самим знать, что это. По этому элемент должен быть проинициализирован в вашем редакторе. От этого зависит перфоманс редактора. На странице может быть не одна сотня элементов, и просто взять и проинициализировать все при старте — не всегда лучшая идея. Но в случае с моим редактором, вроде ок. ха-ха. Инициализацию делать можно по клику, по ховеру, по теории частоты использования.
Редактирование
Раз уж мы заговорили о выделении на примере текста, давайте быстренько пройдемся по его редактированию.
По факту текст это вторая важная часть в редакторе после картиночек и выделения :)
Ну вот как сделать так, что бы когда я выделил часть текст и нажал на какой-то функционал форматирования, я отформатировал именно выделенный текст?
Если вы сын маминой подруги и гениальный разработчик, то вы наверняка скажете — обернуть выделенный текст. О и вы будете правы, примерно как если бы Илон ответил, что построить ракету === нанять сотню инженеров.
Оборачивать выделенный текст не так просто. В итоге там будет куча тегов, и как их потом мержить при очередном выделении и форматировании большой вопрос. Я долго изучал тот же CKEditor и обнаружил чудо:
contentEditable
(https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/contentEditable)
Это блин редактор в браузере. Он не просто дает нам возможность писать текст, но и форматировать его.
Статейки по теме:
- https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Editable_content/Rich-Text_Editing_in_Mozilla
- https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Editable_content
Этот API позволяет выполнять команды форматирования над выделенным текстом:
bool = document.execCommand(aCommandName, aShowDefaultUI, aValueArgument)
Изменение цвета, отступы, вставка линки, кастомные стили и многое другое можно делать с помощью этого инструмента.
Есть небольшие кросс браузерные причуды. Сейчас их все меньше и меньше, но тогда, хром и мазила в некоторых случая выдавала разные обертки под изменение цвета итп. Это разумеется малость мешало, если пользователь редактировал текст в разных браузера. Но это решалось ручным костылем. Просто немного правился тег, после выполнение команды.
contentEditable
можно использовать не только для крутого редактирования текста. В некоторых браузерах, есть функционал по работе с картинками и таблицами. Это по началу мешало. Но когда я делал собственные таблицы, очень хотелось это использовать. Но оно все было очень забагованое и не стабильное.
В то время, contentEditable
был очень известной болью, благодаря контрибьютерам CKEditor, CodeMirror, и прочих. Они активно плакали в своих блогах о разницах в реализации. В тот момент я столкнулся с кросс браузерными проблемами, по большей части именно из за этой части функционала.
Многие редакторы для сайтов делали хитрый ход, который я имел слабость тоже попробовать. Редактировать текст не прямо на странице, а в отдельном месте и с использованием уже готового редактора, того же CKEditor. Но поверьте, это не просто. Дело в том, что перед вами встанет проблема, которую решить ой как не просто. Вам необходимо суметь отобразить текст в другом iframe
точно так же как он отображается на вашей странице. Вам нужно будет собрать все стили, предоставить тот же фон. А фон важен, пользователь хочет видеть как будет смотреться его текст до окончания редактирования/форматирования. Тут я не сдался с contentEditable
и выбросил все наработки в этом направлении. Уж лучше переболеть contentEditable
. Вот Wix, 2018 год. У них вроде как свой редактор, но текст редактируется в прозрачном iframe
. Кстате это был один из вариантов предоставить фон :) Вроде все ок, но текст обрезается, а порой теряются стили. Открыл текст на редактирования, затем закрыл, и он потерял цвет который у него был до этого. Я встретил такое в одном из дефолтных шаблонов. Такие болезни присущи всем редакторам, которые решили пойти таким путем. Всех вариантов верстки просто не видно на момент разработки, и даже задолго после.
До редактирования:
Просто кликнул в сторону и вышел из редактирования:
То есть есть момент с получением стиля обратно.
Ресайз элементов
Пожалуй единственное, что я хочу сказать на этот счет, так это, то, что если вы тяните за левую/верхнюю часть элемента, при увеличении высоты/ширины вам необходимо обновлять его позицию. Так как координаты 0,0
на странице, находятся в верхнем левом углу:
(svg/dom element)
Работа с элементами, VC++, MVC и все такое.
До того как стать веб разработчиком или же точней до того, как веб стал актуальной сферой роста для специалиста, я игрался с Visual C++, C#, Delphi и прочим. И как-то так получилось, что со знанием Backbone и вопросом, а как же мы будем работать с элементами на странице, я ни на секунду не задумываясь привел аналогию из вышеперечисленных языков/технологий/фреймворков.
У кнопок есть представление, они как-то отображаются, есть модель с параметрами, такими как текст
, цвет
и тд. Так же у элементов есть контроллер который обрабатывает события:
Не знаю, настоящий ли это листинг, но вроде выглядит убедительно.
Этот подход, используется очень давно и может у него нет CSS, он очень гибок и удобен в разработке приложений да еще и с визуальным редактором.
Backbone, по сути своей маленькая библиотека дающая нам представления (View), Модели (Model) и коллекции (Collection). Так есть своего рода контроллер (Router). В качестве контроллера как правило выступает View. MV*, MVV, если вы помните такое.
Так вот, очень удачно получилось использовать Backbone, для реализации схожего подхода. В первую очередь я реализовал View типичного элемента. У него была модель с рядом CSS свойств. Поскольку элементы как правило разные, я наследовался от этого элемента и реализовал View под каждый необходимый мне элемент.
При клике на какой либо из элементов, редактор пытался понять, есть ли уже View у элемента. У многих элементов View и Model создавалась при загрузке страницы. Как правило это было у кастомных больших компонентов, которые редактор не мог понять без помощи. Хотя для таких элементов был специальный атрибут и при ховере с выделением, пользователь видел разумный текст и без полноценной View на элементе. Если нет, то была попытка найти подходящий класс. Если этого не получалось, смотрелся .parent
от элемента по которому сделали клик.
Когда View находилась, происходила инициализация и запись View и Model, в data-
атрибут для быстрого доступа. Соответственно сразу после клика, Model передавалась во все инструменты которыми могло быть произведено редактирование элемента и происходил их апдейт, что бы отобразить текущее состояние элемента. Учитывая, что Backbone, не так умен как React, в обновлении DOM, можете представить какой у этого решения перфоманс. Но на самом деле, еще тогда никаких лагов не было. Инструментов бывало штук 50-100 на один элемент и как-то все ок. Плюс конечно, же не происходил перерендер всего инструмента, а четко ручное обновление HTML. Так, что кода больше, но перфоманс ререндеринга напрямую зависел от меня. И никакие ваши virtual dom, не обгонят по скорости ванильный JS. Это не удобно в разработке — факт. НО!, в таких размеров проектах это обусловленно. Иначе будет как Wix, мерцать как новогодняя елка при скроле и ховере на совершенно разные части приложения:
При скроле:
При ховере на левые элементы топ меню:
При работе с инструментами, из меню на странице:
Это еще ладно скриншоты, включите отображение перерисовки и поработайте с ним. Это фантастиш. Но зато React.
В моем редакторе CSS хранится инлайново, так делают многие редакторы до сих пор. Но это плохой подход который ухудшаеть перфоманс, чтение HTML, и ведет к ряду проблем с дальнейшей работой. Но признаться, это очень простой в реализации подход.
Найболее правильным решением, является создание уникального класса под каждый редактируемый элемент и запись в <style>
тег его стилей. То как это реализовать и организовать уже малость выходить за рамки статьй, быть может в другой раз. Там не паханое поле вариантов и возможностей.
Хранение данных
Хранить данные можно в data-
атрибутах, html и на сервере. Я говорю о данных которые !== стилям. Например подписная форма в html может хранить данные об акции. Если же данные лучше так просто не показывать, то у формы может быть ID, по которой сервер сам понимает, к какой рассылке она относится и отправляет данные туда куда нужно.
Редактирование/Предпросмотр/Просмотр
Необходимо различать эти состояния. Интерфейс, модели не должны существовать в предпросмотре и просмотре. Не стоит даже трафик на них тратить. Это просто, то, о чем стоит помнить.
Так же подумайте о различных анимациях и тех же ховер эффектах на кнопках. В момент редактирования они жутко раздражают. Они должны быть отключены.
React? Polymer? Vue(родился позже)? Angular (шутка!)?
Второй и 3тий вариант работы с элементами на странице который я не успел реализовать на том проекте, уже когда стали популярными React и только вышел Polymer, были React компоненты и Web Components.
Веб компоненты разве, что упростили бы работу с данными (Polymer), и ими было бы легче манипулировать. Сами веб компоненты очень соблазнительны в данном проекте. Их инкапсулированная натура дает много преимуществ. Но тогда был только Shady DOM о котором я давненько писал (https://habr.com/post/259187/), так, что эта идея отпала. По факту толку было бы мало, да еще и тяжелей все эти полифилы были.
React
Честно сказать понятия не имею :)
Но давайте подумаем. Отрендерить React в ифрейм и дальше с ним работать проблем вообще не составляет:
const rootElement = document.getElementById("root").contentDocument.body;
ReactDOM.render(<App />, rootElement);
Сохранять вы можете просто HTML, за счет ReactDOM.hydrate(element, container[, callback]), словно на фронте после сервер-сайд рендеринга подключать к DOM React. Все события и прочее будут работать. Это круто. Но конечно, по факту все, что на странице будет иметь свое представление в Virtual DOM хотите вы этого или нет. Это бьет по перфомансу.
Организовать работу с элементами и инструментами, можно по средствам стандартных подходов. Redux, Flux, Mobx в вашем распоряжении. Но тут получается так же каждый элемент будет подконекчен к стору Redux, что не есть хорошо.
Я в свое время боялся добавлять Marrionett к проекту. Это небольшая оболочка для Backbone. А тут такие траты. Но это не известно.
Видится только одна проблема, которая может повлиять на многое. Как добавлять элементы на страницу? Допустим мы перетаскиваем новые элементы написанные на React, на страницу. Как их правильно встроить в DOM? Делать у каждого элемента массив дочерних элементов? Делать ReactDOM.hydrate
после изменения HTML? Так делать можно, учитывая нашу следующую тему которую необходимо обсудить напоследок.
Responsive and Adaptive
В то время еще не было таких понятий. Ну может я вру, они уже применялись, но не были так хорошо известны каждому. Тогда еще правила верстка на основе 960px. То есть сайт помещался на любом экране, а если места было больше, то там был какой-то продуманный фон. С ростом популярности смартфонов ситуация менялась.
В моем редакторе, вы можете явно видеть пунктирные полоски, которые обозначают границы сайта. Выход за их рамки, это ваш риск и страх.
По факту это элемент или элементы, с позиционированием relative
. Все элементы добавляемые на страницу, позиционируются относительного этого. Никакой адаптивности и эластичности дизайна. По факту ВСЕ позиционируется абсолютно. Пользователь может двигать элементы безгранично куда хочет.
(зачетный интерфейс на разных языках)
Это плюс для него, но и большой минус. Он может натворить дел, когда сайт превратиться не в пойми, что очень быстро, сайт очень хрупкий при таком подходе. И у него нет нормальной верстки.
Ограничение 03
Предоставлять готовые куски, экраны. У лендингов есть понятие экраны. Мы назвали это секцией.
Это все тот же 960px, но уже более структурированный. Пользователь не может взять и случайно потянуть пол страницы хлама.
Далеко не ходя, скажу, что адаптивности и отзывчивости тогда не было в редакторах. Wix, и на данный момент решает это весьма стремным способом. Возможно я натыкаюсь на старые шаблоны, но так или иначе они в самом верху и я как и любой другой пробую именно их.
Решено это у них двумя версиями шаблона. Да-да. Если вы, что меняете на большой версии, например просто тот же цвет текста, переключаюсь или сжимая браузер до мобильной — этих изменений не будет.
Интересно пообщаться с первой линией тех. поддержки на этот счет :)
12 друзей верстки, флекс, гриды.
12 колоночная система, знаете? Поклацайте https://grapesjs.com/, и вы сразу поймете о чем я.
Это ограничение, которое несколько ограничивает пользователя в свободе, но позволяет ему творить крутые сайты если он освоит этот инструмент. Пользователь вынужден использовать блоки которые предоставляет ему редактор, помещать элементы в только определенные места и так далее. У пользователя меньше шансов застрелиться. Для нас как для разработчиков это праздник.
Заготовки, шаблончики и платный сервис/помощь, мутные люди вокруг
Но как ни крути сделать идеальный инструмент сложно. Пользователям все равно до лампочки на все ваши элементы верстки и как они должны двигать элементы. Создание сайта это творческая работа и как правило, тем кому не повезло работать в вашем редакторе думают совершенно не о том как бы четенько сделать сайт. По этому есть заготовки и шаблоны. И чем их больше, тем хуже у вас дела с UI/UX.
Пользователи хоть как-то могут получить уже готовый сайт за пару кликов, и меняя только лишь текст, цвет, картиночки — получить желаемый результат.
Отдельный разговор, это прослойка бизнеса, которая делает сайты, на таких инструментах. Это например как настройка рекламы в гугл. Вроде просто, куча туториалов, а сотни тысяч делают на этом третьй фирмы.
Итак
Я лишь пожелаю вам удачи. Cделать редактор на самом деле не рокет сайнс. Главное это UI/UX. Работа с текстом не такая уж и простая задача, я даже не упоминал undo/redo функционал, его сделать достаточно сложно. Ну или просто, если вы будете дампить HTML всей страницы в localStorage… Нет, конечно же я так не делал.
Рокет сайнс научить пользователей им пользоваться. Мой редактор работает от силы на двух проектах, его используют разные аудитории, но насколько им это удобно и соответствует результат их ожиданиям — неизвестно. Если у вас есть вопросы, буду рад ответить в коментариях, личке.
P.S. букв много и я не поэт. Я буду фиксить, все косяки и тавтологии которые найду. Но не стесняйтесь писать в личку, спасибо :)
Автор: auine