css, javascript: textarea текст разного цвета

298
11 апреля 2018, 05:57

Подскажите пожалуйста, можно как-нибудь сделать так, чтобы часть текста в textarea поле было заданного цвета, отличного от основного цвета текста?

Знаю, что есть отдельные библиотеки, но для моих целей это избыточно. Вот и думаю, можно ли обойтись малой кровью.

Answer 1

Стандартные элементы textarea и input не предоставляют возможностей форматирования отдельных подстрок их текстового содержимого, и не могут содержать вложенные строчные элементы (которым можно было бы задать CSS-стили).

Поэтому, данная задача обычно решается созданием имитации поля ввода:

  1. Элементу, предназначенному для вывода (обычно берется div или span), добавляют атрибут contenteditable - он включает возможность прямого редактирования текста содержимого;

  2. CSS-стилями достигают внешнего сходства элемента со стандартным полем ввода - фон, бордер, прокрутка при переполнении, и т.д.. В контексте задачи, это самая легкая часть;

  3. Пишут скрипт, который оборачивает части текстового содержимого в подэлементы (которые, в свою очередь, и оформляются средствами CSS). А также, реализуется поведение элемента, подобное поведению RichText-редактора. И вот это, уже весьма непросто.

Основная сложность - в получении и установке позиции каретки (текстового курсора)... актуальные веб-стандарты не предоставляют удобных средств для этого. Необходимо создавать велосипед, и довольно непростой (с нуля, подобный скрипт за 10 минут не написать).
Следовательно, если цель не в том чтобы создать свое - рациональнее будет использовать одну из множества готовых реализаций такого элемента ввода.

Простой пример подсветки (не "на лету"):

const HL_RULES = [ 
  { cl: 'str',  re: /("[^"]+")/ },  
  { cl: 'optr', re: /^(var|let|for|in|of|function|function*|const|do|while|break|continue|if|else|switch|case|default|try|catch|throw|delete|void|yield|yield*|return)$/ },  
  { cl: 'cnst', re: /(\d+)/ },  
  { cl: 'brcs', re: /({|})/ } 
];  
 
let txt = document.querySelector('.custom-textarea'), 
    btn = document.getElementById('hl');  
txt.addEventListener('focus', () => txt.innerHTML = txt.textContent);  
btn.addEventListener('click', () => simpleHighlighter(txt));  
btn.click();  
 
function simpleHighlighter(el) { 
  if (el instanceof Event) 
    el = this;  
  // если эту функцию назначать обработчиком input, то тут должен быть код сохранения позиции каретки...  
  let words = textToArray(el), 
      html  = '',  
      rule;  
  words.forEach(word => { 
    if (rule = getHlRuleFor(word)) 
      html += `<span class="${rule.cl}">${rule.m}</span>`;  
    else 
      html += word;  
  }); 
  el.innerHTML = html;  
  // ..., а тут - код установки каретки обратно на сохраненную позицию 
} 
 
function textToArray(el) { 
  const RE = /(^|\(+?|[^\w]+?)((?:"[^"]*")|(?:[{}\w]+))(\)+?[\S]+?)?/gmi;   // эта регулярка весьма далека от совершенства :) 
  let result = [],  
      match, i;  
  while (match = RE.exec(el.textContent)) { 
    for (i = 1; i < match.length; i++) { 
      if (match[i]) 
        result.push(match[i]);  
    } 
  } 
  return result;  
} 
 
function getHlRuleFor(word) { 
  let rule, mr;  
  if (rule = HL_RULES.find(r => mr = word.match(r.re))) 
    rule.m = mr[1];  
  return rule;  
}
* { font: 14px sans-serif; } 
html, body { margin: 0; padding: 0; } 
 
.custom-textarea { 
  min-width: 40ch;  
  min-height: 7em; height: 50%;  
  padding: 0.1em 0.3ch; margin: 0.5em 0;  
  overflow: auto; resize: both;  
  border: 1px solid #ccc;  
} 
.custom-textarea, 
.custom-textarea span { 
  font-family: monospace;  
} 
 
.str  { color: #e94; } 
.optr { color: #47d; font-weight: 500; } 
.cnst { color: #4a4; } 
.brcs { color: #d44; }
<pre class="custom-textarea" contenteditable="">function foo() { 
  let a = "Последовательность чисел:"; 
  for (var i = 0; i < 42; i++) { 
    if (a === 13) 
      alert("Попалось 'счастливое' число :)"); 
    a += " " + i; 
  } 
  return a;  
} 
</pre> 
<button id="hl">Подсветить синтаксис</button>

Answer 2

в textarea работать не будет

<textarea rows="7" contenteditable="true"> 
<h1>qwa</h1> 
<p>qwa-qwa</p> 
</textarea>

зато можно так

введи @qwa

// ----- 
var textarea = textarea || document.getElementById('textarea') 
let timer = null // id таймера 
let t = 1e3 // задержка в миллисекундах 
textarea.addEventListener('keyup', foo) 
// -- 
function highlightWords() { 
  var original = textarea.textContent 
  var replaced = original.replace(/@[a-zA-Z0-9]+/g, 
        (username) => "<span class='exists'>" + username + "</span>") 
  textarea.innerHTML = replaced 
} 
 
function foo(e) { 
  clearTimeout(timer) 
  if (e.srcElement.textContent) { 
    timer = setTimeout(highlightWords, t); 
  } 
}
div[contenteditable=true] { 
  width: 300px; 
  height: 200px; 
  border: 1px solid #ccc; 
  padding: 5px; 
} 
 
.exists { 
  color: blue; 
}
<div contenteditable="true" id="textarea"> 
  <h1>qwa</h1> 
  <p>qwa-<b>qwa</b></p> 
  <center>center</center> 
</div>

данный пример взят и отредактирован отсюда https://stackoverflow.com/a/21420554/4794368

READ ALSO
экспорт таблиц html в xlsx на js

экспорт таблиц html в xlsx на js

Доброго времени суток, стоит задача передать N таблиц html в excel в формате xlsx + передавать туда же картинку в виде base64, может кто сталкивался подскажите...

214
Неправильная работа классов css

Неправильная работа классов css

Проблема с работой cssПочему тег p с классом cite не работает? Написал всё правильно, проверил через ideone, работает нормально

206
Как вызвать fancybox по двойному клику и запретить по одинарному?

Как вызвать fancybox по двойному клику и запретить по одинарному?

Есть OwlCarousel с фотографиями, при нажатии на которые по двойному клику должна открываться галерея fancyboxДело в том, что галерея открывается по умолчанию...

235
Изменить положение блока slick slider

Изменить положение блока slick slider

Доброго дня! Имеется карточка товара:

221