На современных технических веб-сайтах есть множество редакторов кода. Большинство редакторов кода делают почти одно и то же - выделение кода, синтаксический анализ языка, автоматический отступ и т. Д. Некоторые даже более причудливые предоставляют вам среду разработки облачного редактора с более близкими к нативным функциям, чтобы облегчить жизнь разработчикам (или Сильнее?). Я не особо много думал при использовании любого из них, до недавнего времени мне нужно было написать его для компонента встроенного конструктора выражений в нашем проекте. Результаты и мысли последних двух недель заставили меня переосмыслить, насколько плохими и хорошими могут быть наши сегодняшние веб-технологии.

Короче говоря, использование contenteditable для редактора кода звучит интересно, но дьявол кроется в деталях - результат множества реверсивных «contenteditable» реализаций в различных браузерах может сделать циклы разработки и отладки очень болезненными. Вы почти наверняка пожалеете об использовании этого предмета черной магии после всех последующих причуд.

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

Именно тогда мне стало интересно "contenteditable". Кажется, это короткий путь, чтобы быстро заставить редактор работать. Недавнее популярное репозиторий github под названием pell (я обнаружил его на самом деле после того, как начал задавать себе вопросы об использовании contenteditable), показывает, насколько просто можно было бы создать веб-редактор. Похоже, я могу редактировать HTML-код элемента по своему усмотрению, что позволяет мне добавлять подсветку синтаксиса после анализа выражений.

Первая проблема - определить «чистый HTML». «Contenteditable» был впервые представлен Microsoft в их браузере IE (IE 5.5). Поскольку не существовало четких стандартов того, как должен выглядеть HTML внутри «контентного» редактора, разные браузеры начали перепроектировать IE и поддерживать его по-своему. Например, IE использует ‘‹p›’ для абзацев или строк, тогда как Chrome использует ‘‹div›’ с ‘‹br›’ в нем. Чтобы сделать его более последовательным, firefox присоединился, используя только «‹br›» рядом с текстовыми узлами. Чтобы наш редактор постоянно отображал отступы и выделения, я хочу, чтобы все строки были «‹div›», а все новые строки - ‘‹br›».

После определения чистого HTML второй проблемой, с которой я столкнулся, было чтение фактического текстового содержимого из этого небольшого редактора, который я создал. Поскольку редактор должен поддерживать несколько строк и должен анализировать выражения и отображать содержимое HTML, ни textContent, ни innerText работать не будут. По дизайну свойство textContent должно возвращать текст без новых строк, а innerText является нестандартным, а также несовместимым при синтаксическом анализе как ‘‹br›’, так и ‘‹div›’. Прочитав несколько сообщений о переполнении стека, я закончил тем, что написал функцию обхода узла, чтобы исследовать каждый узел и самому читать текстовые узлы и новые строки. Это звучит просто и на самом деле довольно прямолинейно, когда он работает в Chrome, но когда я переключаюсь на firefox, все перестает работать - firefox не любит факт ‹div› внутри его contenteditable элемента.

Мне потребовалось на удивление много времени, чтобы исправить эту проблему, как и все остальные. Мне нужно использовать «‹br›», чтобы заставить работать клавиши со стрелками вверх / вниз в Firefox. Мне не удалось нажать Backspace в пустой строке, так как Chrome удалит элемент ‘‹br›’ предыдущей строки. Когда я хотел скопировать и вставить, результат будет другим, в зависимости от того, куда я вставляю свой контент, поскольку в разных браузерах он получился с другим «грязным HTML». Я могу продолжать перечислять их, но чтобы это не было похоже на простую жалобу здесь, я бы сказал, что это просто слишком много возможных крайних случаев из-за различных реализаций «contenteditable» в разных браузерах.

После того, как все заработало, я понял, что встроенные команды клавиатуры (например, ctrl-b / cmd-b для полужирного текста) - это то, что мне может не понадобиться в моем редакторе. Как упоминалось в этом ответе на переполнение стека, я мог бы просто остановить его с помощью JS, но после прочтения сообщения, в котором говорится о том, как contenteditable начиналось и развивалось, я стал больше беспокоиться о своем подходе.

Первоначальной целью атрибута contenteditable было редактирование форматированного текста. И работает это через API «document.execCommand». Благодаря более чем 10-летней поддержке различных браузеров без четкого стандарта фактические подробности того, как каждый браузер понимает команду и как он отображает контент, так глубоко скрыты в коде. Последние две недели я пытался испортить рендеринг и усвоил это на собственном горьком опыте.

Однако использование contenteditable в текстовом редакторе, подобном тому, который вы читаете - средний редактор, согласуется с первоначальной целью этого атрибута и позволяет выполнить работу. Даже несмотря на успех среднего редактора, вот пара постов, критикующих "contenteditable" за нестандартные и испорченные реализации.