I want to create a textarea with a spell check feature similar to Grammarly. When a misspelled word is typed, it should be underlined. Clicking on the underline should trigger a popup like this.
Upon inspecting the website's structure, I found that the content within the textarea remains unchanged. The underlines are achieved using div elements as overlays.
I've attempted this approach, but it's challenging to determine the position of each word. For example, the textarea's behavior includes early line breaks to avoid cutting off words. Here's my current code:
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>
How can I solve this issue? Is there a way to calculate the position of each word within the textarea? Or is there another approach I should consider?
Thank you.