Я хотел бы выделить (применить css) определенный диапазон текста, обозначенный его начальной и конечной позицией. Это сложнее, чем кажется, поскольку в тексте могут быть другие теги, которые нужно игнорировать.
Пример:
<div>abcd<em>efg</em>hij</div>
highlight(2, 6) нужно выделить "cdef", не удаляя тег.
Я уже пытался использовать объект TextRange, но безуспешно.
Заранее спасибо!
Можете ли вы удалить теги во временной строке, а затем подстроку из этой строки? kevinji 05.06.2011
Вы не можете просто игнорировать теги, иначе вы получите недействительный html: ab<x>cd<em>ef</x>f</em>. Вам нужно будет сделать что-то вроде ab<x>cd</x><em><x>ef</x>g</em> serg 05.06.2011
Конечно, я не могу игнорировать теги, но было бы неплохо, если бы браузер каким-то образом решал эти проблемы за меня. Vincent 05.06.2011
Ниже приведена функция для выбора пары смещений символов внутри определенного элемента. Это наивная реализация: она не принимает во внимание любой текст, который может быть сделан невидимым (либо с помощью CSS, либо, например, находясь внутри элемента <script> или <style>) и может иметь несоответствия браузера (IE по сравнению со всем остальным) с разрывами строк, и не учитывает свернутые пробелы (например, 2 или более последовательных пробела, свернутые в один видимый пробел на странице). Тем не менее, он работает для вашего примера во всех основных браузерах.
Что касается другой части, выделения, я бы предложил использовать для этого document.execCommand(). Вы можете использовать мою функцию ниже, чтобы установить выбор, а затем вызвать document.execCommand(). Вам нужно сделать документ временно редактируемым в браузерах, отличных от IE, чтобы команда работала. См. мой ответ здесь для кода: getSelection & SurroundContents по нескольким тегам
Следующее решение не работает для IE, для этого вам нужно применить объекты TextRange и т.д. Поскольку для этого используются выборки, в обычных случаях он не должен нарушать HTML, например:
Обратите внимание, как он переносит первый символ за пределами диапазона в em, а затем остальные символы в пределах span в новый. Где, как если бы он просто открывал его на символе 3 и заканчивал бы на символе 6, это дало бы недопустимую разметку, например:
<div>abc<em>d<span>ef</em>g</span>hij</div>
Код:
var r = document.createRange();
var s = window.getSelection()
r.selectNode($('div')[0]);
s.removeAllRanges();
s.addRange(r);
// not quite sure why firefox has problems with this
if ($.browser.webkit) {
s.modify("move", "backward", "documentboundary");
}
function highlight(start,end){
for(var st=0;st<start;st++){
s.modify("move", "forward", "character");
}
for(var st=0;st<(end-start);st++){
s.modify("extend", "forward", "character");
}
}
highlight(2,6);
var ra = s.getRangeAt(0);
var newNode = document.createElement("em");
newNode.appendChild(ra.extractContents());
ra.insertNode(newNode);
изменить Похоже, по крайней мере, у моего FF4 были проблемы с
s.modify("move", "backward", "documentboundary");
но в то же время вроде и без него работает, поэтому просто поменял на
if ($.browser.webkit) {
s.modify("move", "backward", "documentboundary");
}
забастовка>
редактировать, как указал Тим, модификация доступна только начиная с FF4, поэтому я применил другой подход к выбору, который не требует метода модификации, в надежде сделать его немного более браузерным. совместим (IE по-прежнему нуждается в собственном решении).
Код:
var r = document.createRange();
var s = window.getSelection()
var pos = 0;
function dig(el){
$(el).contents().each(function(i,e){
if (e.nodeType==1){
// not a textnode
dig(e);
}else{
if (pos<start){
if (pos+e.length>=start){
range.setStart(e, start-pos);
}
}
if (pos<end){
if (pos+e.length>=end){
range.setEnd(e, end-pos);
}
}
pos = pos+e.length;
}
});
}
var start,end, range;
function highlight(element,st,en){
range = document.createRange();
start = st;
end = en;
dig(element);
s.addRange(range);
}
highlight($('div'),3,6);
var ra = s.getRangeAt(0);
var newNode = document.createElement("em");
newNode.appendChild(ra.extractContents());
ra.insertNode(newNode);
private highlightRange(selector: JQuery, start: number, end: number): void {
let cur = 0;
let replacements: { node: Text; pos: number; len: number }[] = [];
let dig = function (node: Node): void {
if (node.nodeType === 3) {
let nodeLen = (node as Text).data.length;
let next = cur + nodeLen;
if (next > start && cur < end) {
let pos = cur >= start ? cur : start;
let len = (next < end ? next : end) - pos;
if (len > 0) {
if (!(pos === cur && len === nodeLen && node.parentNode &&
node.parentNode.childNodes && node.parentNode.childNodes.length === 1 &&
(node.parentNode as Element).tagName === 'SPAN' && (node.parentNode as Element).className === 'highlight1')) {
replacements.push({
node: node as Text,
pos: pos - cur,
len: len,
});
}
}
}
cur = next;
}
else if (node.nodeType === 1) {
let childNodes = node.childNodes;
if (childNodes && childNodes.length) {
for (let i = 0; i < childNodes.length; i++) {
dig(childNodes[i]);
if (cur >= end) {
break;
}
}
}
}
};
selector.each(function (index, element): void {
dig(element);
});
for (let i = 0; i < replacements.length; i++) {
let replacement = replacements[i];
let highlight = document.createElement('span');
highlight.className = 'highlight1';
let wordNode = replacement.node.splitText(replacement.pos);
wordNode.splitText(replacement.len);
let wordClone = wordNode.cloneNode(true);
highlight.appendChild(wordClone);
wordNode.parentNode.replaceChild(highlight, wordNode);
}
}