#javascript #regex
Вопрос:
Мне нужно раскрасить код текстовой области для инструмента редактирования HTML/CSS/JS/результатов разделения. В этом примере я хочу, чтобы теги были одного цвета, а весь остальной текст — другого цвета.
Всегда используется моноширинный шрифт.
Поэтому я решил отделить текст тегов от остальных и поместить каждую часть в отдельную текстовую область поверх основной. Недостающая половина (обычный текст для тегов и текст тегов для остальных) заменяется пробелами, чтобы все оставалось в правильном положении.
Ниже приведен рабочий фрагмент (я провел только простые тесты на ввод текста).
У меня есть пара вопросов:
- Есть ли какие-либо серьезные недостатки в этом подходе? Будет ли конечный пользователь отключен от какой-либо обычной текстовой операции в текстовой области?
- Я использую следующее регулярное выражение для отделения тегов от остального текста:
elem.value.match(/(<[^<>] >)|([sS]*?(?=(<[^<>] >)))|((?<=(<[^<>] >))[sS]*)/g);
идея здесь состоит в том, чтобы сопоставить либо тег (<[^<>] >)
, либо что-то, за чем следует тег, либо что-то, что следует за ним.
Можно ли упростить это выражение?
В идеале, чтобы он говорил «захват» или «не тег», мне трудно определить последнее.
const TAB = ' ';
const get = id => document.getElementById(id);
const [main, tag, txt] = [get('main'), get('tag'), get('txt')];
const colorizeHTML = evt => {
const elem = evt.target;
const arr = elem.value.split(/(?!^)(?=<[^<>] >)|(?<=<[^<>] >)(?!$)/);
if (!arr) return;
let [str_tag, str_txt] = ['', ''];
arr.forEach(s => {
const repl = s.replace(/[^n ]/g, 'xa0');
if (s[0] == '<' amp;amp; s[s.length - 1] == '>') {
str_tag = s;
str_txt = repl;
} else {
str_tag = repl;
str_txt = s;
}
});
tag.value = str_tag;
txt.value = str_txt;
}
const onKeyDownDefault = evt => {
if (evt.key == "Tab") {
evt.preventDefault();
document.execCommand('insertText', false, TAB);
}
}
const onScroll = evt => {
const scr = evt.target.scrollTop;
tag.scrollTop = scr;
txt.scrollTop = scr;
}
main.addEventListener('keydown', onKeyDownDefault);
main.addEventListener('scroll', onScroll);
['input', 'keyup', 'mouseup', 'drop', 'paste'].forEach(nm => { main.addEventListener(nm, colorizeHTML) });
main.value = 'Chapter 12<div id="sec1">n<div>Standard Curves</div>n<div id="tot1"></div>n<button id="btn1">Toggle points</button>n</div>';
colorizeHTML({ target: main });
* {
margin: 0px;
padding: 0px;
box-sizing: border-box;
}
body {
background-color: #000;
color: #999;
}
textarea:focus {
outline: none;
}
#wrapper {
margin: 5px;
position: relative;
width: 400px;
height: 170px;
}
#tag, #txt {
position: absolute;
left: 0px;
top: 0px;
pointer-events: none;
}
#main, #tag, #txt {
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0);
resize: none;
font-family: monospace;
padding: 3px;
}
#main {
border: solid 1px #666;
background: rgba(8, 15, 32, 1.0);
caret-color: #91d6db;
}
#main::selection {
background: rgba(0, 255, 128, 0.15);
}
#main::-moz-selection {
background: rgba(0, 255, 128, 0.15);
}
#tag {
color: #4576e6;
}
#txt {
color: #9bc6ca;
}
.clean_scroll {
overflow: scroll;
-ms-overflow-style: none;
scrollbar-width: none;
}
.clean_scroll::-webkit-scrollbar {
display: none;
}
<div id="wrapper">
<textarea id="main" class="clean_scroll"></textarea>
<textarea id="tag" class="clean_scroll"></textarea>
<textarea id="txt" class="clean_scroll"></textarea>
</div>
Комментарии:
1. Я вижу один ГЛАВНЫЙ недостаток … когда вы нажимаете вкладку, вы переключаетесь между текстовыми областями, и вы можете писать напрямую
tag
илиtxt
без какого-либо события, создавая беспорядок дублированных цветов в одном и том же месте, и один раз, когда я нажимал случайные клавиши, он начал писать черным (не знаю, когда).2. Я добавил код для обработки вкладки на месте
3. Захват фокуса внутри элемента убивает a11y. Вам необходимо предоставить хотя бы функциональность (и
aria-label
объяснение этого) дляesc
ключа для выхода из текстовой области для пользователей, не использующих мышь.