我想创建一个具有类似于 Grammarly 的拼写检查功能的文本区域。当输入拼写错误的单词时,应在其下划线。单击下划线应会触发类似 的弹出窗口。
检查网站结构后,我发现文本区域内的内容保持不变。下划线是使用 div 元素作为叠加层来实现的。
我尝试过这种方法,但确定每个单词的位置很有挑战性。例如,文本区域的行为包括早期换行以避免切断单词。这是我当前的代码:
function updateOverlay() {
const textarea = document.getElementById('textarea');
const overlay = document.getElementById('overlay');
overlay.innerHTML = '';
const text = textarea.value;
const wordsAndSpaces = text.split(/(\s+)/) //.filter(e => e.length > 0)//.map(e => e.replace(" ", " "));
console.log(wordsAndSpaces)
let offsetLeft = 0;
let offsetTop = 0;
let lineHeight = parseFloat(window.getComputedStyle(textarea).lineHeight);
for (let item of wordsAndSpaces) {
const span = document.createElement('span');
if (item.includes(" ")) {
span.innerHTML = item.replace(" ", " ");
} else {
span.textContent = item;
}
span.style.display = 'inline-block';
overlay.appendChild(span);
const rect = span.getBoundingClientRect();
const width = rect.width;
const height = rect.height;
const left = offsetLeft + rect.left - 10;
const top = offsetTop + rect.top;
const underline = document.createElement('div');
underline.classList.add('underline');
underline.style.width = width + 'px';
underline.style.height = '1px';
underline.style.left = left + 'px';
underline.style.top = top + height - 10 + 'px';
overlay.appendChild(underline);
offsetLeft += span.offsetWidth;
if (offsetLeft >= textarea.clientWidth) {
offsetLeft = 0;
offsetTop += lineHeight;
}
overlay.removeChild(span);
}
}
// Initial call to update overlay
updateOverlay();
#textarea-container {
position: relative;
width: 400px;
font-size: medium;
font-weight: 500;
font-family: 'Courier New', Courier, monospace;
/* margin-bottom: 20px; */
}
#textarea, #overlay-container {
width: 100%;
height: 200px;
box-sizing: border-box;
padding: 0px;
font-size: 16px;
resize: none;
border: 0px solid #ccc;
margin: 0;
}
#overlay-container {
position: absolute;
top: 0;
left: 0;
pointer-events: none;
will-change: transform;
}
#overlay {
position: relative;
width: 100%;
height: 100%;
box-sizing: border-box;
padding: 0px;
margin: 0px;
}
.underline {
position: absolute;
border-bottom: 1px solid blue;
}
<div id="textarea-container">
<textarea id="textarea" oninput="updateOverlay()">a b</textarea>
<div id="overlay-container">
<div id="overlay"></div>
</div>
</div>
如何解决这个问题?有没有办法计算文本区域中每个单词的位置?或者我应该考虑其他方法吗?
谢谢你。
<!DOCTYPE html>
<html>
<head>
<title>Grammarly Style Underline</title>
<style>
#editor {
width: 80%;
margin: 20px auto;
padding: 10px;
font-size: 16px;
line-height: 1.5;
border: 1px solid #ccc;
}
.grammarly-spelling-error {
border-bottom: 2px dotted red;
}
.popup {
display: none;
position: absolute;
background: #fff;
border: 1px solid #ccc;
padding: 10px;
}
#popupContent {
margin: 0;
}
.textarea {
width: 300px;
height: 200px;
padding: 10px;
font-size: 16px;
border: 1px solid #ccc;
}
.error {
background-color: #ffcfcf;
border-bottom: 2px solid red;
}
</style>
</head>
<body>
<div class="popup" id="popup">
<p id="popupContent">Popup Content Here</p>
</div>
<div id="editor" contenteditable="true">
The red underline in a text editor or word processor typically indicates a spelling error. Most modern word
processors or text editing software, like Microsoft Word or Google Docs, have built-in spell checkers. When you
type a word that isn't recognized by the spell checker's dictionary, it underlines it with a red squiggly line
to alert you to a potential spelling mistake.
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
const editor = document.getElementById('editor');
const misspelledWords = new Set();
editor.addEventListener('keyup', function (e) {
const text = editor.innerText; // Get plain text content
const words = text.split(' '); // Split text into words
let newContent = []; // Store the corrected content here
words.forEach(word => {
if (!spellCheck(word) && !misspelledWords.has(word)) {
newContent.push(addErrorStyle(word));
misspelledWords.add(word);
} else {
newContent.push(removeErrorStyle(word));
misspelledWords.delete(word);
}
});
editor.innerHTML = newContent.join(" ");
});
// Event listener for double-click on textarea
editor.addEventListener('dblclick', function (event) {
const selection = window.getSelection();
const selectedWord = selection.toString().trim();
if (selectedWord !== '') {
const range = selection.getRangeAt(0);
const rect = range.getBoundingClientRect();
const x = rect.x + window.scrollX;
const y = rect.y + window.scrollY + rect.height;
showPopup(selectedWord, x, y);
}
});
// Function to show popup with selected word
function showPopup(word, x, y) {
const popup = document.getElementById('popup');
const popupContent = document.getElementById('popupContent');
popupContent.innerText = `Selected Word: ${word}`;
popup.style.left = `${x}px`;
popup.style.top = `${y}px`;
popup.style.display = 'block';
// Hide popup after 3 seconds
setTimeout(function () {
popup.style.display = 'none';
}, 3000);
}
function spellCheck(word) {
const dictionary = ["red", "underline", "text", "editor", "word", "processor", "spelling", "error", 'Moest', 'dsasassthe'];
return dictionary.includes(word.toLowerCase().replace(/[.,\/#!$%\^&\*;:{}=\-_`~()]/g, "")); // Removing punctuation
}
// Function to add error style
function addErrorStyle(word) {
return `<span class="grammarly-spelling-error">${word}</span>`;
}
// Function to remove error style
function removeErrorStyle(word) {
const regex = new RegExp(`<span class="grammarly-spelling-error">${word}</span>`, 'gi');
return word.replace(regex, "");
}
});
</script>
</body>
</html>`
检查此代码是否对您有帮助。