Подсветка текста в «TextArea»

    Эта статья покажет, как в современных браузерах можно сделать подсветку текста в поле редактирования (WYSIWYG).
    Пример можно посмотреть, в google.docs или на некоторых продвинутых форумах.

    Уверен, многим это будет интересно.

    Атрибут contentEditable и параметр designMode


    Именно contentEditable или designMode и делает для нас всю основную «грязную» работу.

    Если этот атрибут поставить в тег, то всё, что внутри тега, становится автоматически редактируемым.

    <html><head></head><body>
      <div contentEditable="true"><b>C</b>ontent<u>!!!</u></div>
    </body></html>
    работающий пример

    Этот атрибут не поддерживается сейчас некоторыми старыми браузерами (например, FireFox 2.0). Но есть более «глобальный» параметр страницы, который сейчас поддерживают все браузеры, который нам может помочь — designMode.

    По этому я разберу именно его.

    Особенности designMode


    Это на самом деле это свойство document, которое позволяет редактировать HTML-контент. Отличием от contentEditable является глобальность. То есть он привязан ко всему документу, а не к конкретному тегу.

    По этому, нам необходимо создать в нашей странице локальный document, который и будет полностью редактируемым. Для этого мы воспользуемся iframe.

    <html><head>
    <script language="javascript">
    	<!--
    /*Превращение iframe в область редактирования см. ниже (следующий JS код)*/
    	//-->
    </script></head>
    <body>
    	<iframe id="frm" ></iframe>
    </body></html>


    Чтобы превратить iframe в область редактирования нам необходимо:
    1) Получить указатель на iframe document (contentDocument).
    2) Оформить в нём html документ для работы.
    3) Установить режим редактирования designMode.

    Для этого я написал небольшую наглядную библиотечку.
      var NewTextArea={
        frame:{},
        document:{},
        window:{},
        init:function(frame){
          NewTextArea.frame=frames[frame]?frames[frame]:document.getElementById(frame);//IE, Opera - frames.document, другие - ById.document

          if (!NewTextArea.frame){
            alert("Ошибка ID");
            return -1;
          }
          
          //1) получить указатель
          NewTextArea.document=NewTextArea.frame.contentDocument || NewTextArea.frame.document;
          if (!NewTextArea.document){
            alert("Ошибка iframe");
            return -2;
          }
          
          NewTextArea.window=NewTextArea.frame.contentWindow || NewTextArea.frame.window;
          if (!NewTextArea.window){
            alert("window");
            return -2;
          }

          //2) Оформить iframe HTML документ
          var HTML = "<html><head></head><body>";       HTML += "<u>Document</u> <b>HTML</b>";       HTML += "</body></html>"

          
          NewTextArea.document.open();
          NewTextArea.document.write(HTML);
          NewTextArea.document.close();
          
          //3) Установить designMode
          if (NewTextArea.document.designMode){
            NewTextArea.document.designMode='on';
          }else{
            alert("Ошибка designMode");
            return -3;
          }
        }
      }

    * This source code was highlighted with Source Code Highlighter.


    Собственно, нам остаётся инициализировать сам iframe в секции onload.
    ...<body onload="NewTextArea.init('frm')">
    	<iframe id="frm" ></iframe>
    </body>
    работающий пример

    Вот и всё. Единственное замечание — этот скрипт не будет работать в IE локально — только с сервера.

    Разные вкусные доработки


    Добавим клавиши, с помощью которых мы сможем делать B, I, U.
    ...<body onload="NewTextArea.init('frm')">
    	<iframe id="frm" ></iframe>
    	<input type="button" value="B" 
    onclick="NewTextArea.window.focus();NewTextArea.document.execCommand('bold',null,'')" />
    
    	<input type="button" value="I" 
    onclick="NewTextArea.window.focus();NewTextArea.document.execCommand('italic',null,'')" />
    
    	<input type="button" value="U" 
    onclick="NewTextArea.window.focus();NewTextArea.document.execCommand('underline',null,'')" />
    
    </body>
    работающий пример

    Конкретно команды execCommand разбирать не буду — кому интересно читайте на MSDN. С помощью них вы сможете добавлять различные цвета в документ, картинки и много всего.

    И ещё — добавим стили нашего текстового поля. Для этого необходимо доработать наш Javascript:
    ...
    	var HTML = "<html><head><style>";
    	HTML += "html,body{margin:0px;padding:0px;background:black;color:white;font-size:20px}";
    	HTML += "p,div,span{margin:0px;padding:0px}";
    	HTML += "</style></head><body>";
    	HTML += "<u>Document</u> <b>HTML</b>";
    	HTML += "</body></html>";			
    ...

    Ещё можно сделать подгрузку стиля тексового поля из файла .css, вставив соответствующий тег <link ...> вместо <style>.


    UPD: Кстати, только что нашел ещё одну хабра-статью на эту тему. Кому интересно, почитайте.
    Поделиться публикацией

    Похожие публикации

    Комментарии 28
      +9
      круто, еще бы живой пример посмотреть ;-)
        0
        До дома доберусь — залью на сервер то, что получилось.
        А так — можете руками всё собрать вместе :) и посмотреть локально — notepad'a и Opera/FireFox должно хватить.
          +1
          это понятно, что статьи «хау-ту» обычно пишут с расчетом того, что кто-то доберется и сделает все как написано. я к тому, что было бы нагляднее и полезнее увидеть живой пример )
            0
            хорошо бы еще результаты тестов на разных браузерах увидеть
            +1
            Добавил рабочие примеры.
            +1
            На этом основаны все визивик-редакторы, насколько я понимаю. Так что можно какой нибудь FCK расковырять в качестве примера :)
              +1
              Я написал основы. Всё, как можно проще — для новичков.
              Если вам интерестно, что из этого можно сделать — тогда я напишу следующую статью: «Использование WYSIWYG» :)
                0
                Это я скорее товарищу Markovnin-у написал.

                Спасибо за статью. Следующую буду ждать тоже, интересно.
            • НЛО прилетело и опубликовало эту надпись здесь
                +2
                рад за вас, вы круты… а я такой ерундой занимался 6-7 лет назад и тем не менее считаю что статья найдет свой круг читателей.
                  +3
                  Я не ставил себе задачу вас удивить.

                  У меня была своя задача. Я её для себя решил.
                  И так как я много времени потратил, чтобы найти хороший материал и разобраться в нём, решил сделать несколько статей на эту тему.
                    +1
                    Спасибо! Никак не мог найти время сам поковыряться.
                  0
                  Это все конечно хорошо.
                  Здорово было бы, если подсветка кода включалась одним единственным атрибутом html или css-свойством… И обрабатывалась браузером. Давно жду. Жаль не то…
                    +1
                    Заголовок в первые пару секунд ввел буквально в шок, что за супер-секрет о textarea не знаю. Хороший заголовок :)
                    Советую вам развивать эту тему, постепенно усложняя алгоритмы и превнося новые возможности, написав простенький WYSIWYG. Плодотворная тема, если копнуть глубже.
                      +3
                      Отлично! Давно хотел узнать, как всё-таки WYSIWYG так красиво рисует контент ))
                        0
                        Последняя ссылка из статьи убивает Opera 9.61 ( zcn.ru/tmp/W3.html )
                          0
                          Вы абсолютно правы. Я уже исправил.

                          надо
                          NewTextArea.frame=frames[frame]?frames[frame]:document.getElementById(frame);
                          вместо
                          NewTextArea.frame=document.getElementById(frame);

                          Надо отослать баг-репорт в Оперу :)
                          +1
                          Немного вводит в заблуждение слово «TextArea», хоть и взято в кавычки.
                            0
                            //1) получить указатель NewTextArea.document=NewTextArea.frame.contentDocument?NewTextArea.frame.contentDocument:NewTextArea.frame.document?NewTextArea.frame.document:false;


                            NewTextArea.document = NewTextArea.frame.contentDocument || NewTextArea.frame.document || false;

                            Используйте краткую запись)
                              0
                              Век живи, век учись ;)
                              Спасибо!
                              +1
                              а возможно ли таким методом сделать подсветку синтаксиса html, php, css или js? имеется в виду повышение удобства редактирования шаблонов из цмсок.
                                +1
                                Да, возможно. В следующих статьях я разберу эти методы.
                              • НЛО прилетело и опубликовало эту надпись здесь
                                  –3
                                  Автор, не стоило designMode уделять столько времени. contentEditable поддерживается в


                                  Firefox по статистике одного очень посещаемого сайта (не хочу называть имени) — 33%, из них Firefox 3 — 26,8% (от общего числа посетителей). Таким образом contentEditable не покрывает всего лишь 6,2% (Firefox 2). Для такой в принципе нетривиальной вещи, как WYSIWYG-редактор, это нормальная погрешность, на которую можно забить и получать удовольствие от правильного атрибута contentEditable.
                                    0
                                    наверное, поэтому в Хроме не работает WYSIWYG на некоторых сайтах
                                      0
                                      Как раз contentEditable там работает и даже Ctrl+B, Ctrl+I и Ctrl+U работают без всяких жабоскриптов (как и в Опере).
                                      0
                                      Ничего страшного. contentEditable работает так-же, как и designMode. По этому разобравшись с более сложными вещами, можно делать тоже самое с более простыми.
                                      0
                                      глупый вопрос: А почему NewTextArea.init работает только при событии body.onload?

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

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