Как сделать простую подсветку кода в HTML средствами JS


Если вам потребуется осуществить подсветку синтаксиса кода на странице, например HTML или PHP, то это можно несложно реализовать на Javascript при помощи регулярных выражений (Regular Expression).
Примечание: имеется ввиду что спецсимволы вроде треугольных скобок, как при обозначении HTML-тегов, будут заменены HTML-сущностями, т.е < будет заменено на &lt; ну итд.
Итак, необходимый нам код мы будем размещать в блоках <pre class="code"> тут будет HTML/JS/PHP и любой другой код </pre>, а в после этих блоков или же на событие window.onload мы подключим нашу будущую функцию js_simple_code_highlighter(), для обработки и раскраски кода.
Если вы не знакомы с регулярными выраженями в Javascript, придется этот вопрос изучить самостоятельно.

Для нашего будущего «хайлайтера» мы поставим следующие задачи:

  • отображение номеров строк

  • подсветка некоторых ключевых слов, указанных в скрипте (например true, char, итд.)

  • подсветка HTML-тегов

  • подсветка комментариев в коде

  • подсветка имен переменных в PHP


Основная идея: получить все необходимые блоки с кодом на странице, после чего последовательно обработать их содержимое в цикле, применяя по ходу каждое правило замены регулярного выражения, и выводить новый результат за место старого в теже блоки с кодом.
Нужные куски текста в коде мы будем «оборачивать» в HTML-теги с определенными названиями классов (например: <font class="tagname">полученная строка</font>), чтобы в дальнейшем при помощи CSS сделать непосредственную подсветку кода.
Немного «пошаманив» над яваскриптом, получилась вот такая функция и CSS-стили к ней:

<script type="text/javascript">
function js_simple_code_highlighter()
{
// получаем список всех элементов (тегов) с содержимым кода
code = document.getElementsByTagName('pre');

// определяем список ключевых слов для подсветки
keywords = 'abstract boolean break byte case catch char const continue debugger delete do else enum export extends false final finally for function goto if implements in instanceof int interface long native new null package private protected public return short static super switch synchronized throw throws transient true try typeof var void volatile while with and or xor __FILE__ __LINE__ array as break case cfunction const continue declare die do else elseif empty enddeclare endfor endforeach endif endswitch endwhile extends for foreach function include include_once global if new old_function return static switch use require require_once var while __FUNCTION__ __CLASS__ __METHOD__ abstract interface public implements extends private protected throw define';

// цикл обработки полученных элементов
for(i = 0; i < code.length; i++)
{
if( code[i].className == 'code' )
{
data = code[i].innerHTML;

data = data.replace(/(".*?"|'.*?')/g, '<font class="string">$1</font>'); // замена всех строк, обернутых в одинарные и двойные кавычки
data = data.replace(/(&lt;[a-z]+)(.*?)(\s*\/?&gt;)/gi, '<font class="tagname">$1</font>$2<font class="tagname">$3</font>'); // обработка HTML-тегов
data = data.replace(/(&lt;\/?[a-z]+&gt;)/gi, '<font class="tagname">$1</font>'); // обработка HTML-тегов (закрывающих)
data = data.replace(/(&lt;\?php|\?&gt;)/g, '<font class="tagname">$1</font>'); // обработка HTML-тега при подключении PHP-кода
data = data.replace(/(&lt;!--.*--&gt;|\/\*.*\*\/|\/\/.*)/g, '<font class="comment">$1</font>'); // обработка комментариев
data = data.replace(new RegExp('\\\x62('+ keywords.replace(/ /g,'|') +')\\\x62', 'g'), '<font class="keyword">$1</font>'); // обработка ключевых слов
data = data.replace(/(\$[a-z0-9_]+)/gi, '<font class="phpvar">$1</font>'); // подсветка PHP-переменных ($varname)

// обработка пробельных символов и номеров строк
data = data.replace(/ /g, '&nbsp;').replace(/\t/g, '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;').replace(/^(.*?)\r?(\n|$)/gm,'<div><span class="numline">$1</span></div>');
data = data.replace(/<font&nbsp;/gi, '<font ');

code[i].innerHTML = data;
}
}
}
</script>

<style type="text/css">
pre.code { position:relative; display:block; width:100%; overflow:auto; overflow-y:visible; background:#333; padding:5px 0; margin-bottom:-20px; counter-reset: line; max-height:450px; }
pre.code { font:14px/130% "Lucida Console", "Courier New", monospace; color:#888; }
pre.code div { padding:0 5px; counter-increment: line; //display:list-item; //list-style:decimal outside; //margin-left:30px; }
pre.code div:before { content:counter(line) ". "; font-style:normal; }
pre.code div span.numline { color:#ddd; }
pre.code .comment, pre .comment * { color:#999999 !important; font-style:italic !important; }
pre.code .string, pre .string * { color:#B6DDDE !important; }
pre.code .tagname { color:#91B7F7; }
pre.code .keyword { color:#CCFF99; }
pre.code .phpvar { color:#97DBF9; }
</style>


Смотреть результат работы скрипта

Если вас по каким-то причинам не устраивает такая простая подсветка кода, то могу посоветовать хайлайтер Ивана Салагаева, который имеет ряд настроек и может подсвечивать синтаксис различных языков программирования. Его можно скачать отсюда: http://softwaremaniacs.org/soft/highlight/

07.12.2009 / javascript, кодинг, css
Понравилась статья?
Подпишись на рассылку через RSS или следуй за нами в Twitter!
Похожие статьи:
Оценка статьи: проголосовало - 9, средняя оценка - 4,89
Комментарии к статье (6):
[ 28.12.2010 - 11:23 ] - VSVLAD
Всё хорошо, но нужно в стилях убрать // тогда и Opera 9.6 показывает номера строк. Если не сложно, можно ли сделать для VB подсветку...
[ 11.01.2011 - 18:52 ] - webmaestro
А нельзя ли темный шрифт на светлом фоне?
можно, это всё настраивается через CSS
[ 31.05.2011 - 04:00 ] - Vijit
Спасибо за код :) Использовал за основу. Почему в этой статье не используется описанный код подсветки?

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

Если слегка исправить шаблон "Обработка HTML-тегов" на "/(&lt;\/?[a-z]+)(.*?)(\s*\/?&gt;)/gi", то следующий за ним шаблон "Обработка HTML-тегов (закрывающих)" не нужен.

С подсветкой комментариев код не справляется:
1. коммент в нескольких строках не будет подсвечен.
2. если в комментарий попадет, например ключевое слово, то оно подсветится отдельно. Аналогично с тегами, строками в кавычках и т.д. Но это уже на любителя :)
да этот код я давно писал, многое там не очень правильно сделано)) в ближайшем будущем выложу другую версию хайлайтера в виде jQuery-плагина
[ 31.05.2011 - 08:14 ] - Vijit
Последнее замечание по комментариям не верно, прошу прощения. Не учел, что в CSS прописано "!important".
А для подсветки многострочных комментов предлагаю использовать шаблон типа: "/(\/\*(.|\s)*?\*\/)/g" (это для комментариев вида /*...*/)
[ 22.06.2011 - 16:05 ] - Тарас
Отличный код, главное что у него небольшой вес! Это для меня было важно. Уже себе поставил и доволен.
А нащет недоработок, тут уж извините, нужно и самому что-то сделать.
Большое спасибо автору!
[ 17.08.2011 - 00:12 ] - 123
эм... а у меня не экранируется в html сущности
Добавить комментарий
АНТИСПАМ: Выберите улыбающийся смайл: yep! nope! nope!
Оформление заявки
Файл>>