JavaScript « Манипуляции с выделенным текстом

Всем привет!

Есть такой код (внутри контейнера с аттрибутом contenteditable="true"):
/** * GeSHi (C) 2004 - 2007 Nigel McNie, 2007 - 2008 Benny Baumann * (http://qbnz.com/highlighter/ and http://geshi.org/) */ .html4strict.geshi_code {font-family:monospace;} .html4strict.geshi_code .imp {font-weight: bold; color: red;} .html4strict.geshi_code .kw2 {color: #000000; font-weight: bold;} .html4strict.geshi_code .kw3 {color: #000066;} .html4strict.geshi_code .es0 {color: #000099; font-weight: bold;} .html4strict.geshi_code .br0 {color: #66cc66;} .html4strict.geshi_code .sy0 {color: #66cc66;} .html4strict.geshi_code .st0 {color: #ff0000;} .html4strict.geshi_code .nu0 {color: #cc66cc;} .html4strict.geshi_code .sc-1 {color: #808080; font-style: italic;} .html4strict.geshi_code .sc0 {color: #00bbdd;} .html4strict.geshi_code .sc1 {color: #ddbb00;} .html4strict.geshi_code .sc2 {color: #009900;} .html4strict.geshi_code span.xtra { display:block; }

<ol>
    <li>Mozilla Firefox 1.5+</li>
    <li>Internet <span>Explorer</span> 6+</li>
    <li>Opera 9.6+</li>
    <li>Google Chrome 2+</li>
    <li>Apple Safari 3+</li>
</ol>
 

Пользователь выделил фрагмент текста:
  • конкретно слово «Chrome»,
  • или конкретно слово «Explorer».
Слово «Explorer» обернуто в тег span, а «Chrome» — нет.

Так вот, как мне узнать, обернут ли выделенный фрагмент текста каким-нибудь тегом или нет?
  1. Регулярным выражением? — Не могу понять, как выдернуть текст вместе с ХТМЛ-тегами, получается выдернуть просто чистый текст.
  2. Используя commonAncestorContainer.parentNode? — Если я выделил слово «Chrome», то родительская нода будет тег li (но этот тег li не оборачивает именно мой выделенный текст, там еще куча символов между выделенным фрагментом и родительской нодой, а задавать стили для li мне не нужно).
Вообще, хочу задать CSS-стили для выделенного юзером фрагмента:
  • если выделенный фрагмент текста уже впритык обернут каким-нибудь тегом, то просто задам стили внутри этого тега (например, в случае со словом «Explorer» я просто задам стиль style="letter-spacing:1px;" для тега span; если пользователь выделит Apple Safari 3+, то задам стиль уже тегу li);
  • если же выделенный фрагмент является просто текстом (никаким тегом вокруг впритык не обернут), то я оберну этот текст span'ом и задам ему стиль. Но с этим пунктом проблем нет, тут всё легко, уже реализовано.

Апдейт:
Вот смотрите, если выделить текст и исполнить команду document.execCommand('Bold', false, null), то браузер обернет выделенный текст в теги b. Если опять выделить тот же самый текст (а он уже жирный) и опять испольнить команду document.execCommand('Bold', false, null), то браузер удалит теги b. Тобишь, браузер же ведь как-то знает, обернут выделенный текст тегами или нет. Вроде, не должно быть сложности повторить те же действия Яваскриптом, но не вижу в упор, как…

Еще апдейт (поговорю сам с собой :):
Походу, придется регулярками парсить ХТМЛ. Например, выделяем полностью текст Apple Safari 3+ и задаем ему какой-нить шрифт стандартной командой document.execCommand, в результате получим такой ХТМЛ-код, который браузер создает сам:/** * GeSHi (C) 2004 - 2007 Nigel McNie, 2007 - 2008 Benny Baumann * (http://qbnz.com/highlighter/ and http://geshi.org/) */ .html4strict.geshi_code {font-family:monospace;} .html4strict.geshi_code .imp {font-weight: bold; color: red;} .html4strict.geshi_code .kw2 {color: #000000; font-weight: bold;} .html4strict.geshi_code .kw3 {color: #000066;} .html4strict.geshi_code .es0 {color: #000099; font-weight: bold;} .html4strict.geshi_code .br0 {color: #66cc66;} .html4strict.geshi_code .sy0 {color: #66cc66;} .html4strict.geshi_code .st0 {color: #ff0000;} .html4strict.geshi_code .nu0 {color: #cc66cc;} .html4strict.geshi_code .sc-1 {color: #808080; font-style: italic;} .html4strict.geshi_code .sc0 {color: #00bbdd;} .html4strict.geshi_code .sc1 {color: #ddbb00;} .html4strict.geshi_code .sc2 {color: #009900;} .html4strict.geshi_code span.xtra { display:block; }

...
<li><font face="Tahoma">Apple Safari 3+</font></li>
...
 

Получается, что можно выдрать два подряд идущих тега (с их аттрибутами), перекинуть аттрибуты из второго тега в первый (верхний), удалить этот второй тег…/** * GeSHi (C) 2004 - 2007 Nigel McNie, 2007 - 2008 Benny Baumann * (http://qbnz.com/highlighter/ and http://geshi.org/) */ .html4strict.geshi_code {font-family:monospace;} .html4strict.geshi_code .imp {font-weight: bold; color: red;} .html4strict.geshi_code .kw2 {color: #000000; font-weight: bold;} .html4strict.geshi_code .kw3 {color: #000066;} .html4strict.geshi_code .es0 {color: #000099; font-weight: bold;} .html4strict.geshi_code .br0 {color: #66cc66;} .html4strict.geshi_code .sy0 {color: #66cc66;} .html4strict.geshi_code .st0 {color: #ff0000;} .html4strict.geshi_code .nu0 {color: #cc66cc;} .html4strict.geshi_code .sc-1 {color: #808080; font-style: italic;} .html4strict.geshi_code .sc0 {color: #00bbdd;} .html4strict.geshi_code .sc1 {color: #ddbb00;} .html4strict.geshi_code .sc2 {color: #009900;} .html4strict.geshi_code span.xtra { display:block; }

...
<li face="Tahoma">Apple Safari 3+</li>
...
 
и заменить аттрибуты на стили:/** * GeSHi (C) 2004 - 2007 Nigel McNie, 2007 - 2008 Benny Baumann * (http://qbnz.com/highlighter/ and http://geshi.org/) */ .html4strict.geshi_code {font-family:monospace;} .html4strict.geshi_code .imp {font-weight: bold; color: red;} .html4strict.geshi_code .kw2 {color: #000000; font-weight: bold;} .html4strict.geshi_code .kw3 {color: #000066;} .html4strict.geshi_code .es0 {color: #000099; font-weight: bold;} .html4strict.geshi_code .br0 {color: #66cc66;} .html4strict.geshi_code .sy0 {color: #66cc66;} .html4strict.geshi_code .st0 {color: #ff0000;} .html4strict.geshi_code .nu0 {color: #cc66cc;} .html4strict.geshi_code .sc-1 {color: #808080; font-style: italic;} .html4strict.geshi_code .sc0 {color: #00bbdd;} .html4strict.geshi_code .sc1 {color: #ddbb00;} .html4strict.geshi_code .sc2 {color: #009900;} .html4strict.geshi_code span.xtra { display:block; }

...
<li style="font-face:Tahoma;">Apple Safari 3+</li>
...
 

Пока буду так пробовать.

1 ответов


Регулярки тут точно не помогут. Вообще тема не сложная, но довольно хитрая - оч много возможных вариантов.
Возможно с commonAncestorContainer, но без parentNode.
Я делаю примерно так - в начальную и конечные точки выделения ставлю пустые спаны - что-то вроде bookmark в движке trident. Дальше нахожу ближайшего общего родителя для них. и этих 3 нод достаточно чтобы получить коллекцию выделеных нод (забудьте что она может быть одна - всегда имейте дело с выделением, как с коллекцией нод)


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