Подсветка выбора пользователя javascript
Я пытаюсь найти способ с помощью javascript выделить текст, который выбирает пользователь, когда нажимает какую-то нечетную кнопку выделения (как в выделенный текст span>). Он должен работать только с WebKit или Firefox, но кажется, что это почти невозможно, потому что он должен работать в следующих случаях:
<p>this is text</p>
<p>I eat food</p>
Когда пользователь выбирает из "это текст" через "я ем" в браузере (не могу просто поставить там промежуток).
и этот случай:
<span><span>this is text</span>middle text<span>this is text</span></span>
Когда пользователь выбирает из "is text" в "this is" в браузере (даже если вы можете обернуть свои выделенные области вокруг каждого элемента в выделении, я бы хотел, чтобы вы попытались выделить этот средний текст).
Эта проблема, кажется, нигде не решена, честно говоря, я сомневаюсь, что это возможно.
Было бы возможно, если бы вы могли получить Range, полученный из выделения, в виде строки с html, которую можно было бы проанализировать и затем заменить, но, насколько я могу судить, вы не можете получить необработанный html Range. жаль.
8 ответов
Этот ответ, вероятно, на несколько лет опоздал для вас, но я столкнулся с подобной проблемой и хотел документировать ее здесь, так как это первый хит в Google.
Повторим еще раз: проблема в том, что вы хотели бы просто захватить объект Range из выбора пользователя и окружить его стилизованным div, например, так:
function highlightSelection() {
var userSelection = window.getSelection().getRangeAt(0);
highlightRange(userSelection);
}
function highlightRange(range) {
var newNode = document.createElement("div");
newNode.setAttribute(
"style",
"background-color: yellow; display: inline;"
);
range.surroundContents(newNode);
}
Но, как заявляет Первоначальный Родитель, это небезопасно. Это сработает, если выделение не пересекает границы элементов, но выдает ошибку DOM, если диапазон, созданный в разделе "Выбор пользователя", является небезопасным диапазоном, пересекающим границы тегов HTML.
Решение состоит в том, чтобы создать массив меньших объектов Range, ни один из которых по отдельности не пересекает элементный барьер, но которые совместно охватывают Range, выбранный пользователем. Каждый из этих безопасных диапазонов может быть выделен, как указано выше.
function getSafeRanges(dangerous) {
var a = dangerous.commonAncestorContainer;
// Starts -- Work inward from the start, selecting the largest safe range
var s = new Array(0), rs = new Array(0);
if (dangerous.startContainer != a)
for(var i = dangerous.startContainer; i != a; i = i.parentNode)
s.push(i)
;
if (0 < s.length) for(var i = 0; i < s.length; i++) {
var xs = document.createRange();
if (i) {
xs.setStartAfter(s[i-1]);
xs.setEndAfter(s[i].lastChild);
}
else {
xs.setStart(s[i], dangerous.startOffset);
xs.setEndAfter(
(s[i].nodeType == Node.TEXT_NODE)
? s[i] : s[i].lastChild
);
}
rs.push(xs);
}
// Ends -- basically the same code reversed
var e = new Array(0), re = new Array(0);
if (dangerous.endContainer != a)
for(var i = dangerous.endContainer; i != a; i = i.parentNode)
e.push(i)
;
if (0 < e.length) for(var i = 0; i < e.length; i++) {
var xe = document.createRange();
if (i) {
xe.setStartBefore(e[i].firstChild);
xe.setEndBefore(e[i-1]);
}
else {
xe.setStartBefore(
(e[i].nodeType == Node.TEXT_NODE)
? e[i] : e[i].firstChild
);
xe.setEnd(e[i], dangerous.endOffset);
}
re.unshift(xe);
}
// Middle -- the uncaptured middle
if ((0 < s.length) && (0 < e.length)) {
var xm = document.createRange();
xm.setStartAfter(s[s.length - 1]);
xm.setEndBefore(e[e.length - 1]);
}
else {
return [dangerous];
}
// Concat
rs.push(xm);
response = rs.concat(re);
// Send to Console
return response;
}
Затем можно (кажется) выделить Выбор пользователя с помощью этого измененного кода:
function highlightSelection() {
var userSelection = window.getSelection().getRangeAt(0);
var safeRanges = getSafeRanges(userSelection);
for (var i = 0; i < safeRanges.length; i++) {
highlightRange(safeRanges[i]);
}
}
Обратите внимание, что вам, вероятно, нужен какой-то более причудливый CSS, чтобы сделать много разнородных элементов, чтобы пользователь мог хорошо выглядеть вместе. Я надеюсь, что в конце концов это поможет другой усталой душе в интернете!
Ну, вы можете сделать это с помощью манипуляции DOM. Это работает в Firefox:
var selection = window.getSelection();
var range = selection.getRangeAt(0);
var newNode = document.createElement("span");
newNode.setAttribute("style", "background-color: pink;");
range.surroundContents(newNode);
Кажется, работает и в текущей версии Safari. См. https://developer.mozilla.org/en/DOM/range.surroundContents и http://www.w3.org/TR/2000/REC-DOM-Level-2-Traversal-Range-20001113/ranges.html
Я впервые пишу здесь, но просматривая ваши ответы, разве это не похоже на эту работу? У меня есть образец здесь: http://henriquedonati.com/projects/Extension/extension.html
function highlightSelection() {
var userSelection = window.getSelection();
for(var i = 0; i < userSelection.rangeCount; i++) {
highlightRange(userSelection.getRangeAt(i));
}
}
function highlightRange(range) {
var newNode = document.createElement("span");
newNode.setAttribute(
"style",
"background-color: yellow; display: inline;"
);
range.surroundContents(newNode);
}
Вот полный код для выделения и удаления текста
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
.highlight
{
background-color: yellow;
}
#test-text::-moz-selection { /* Code for Firefox */
background: yellow;
}
#test-text::selection {
background: yellow;
}
</style>
</head>
<body>
<div id="div1" style="border: 1px solid #000;">
<div id="test-text">
<h1> Hello How are you </h1>
<p >
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
</p>
</div>
</div>
<br />
</body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script type="text/javascript">
mouseXPosition = 0;
$(document).ready(function () {
$("#test-text").mousedown(function (e1) {
mouseXPosition = e1.pageX;//register the mouse down position
});
$("#test-text").mouseup(function (e2) {
var highlighted = false;
var selection = window.getSelection();
var selectedText = selection.toString();
var startPoint = window.getSelection().getRangeAt(0).startOffset;
var endPoint = window.getSelection().getRangeAt(0).endOffset;
var anchorTag = selection.anchorNode.parentNode;
var focusTag = selection.focusNode.parentNode;
if ((e2.pageX - mouseXPosition) < 0) {
focusTag = selection.anchorNode.parentNode;
anchorTag = selection.focusNode.parentNode;
}
if (selectedText.length === (endPoint - startPoint)) {
highlighted = true;
if (anchorTag.className !== "highlight") {
highlightSelection();
} else {
var afterText = selectedText + "<span class = 'highlight'>" + anchorTag.innerHTML.substr(endPoint) + "</span>";
anchorTag.innerHTML = anchorTag.innerHTML.substr(0, startPoint);
anchorTag.insertAdjacentHTML('afterend', afterText);
}
}else{
if(anchorTag.className !== "highlight" && focusTag.className !== "highlight"){
highlightSelection();
highlighted = true;
}
}
if (anchorTag.className === "highlight" && focusTag.className === 'highlight' && !highlighted) {
highlighted = true;
var afterHtml = anchorTag.innerHTML.substr(startPoint);
var outerHtml = selectedText.substr(afterHtml.length, selectedText.length - endPoint - afterHtml.length);
var anchorInnerhtml = anchorTag.innerHTML.substr(0, startPoint);
var focusInnerHtml = focusTag.innerHTML.substr(endPoint);
var focusBeforeHtml = focusTag.innerHTML.substr(0, endPoint);
selection.deleteFromDocument();
anchorTag.innerHTML = anchorInnerhtml;
focusTag.innerHTml = focusInnerHtml;
var anchorafterHtml = afterHtml + outerHtml + focusBeforeHtml;
anchorTag.insertAdjacentHTML('afterend', anchorafterHtml);
}
if (anchorTag.className === "highlight" && !highlighted) {
highlighted = true;
var Innerhtml = anchorTag.innerHTML.substr(0, startPoint);
var afterHtml = anchorTag.innerHTML.substr(startPoint);
var outerHtml = selectedText.substr(afterHtml.length, selectedText.length);
selection.deleteFromDocument();
anchorTag.innerHTML = Innerhtml;
anchorTag.insertAdjacentHTML('afterend', afterHtml + outerHtml);
}
if (focusTag.className === 'highlight' && !highlighted) {
highlighted = true;
var beforeHtml = focusTag.innerHTML.substr(0, endPoint);
var outerHtml = selectedText.substr(0, selectedText.length - beforeHtml.length);
selection.deleteFromDocument();
focusTag.innerHTml = focusTag.innerHTML.substr(endPoint);
outerHtml += beforeHtml;
focusTag.insertAdjacentHTML('beforebegin', outerHtml );
}
if (!highlighted) {
highlightSelection();
}
$('.highlight').each(function(){
if($(this).html() == ''){
$(this).remove();
}
});
selection.removeAllRanges();
});
});
function highlightSelection() {
var selection;
//Get the selected stuff
if (window.getSelection)
selection = window.getSelection();
else if (typeof document.selection != "undefined")
selection = document.selection;
//Get a the selected content, in a range object
var range = selection.getRangeAt(0);
//If the range spans some text, and inside a tag, set its css class.
if (range && !selection.isCollapsed) {
if (selection.anchorNode.parentNode == selection.focusNode.parentNode) {
var span = document.createElement('span');
span.className = 'highlight';
span.textContent = selection.toString();
selection.deleteFromDocument();
range.insertNode(span);
// range.surroundContents(span);
}
}
}
</script>
</html>
<html>
<head>
<script type="text/javascript">
function load(){
window.document.designMode = "On";
//run this in a button, will highlight selected text
window.document.execCommand("hiliteColor", false, "#000");
}
</script>
</head>
<body contentEditable="true" onload="load()">
this is text
</body>
</html>
Я только что закончил выпуск пакета, который представляет собой порт машинописного текста для texthighlighter (устаревшая библиотека). Простое преобразование его в машинописный текст позволило выявить несколько ошибок и упростило работу над ним в будущем. Оформить заказ https://www.npmjs.com/package/@funktechno/texthighlighter. Это не имеет зависимостей и позволяет выделять выбор пользователя, объединять основные моменты, удалять выделения, сериализовать и десериализовать (применять на основе данных) выделения.
Обратите внимание, что вам нужно будет использовать событие mouseup javascript, чтобы правильно его запустить.
import { doHighlight, deserializeHighlights, serializeHighlights, removeHighlights, optionsImpl } from "@/../node_modules/@funktechno/texthighlighter/lib/index";
const domEle = document.getElementById("sandbox");
const options: optionsImpl = {};
if (this.color) options.color = this.color;
if (domEle) doHighlight(domEle, true, options);
вот как я запустил его в проекте vue ts
<div
id="sandbox"
@mouseup="runHighlight($event)"
>text to highlight</div>
У меня была та же проблема сегодня, выделение выбранных тегов в пределах нескольких тегов. Решение:
- Найдите способ извлечь выделенную часть вместе с тегами HTML.
- Оберните извлеченную часть элементом span и поместите его обратно в DOM.
Обратитесь к приведенному ниже коду для уточнения.
function getRangeObject(selectionObject){
try{
if(selectionObject.getRangeAt)
return selectionObject.getRangeAt(0);
}
catch(ex){
console.log(ex);
}
}
document.onmousedown = function(e){
var text;
if (window.getSelection) {
/* get the Selection object */
userSelection = window.getSelection()
/* get the innerText (without the tags) */
text = userSelection.toString();
/* Creating Range object based on the userSelection object */
var rangeObject = getRangeObject(userSelection);
/*
This extracts the contents from the DOM literally, inclusive of the tags.
The content extracted also disappears from the DOM
*/
contents = rangeObject.extractContents();
var span = document.createElement("span");
span.className = "highlight";
span.appendChild(contents);
/* Insert your new span element in the same position from where the selected text was extracted */
rangeObject.insertNode(span);
} else if (document.selection && document.selection.type != "Control") {
text = document.selection.createRange().text;
}
};
с момента использования HTML
<mark>
элемент в виде выделенного текста, может быть, проще использовать этот узел, вместо того, чтобы использовать свой собственный css, гораздо более чистый код:
function highlightRange(range) {
var newNode = document.createElement('mark');
range.surroundContents(newNode);
}
// original select range function
function highlight() {
var userSelection = window.getSelection();
for(var i = 0; i < userSelection.rangeCount; i++) {
highlightRange(userSelection.getRangeAt(i));
}
}