提问人:psychcoder 提问时间:10/24/2023 最后编辑:psychcoder 更新时间:10/25/2023 访问量:136
Javascript 突出显示光标前进和后退的文本
Javascript highlight text following cursor forwards and backwards
问:
我有一个任务,用户需要用光标突出显示部分文本。但是,我现在处理鼠标移动事件的方式只允许用户在单个方向上突出显示文本 - 即,如果他们在移动事件中更改鼠标方向,则文本突出显示不会跟随他们的光标。
这篇文章与请求相关,但通过添加/切换类来处理文本突出显示,而我通过更改跨度元素的背景颜色来处理突出显示。这篇文章也非常相关,但我不确定如何实现。
我希望弄清楚如何在鼠标改变方向时允许删除突出显示。这可能吗?
// Initialize data structures and variables
var wordDictionary = {}; // Dictionary to store words
var selectedWords = {}; // Currently selected words
var isMouseDown = false; // Flag for mouse state
var currentColor = ''; // Current highlight color
var usedColors = new Set(); // Set of used colors
// Available highlight colors
var availableColors = ["yellow", "red", "blue", "green", "orange"];
var highlights = {}; // Store highlighted words for each event
var eventCounter = 0; // Counter for events
var text = "Pangolins, sometimes known as scaly anteaters, are mammals of the order Pholidota. \
The one extant family, the Manidae, has three genera: Manis, Phataginus, and Smutsia. \
Manis comprises four species found in Asia, while Phataginus and Smutsia include two species each, all found in sub-Saharan Africa. \
These species range in size from 30 to 100 cm (12 to 39 in). \
A number of extinct pangolin species are also known. \
In September 2023, nine species were reported.<br><br> \
Pangolins have large, protective keratin scales, similar in material to fingernails and toenails, covering their skin; \
they are the only known mammals with this feature. \
They live in hollow trees or burrows, depending on the species. \
Pangolins are nocturnal, and their diet consists of mainly ants and termites, which they capture using their long tongues. \
They tend to be solitary animals, meeting only to mate and produce a litter of one to three offspring, which they raise for about two years.";
var textParagraph = document.getElementById("textParagraph");
textParagraph.innerHTML = text;
// Execute when the window is loaded
window.onload = function () {
var contentDiv = document.getElementById('content');
let ptag = contentDiv.querySelector('p');
var text = ptag.innerHTML.trim();
var words = text.split(/\s+|(?=<br><br>)/);
// Populate wordDictionary with words and create span elements for each word
for (var i = 0; i < words.length; i++) {
wordDictionary[i] = words[i];
}
for (var i = 0; i < words.length; i++) {
var wordElement = document.createElement('span');
wordElement.textContent = words[i] + ' ';
wordElement.dataset.index = i;
wordElement.addEventListener('mousedown', handleMouseDown);
if (words[i] == '<br><br>') {
wordElement.textContent = ' ';
wordElement.classList.add('line-break')
}
contentDiv.appendChild(wordElement);
}
// Add mouseup event listener for handling mouse up events
document.addEventListener('mouseup', handleMouseUp);
};
// Function to get a random highlight color
function getRandomColor() {
if (availableColors.length === 0) {
availableColors = [...usedColors];
usedColors.clear();
}
var randomIndex = Math.floor(Math.random() * availableColors.length);
var color = availableColors.splice(randomIndex, 1)[0];
usedColors.add(color);
return color;
}
// Function to handle mouse down event on words
function handleMouseDown(event) {
isMouseDown = true;
var index = event.target.dataset.index;
var word = event.target.textContent.trim();
if (!isHighlighted(index)) {
selectedWords = {};
selectedWords.startIndex = index;
selectedWords.endIndex = index;
selectedWords[index] = word;
// console.log(selectedWords)
currentColor = getRandomColor();
event.target.style.backgroundColor = currentColor;
event.preventDefault();
document.addEventListener('mousemove', handleMouseMove);
}
}
// Function to handle mouse up event
function handleMouseUp(event) {
if (isMouseDown) {
document.removeEventListener('mousemove', handleMouseMove);
var highlightedWords = {};
for (var index in selectedWords) {
if (index !== 'startIndex' && index !== 'endIndex') {
highlightedWords[index] = selectedWords[index];
}
}
eventCounter++;
highlights[eventCounter] = highlightedWords;
// console.log(highlights);
}
isMouseDown = false;
}
// Function to handle mouse move event (word selection)
function handleMouseMove(event) {
if (isMouseDown) {
var currentIndex = event.target.dataset.index;
var startIndex = selectedWords.startIndex;
var endIndex = selectedWords.endIndex;
var contentDiv = document.getElementById('content');
var newStartIndex = Math.min(startIndex, currentIndex);
var newEndIndex = Math.max(endIndex, currentIndex);
clearPreviousSelection();
for (var i = newStartIndex; i <= newEndIndex; i++) {
selectedWords[i] = wordDictionary[i];
}
for (var i = newStartIndex + 1; i <= newEndIndex + 1; i++) {
contentDiv.children[i].style.backgroundColor = currentColor;
}
selectedWords.startIndex = newStartIndex;
selectedWords.endIndex = newEndIndex;
}
}
// Function to clear previously selected words
function clearPreviousSelection() {
var contentDiv = document.getElementById('content');
for (var i in selectedWords) {
if (i !== 'startIndex' && i !== 'endIndex') {
contentDiv.children[i].style.backgroundColor = '';
delete selectedWords[i];
}
}
}
// Function to check if a word is already highlighted
function isHighlighted(index) {
for (var eventKey in highlights) {
var highlightedWords = highlights[eventKey];
for (var wordIndex in highlightedWords) {
if (wordIndex === index) {
return true;
}
}
}
return false;
}
// Function to clear all selections and reset
function clearSelections() {
var contentDiv = document.getElementById('content');
var wordElements = contentDiv.getElementsByTagName('span');
for (var i = 0; i < wordElements.length; i++) {
wordElements[i].style.backgroundColor = '';
}
highlights = {};
eventCounter = 0;
}
// Function to undo the last selection
function undoSelection() {
if (eventCounter > 0) {
var lastHighlight = highlights[eventCounter];
for (var index in lastHighlight) {
var wordIndex = parseInt(index);
var contentDiv = document.getElementById('content');
if (!isNaN(wordIndex)) {
contentDiv.children[wordIndex + 1].style.backgroundColor = '';
}
}
delete highlights[eventCounter];
eventCounter--;
}
}
// Add event listeners to the clear and undo buttons
document.getElementById("removeHighlight").addEventListener("click", clearSelections);
document.getElementById("undoHighlight").addEventListener("click", undoSelection);
#buttons {
margin-top: 30px;
}
.line-break {
display: block;
margin: 15px;
}
#trial_display {
display: block;
padding: 50px;
}
#title{
text-align: center;
font-size: 20px;
}
#content {
display: block;
border: 2px solid gray;
padding: 50px;
}
<div id="trial_display">
<div id="content">
<p id="textParagraph" style="display: none"></p>
</div>
<div id="buttons">
<button id="removeHighlight">Clear</button>
<button id="undoHighlight">Undo</button>
</div>
</div>
答:
1赞
traynor
10/24/2023
#1
似乎您想要的,已经通过浏览器的选择 API 原生实现。
因此,您可以通过选择来处理这一切,方法是从中获取元素,然后通过其数据索引更改它们的背景颜色,以及更改默认选择突出显示背景颜色,以便它们保持一致。
试试这个(代码关键点):
- 从选区中提取元素
sel.getRangeAt(i).cloneContents()
- 循环每个元素,获取索引,然后更改其背景
document.querySelector(`[data-index="${index}"]`).style.backgroundColor = currentColor;
- 每次选择开始时,删除以前的选择规则,然后使用新颜色添加新颜色
sheet.insertRule(`#content span::selection { background-color: ${currentColor}; }`, sheet.cssRules.length);
编辑
我还更改了 和 功能,因此现在我只是将突出显示的索引存储在数组中,然后在撤消时弹出最后一个元素,循环跨度并删除它们的背景。如果需要存储单词,也可以通过索引从元素中获取它们。undo
clear
包含完整新添加代码的步骤:
从选区中提取元素
function getSelectionElements(e) { const sel = window.getSelection(); if (sel.rangeCount) { // container to store all seleccted elements const container = document.createElement('div'); for (let i = 0, len = sel.rangeCount; i < len; ++i) { // append elements // if only single text node, then append element from event.target if (sel.getRangeAt(i).cloneContents().childNodes.length > 1) { container.appendChild(sel.getRangeAt(i).cloneContents()); } else { container.appendChild(e.target.cloneNode()); } } return container; } }
循环每个元素,获取索引,然后更改其背景
// get elements from selection // loop and add background color to each document.addEventListener('mouseup', (e) => { const selectedElements = getSelectionElements(e); if (selectedElements) selectedElements.childNodes.forEach(el => { let index = el.dataset.index; let word = el.textContent.trim(); if (!isHighlighted(index)) { selectedWords = {}; selectedWords.startIndex = index; selectedWords.endIndex = index; selectedWords[index] = word; document.querySelector(`[data-index="${index}"]`).style.backgroundColor = currentColor; } }); });
每次选择开始时,删除以前的选择规则,然后使用新颜色添加新颜色
// remove previous selection style // add new highlight color document.addEventListener('mousedown', (e) => { const sheet = document.styleSheets[0]; const rules = sheet.cssRules; for (let i = 0; i < rules.length; i++) { const rule = rules[i]; if (rule.selectorText.includes('::selection')) sheet.deleteRule(i); } currentColor = getRandomColor(); sheet.insertRule(`#content span::selection { background-color: ${currentColor}; }`, sheet.cssRules.length); });
编辑
- 撤消(代码关键点)
// remove background from last element, if any
if(previousHiglight.length > 0) {
previousHiglight.pop().forEach(word=>{
contentDiv.querySelector(`[data-index="${word}"]`).style.backgroundColor = '';
});
}
- 清除(代码关键点)
contentDiv.querySelectorAll('span').forEach(word=>{
word.style.backgroundColor = '';
});
previousHiglight.length = 0;
// Execute when the window is loaded
window.onload = function() {
// Initialize data structures and variables
var wordDictionary = {}; // Dictionary to store words
var selectedWords = {}; // Currently selected words
var isMouseDown = false; // Flag for mouse state
var currentColor = ''; // Current highlight color
var usedColors = new Set(); // Set of used colors
// Available highlight colors
var availableColors = ["yellow", "red", "blue", "green", "orange"];
var highlights = {}; // Store highlighted words for each event
var eventCounter = 0; // Counter for events
var text = "Pangolins, sometimes known as scaly anteaters, are mammals of the order Pholidota. \
The one extant family, the Manidae, has three genera: Manis, Phataginus, and Smutsia. \
Manis comprises four species found in Asia, while Phataginus and Smutsia include two species each, all found in sub-Saharan Africa. \
These species range in size from 30 to 100 cm (12 to 39 in). \
A number of extinct pangolin species are also known. \
In September 2023, nine species were reported.<br><br> \
Pangolins have large, protective keratin scales, similar in material to fingernails and toenails, covering their skin; \
they are the only known mammals with this feature. \
They live in hollow trees or burrows, depending on the species. \
Pangolins are nocturnal, and their diet consists of mainly ants and termites, which they capture using their long tongues. \
They tend to be solitary animals, meeting only to mate and produce a litter of one to three offspring, which they raise for about two years.";
var textParagraph = document.getElementById("textParagraph");
textParagraph.innerHTML = text;
var contentDiv = document.getElementById('content');
let ptag = contentDiv.querySelector('p');
var text = ptag.innerHTML.trim();
var words = text.split(/\s+|(?=<br><br>)/);
// Populate wordDictionary with words and create span elements for each word
for (var i = 0; i < words.length; i++) {
wordDictionary[i] = words[i];
}
for (var i = 0; i < words.length; i++) {
var wordElement = document.createElement('span');
wordElement.textContent = words[i] + ' ';
wordElement.dataset.index = i;
//wordElement.addEventListener('mousedown', handleMouseDown);
if (words[i] == '<br><br>') {
wordElement.textContent = ' ';
wordElement.classList.add('line-break')
}
contentDiv.appendChild(wordElement);
}
// Add mouseup event listener for handling mouse up events
//document.addEventListener('mouseup', handleMouseUp);
// Function to get a random highlight color
function getRandomColor() {
if (availableColors.length === 0) {
availableColors = [...usedColors];
usedColors.clear();
}
var randomIndex = Math.floor(Math.random() * availableColors.length);
var color = availableColors.splice(randomIndex, 1)[0];
usedColors.add(color);
return color;
}
// Function to handle mouse down event on words
function handleMouseDown(event) {
isMouseDown = true;
var index = event.target.dataset.index;
var word = event.target.textContent.trim();
if (!isHighlighted(index)) {
selectedWords = {};
selectedWords.startIndex = index;
selectedWords.endIndex = index;
selectedWords[index] = word;
// console.log(selectedWords)
currentColor = getRandomColor();
event.target.style.backgroundColor = currentColor;
event.preventDefault();
//document.addEventListener('mousemove', handleMouseMove);
}
}
// Function to handle mouse up event
function handleMouseUp(event) {
if (isMouseDown) {
document.removeEventListener('mousemove', handleMouseMove);
var highlightedWords = {};
for (var index in selectedWords) {
if (index !== 'startIndex' && index !== 'endIndex') {
highlightedWords[index] = selectedWords[index];
}
}
eventCounter++;
highlights[eventCounter] = highlightedWords;
// console.log(highlights);
}
isMouseDown = false;
}
// Function to handle mouse move event (word selection)
function handleMouseMove(event) {
if (isMouseDown) {
var currentIndex = event.target.dataset.index;
var startIndex = selectedWords.startIndex;
var endIndex = selectedWords.endIndex;
var contentDiv = document.getElementById('content');
var newStartIndex = Math.min(startIndex, currentIndex);
var newEndIndex = Math.max(endIndex, currentIndex);
clearPreviousSelection();
for (var i = newStartIndex; i <= newEndIndex; i++) {
selectedWords[i] = wordDictionary[i];
}
for (var i = newStartIndex + 1; i <= newEndIndex + 1; i++) {
contentDiv.children[i].style.backgroundColor = currentColor;
}
selectedWords.startIndex = newStartIndex;
selectedWords.endIndex = newEndIndex;
}
}
// Function to clear previously selected words
function clearPreviousSelection() {
var contentDiv = document.getElementById('content');
for (var i in selectedWords) {
if (i !== 'startIndex' && i !== 'endIndex') {
contentDiv.children[i].style.backgroundColor = '';
delete selectedWords[i];
}
}
}
// Function to check if a word is already highlighted
function isHighlighted(index) {
for (var eventKey in highlights) {
var highlightedWords = highlights[eventKey];
for (var wordIndex in highlightedWords) {
if (wordIndex === index) {
return true;
}
}
}
return false;
}
// Function to clear all selections and reset
function clearSelections() {
var contentDiv = document.getElementById('content');
var wordElements = contentDiv.getElementsByTagName('span');
for (var i = 0; i < wordElements.length; i++) {
wordElements[i].style.backgroundColor = '';
}
highlights = {};
eventCounter = 0;
}
// Function to undo the last selection
function undoSelection() {
if (eventCounter > 0) {
var lastHighlight = highlights[eventCounter];
for (var index in lastHighlight) {
var wordIndex = parseInt(index);
var contentDiv = document.getElementById('content');
if (!isNaN(wordIndex)) {
contentDiv.children[wordIndex + 1].style.backgroundColor = '';
}
}
delete highlights[eventCounter];
eventCounter--;
}
}
// Add event listeners to the clear and undo buttons
document.getElementById("removeHighlight").addEventListener("click", clearSelectionsNew);
document.getElementById("undoHighlight").addEventListener("click", undoSelectionNew);
// remove selection highlight
function clearHighlight() {
const sel = window.getSelection();
if (sel.rangeCount > 0) {
sel.removeAllRanges();
}
}
// remove background from all elements
function clearSelectionsNew() {
clearHighlight();
contentDiv.querySelectorAll('span').forEach(word => {
word.style.backgroundColor = '';
});
previousHiglight.length = 0;
}
// previous indexes store
const previousHiglight = [];
function undoSelectionNew() {
clearHighlight();
// remove background from last element, if any
if (previousHiglight.length > 0) {
previousHiglight.pop().forEach(word => {
contentDiv.querySelector(`[data-index="${word}"]`).style.backgroundColor = '';
});
}
}
function getSelectionElements(e) {
const sel = window.getSelection();
if (sel.rangeCount) {
// container to store all seleccted elements
const container = document.createElement('div');
for (let i = 0, len = sel.rangeCount; i < len; ++i) {
// append elements
// if only single text node, then append element from event.target
if (sel.getRangeAt(i).cloneContents().childNodes.length > 1) {
container.appendChild(sel.getRangeAt(i).cloneContents());
} else {
container.appendChild(e.target.cloneNode());
}
}
return container;
}
}
// remove previous selection style
// add new highlight color
contentDiv.addEventListener('mousedown', (e) => {
const sheet = document.styleSheets[0];
const rules = sheet.cssRules;
for (let i = 0; i < rules.length; i++) {
const rule = rules[i];
if (rule.selectorText.includes('::selection')) sheet.deleteRule(i);
}
currentColor = getRandomColor();
sheet.insertRule(`#content span::selection { background-color: ${currentColor}; }`, sheet.cssRules.length);
});
// get elements from selection
// loop and add background color to each
contentDiv.addEventListener('mouseup', (e) => {
const selectedElements = getSelectionElements(e);
if (selectedElements) {
const indexes = [];
selectedElements.childNodes.forEach(el => {
let index = el.dataset.index;
let word = el.textContent.trim();
if (!isHighlighted(index)) {
selectedWords = {};
selectedWords.startIndex = index;
selectedWords.endIndex = index;
selectedWords[index] = word;
contentDiv.querySelector(`[data-index="${index}"]`).style.backgroundColor = currentColor;
indexes.push(index);
}
});
previousHiglight.push(indexes);
}
});
};
#buttons {
margin-top: 30px;
}
.line-break {
display: block;
margin: 15px;
}
#trial_display {
display: block;
padding: 50px;
}
#title {
text-align: center;
font-size: 20px;
}
#content {
display: block;
border: 2px solid gray;
padding: 50px;
}
<div id="trial_display">
<div id="content">
<p id="textParagraph" style="display:none"></p>
</div>
<div id="buttons">
<button id="removeHighlight">Clear</button>
<button id="undoHighlight">Undo</button>
</div>
</div>
评论
1赞
psychcoder
10/25/2023
这太棒了,非常感谢你@traynor!!
1赞
traynor
10/25/2023
很高兴它很有帮助。此外,我已经实现并且添加了我自己的方法,因为如果不完全重构,我就无法卡住您的代码,它基于简单地将索引存储在数组中,因此如果您需要单词或其他东西,一个简单的循环可以为您提供所需的任何内容clear
undo
评论