Выбранный Javascript текст
У меня есть html-страница с текстовым контентом. При выборе любого текста и нажатии кнопки выделения, я могу изменить стиль выделенного текста, чтобы выделить то же самое. Чтобы реализовать эту функцию, я написал следующий метод.
sel = window.getSelection();
var range = sel.getRangeAt(0);
var span = document.createElement('span');
span.className = "highlight" + color;
range.surroundContents(span);
Это отлично работает, если вы выберете текст без тега html, но когда текст имеет какой-либо тег html между ними, он дает ошибку
Не удалось выполнить "surroundContents" в "Range": диапазон частично выбрал нетекстовый node.
Как решить эту проблему. Можно ли выделить одно и то же отдельно для каждой части (разделенной тэгами html)?
Ответы
Ответ 1
if (window.getSelection) {
var sel = window.getSelection();
if (!sel) {
return;
}
var range = sel.getRangeAt(0);
var start = range.startContainer;
var end = range.endContainer;
var commonAncestor = range.commonAncestorContainer;
var nodes = [];
var node;
for (node = start.parentNode; node; node = node.parentNode){
var tempStr=node.nodeValue;
if(node.nodeValue!=null && tempStr.replace(/^\s+|\s+$/gm,'')!='')
nodes.push(node);
if (node == commonAncestor)
break;
}
nodes.reverse();
for (node = start; node; node = getNextNode(node)){
var tempStr=node.nodeValue;
if(node.nodeValue!=null && tempStr.replace(/^\s+|\s+$/gm,'')!='')
nodes.push(node);
if (node == end)
break;
}
for(var i=0 ; i<nodes.length ; i++){
var sp1 = document.createElement("span");
sp1.setAttribute("class", "highlight"+color );
var sp1_content = document.createTextNode(nodes[i].nodeValue);
sp1.appendChild(sp1_content);
var parentNode = nodes[i].parentNode;
parentNode.replaceChild(sp1, nodes[i]);
}
}
Ответ 2
Смотрите Range.extractContents
:
document.getElementById('execute').addEventListener('click', function() {
var range = window.getSelection().getRangeAt(0),
span = document.createElement('span');
span.className = 'highlight';
span.appendChild(range.extractContents());
range.insertNode(span);
});
.highlight { background-color: yellow; }
<div id="test">
Select any part of <b>this text and</b> then click 'Run'.
</div>
<button id="execute">Run</button>
Ответ 3
Вместо того, чтобы изобретать велосипед, я бы использовал Rangy возможности выделения.
Я разветкил скрипку RGraham created и создал новый скрипт, который показывает, как это работает. Вот как это делается:
var applier = rangy.createClassApplier("highlight");
var highlighter = rangy.createHighlighter();
highlighter.addClassApplier(applier);
document.getElementById('execute').addEventListener('click', function() {
highlighter.removeAllHighlights();
highlighter.highlightSelection("highlight");
});
Это создает ярлык, который будет устанавливать класс highlight
на элементах, которые полностью находятся внутри выделения, и создавать промежутки с классом highlight
по мере необходимости для элементов, которые охватывают выбор. Когда нажата кнопка с id execute
, старые фокусы удаляются и применяются новые блики.
Функциональность ярлыка является частью выпуска Rangy, которые считаются "альфа". Тем не менее, Я уже несколько лет использую альфа-версии Rangy, но было очень редко, что я обнаружил проблему с моим приложением, чтобы я мог вернуться к Rangy. И несколько раз, когда я обнаружил проблему с Rangy, Тим Даун (ее автор) был довольно отзывчив.
Ответ 4
попробуйте следующее:
newNode.appendChild(range.extractContents())
в соответствии с MDN:
Частично выбранные узлы клонируются для включения родительских тегов необходимо сделать документ фрагмента действительным.
В то время как Range.surroundContents:
Исключение будет выдано, однако, если Range разбивает нетекстовый node только с одной из ее граничных точек. То есть, в отличие от альтернатива выше, если есть частично выбранные узлы, они будут не клонироваться, и вместо этого операция завершится неудачно.
Не тестировалось, но...
Ответ 5
Это решение немного сложно, но я считаю, что этого будет достаточно
Когда вы увидите внимательно объект выделения, который мы получаем через вызов window.getSelection(). getRangeAt (0)
Вы увидите, что есть 4 свойства:
startContainer
startOffset
endContainer
endOffset
Итак, теперь вам нужно начать с startContainer с помощью startOffset и начать размещать необходимые узлы диапазона.
Если теперь endContainer отличается от node, тогда вам нужно начать перемещение узлов от startContainer до endContainer
Для прохождения вам нужно проверить дочерние узлы и узлы-узлы, которые вы можете получить из объектов DOM,
Итак, сначала запустите startContainer, пройдите через все его дочерние элементы и проверьте, не является ли дочерний элемент node встроенным элементом, а затем примените тег span вокруг него, а затем вам нужно написать небольшое кодирование для различных угловых случаев
Ответ 6
Решение действительно сложно.
Я как-то нашел обходное решение. См. Мой fiddle
function highlight() {
var range = window.getSelection().getRangeAt(0),
parent = range.commonAncestorContainer,
start = range.startContainer,
end = range.endContainer;
var startDOM = (start.parentElement == parent) ? start.nextSibling : start.parentElement;
var currentDOM = startDOM.nextElementSibling;
var endDOM = (end.parentElement == parent) ? end : end.parentElement;
//Process Start Element
highlightText(startDOM, 'START', range.startOffset);
while (currentDOM != endDOM && currentDOM != null) {
highlightText(currentDOM);
currentDOM = currentDOM.nextElementSibling;
}
//Process End Element
highlightText(endDOM, 'END', range.endOffset);
}
function highlightText(elem, offsetType, idx) {
if (elem.nodeType == 3) {
var span = document.createElement('span');
span.setAttribute('class', 'highlight');
var origText = elem.textContent, text, prevText, nextText;
if (offsetType == 'START') {
text = origText.substring(idx);
prevText = origText.substring(0, idx);
} else if (offsetType == 'END') {
text = origText.substring(0, idx);
nextText = origText.substring(idx);
} else {
text = origText;
}
span.textContent = text;
var parent = elem.parentElement;
parent.replaceChild(span, elem);
if (prevText) {
var prevDOM = document.createTextNode(prevText);
parent.insertBefore(prevDOM, span);
}
if (nextText) {
var nextDOM = document.createTextNode(nextText);
parent.appendChild(nextDOM);
}
return;
}
var childCount = elem.childNodes.length;
for (var i = 0; i < childCount; i++) {
if (offsetType == 'START' && i == 0)
highlightText(elem.childNodes[i], 'START', idx);
else if (offsetType == 'END' && i == childCount - 1)
highlightText(elem.childNodes[i], 'END', idx);
else
highlightText(elem.childNodes[i]);
}
}