实现可悬停删除字符的光标遇移动问题,寻求修复建议
光标移动功能修复建议
需求说明
- 光标初始需位于屏幕最左侧
- 输入内容后,光标需始终处于最新字符右侧
- 按下左右箭头按钮,光标需在相邻字符间切换悬停
- 按下删除按钮,需删除光标悬停的字符
实现思路
点击「Press to Enter Text」按钮时,向DOM中添加span元素,借助span的宽度与位置设置光标属性;按下左、右或删除按钮时,获取screenText的对应子元素执行操作。
当前问题
光标无法移动,以下是编写的代码:
HTML代码
<!DOCTYPE html> <html> <body> <canvas id="screen" style="background-color:black; border:1px; position:absolute; top:1em; left:40em; width:26em; height:10em;"></canvas> <div id="screenText" style="position:absolute; top:7em; left: 41em;"></div> <button id="textButton" style="width:100px; height:100px; position:absolute; top:20em; left:60em;">Press to Enter Text</button> <button id="leftButton" style="width:100px; height:100px; position:absolute; top:30em; left:50em;">←</button> <button id="rightButton" style="width:100px; height:100px; position:absolute; top:30em; left:70em;">→</button> <button id="deleteButton" style="width:100px; height:100px; position:absolute; top:40em; left:60em;">Press to delete character</button> </body> <script src="script.js"></script> </html>
JavaScript代码
let screen; let cursor; let id; let num = 0; let cursorPosition = 1; //used to index into `screenText`'s NodeList; initialized to one because the cursor is the first child node, and it needs to be skipped window.onload = () =>{ screen = document.getElementById("screen"); fillScreen(); blinkCursor(); document.getElementById("textButton").addEventListener("click", addText); document.getElementById("leftButton").addEventListener("click",moveLeft); document.getElementById("rightButton").addEventListener("click", moveRight); document.getElementById("deleteButton").addEventListener("click", deleteText); cursor = document.createElement("div"); setCursor(); document.getElementById("screenText").appendChild(cursor); } fillScreen = () => { let context = screen.getContext("2d"); context.fillStyle = "white"; context.lineWidth = 10; context.fillRect(10, 10, 280, 100); } blinkCursor = () =>{ clearInterval(id); id = setInterval(hide, 1000); function hide() { if(cursor != null) cursor.hidden = !cursor.hidden; } } setCursor = () =>{ cursor.id = "cursor"; cursor.style.width = "20px"; cursor.style.height = "20px"; cursor.style.backgroundColor = "black"; cursor.style.opacity = "0.7"; cursor.style.position = "absolute"; } addText = ()=>{ //code for adding new text to the screen let newElem = document.createElement("span"); newElem.id = "newText"; newElem.style.color = "red"; newElem.style.width = "3px"; newElem.style.left = "1px"; let contents = document.createTextNode(`${num++}`); newElem.appendChild(contents); document.getElementById("screenText").appendChild(newElem); //set cursor's properties using the new span element's position and width cursorPosition++; cursor.style.left = newElem.style.left; cursor.style.width = newElem.style.width; } moveLeft = ()=>{ if(cursorPosition > 0) { let prev = document.getElementById("screenText").childNodes.item(cursorPosition-1); cursorPosition--; cursor.style.left = prev.getBoundingClientRect().left; cursor.style.width = prev.offsetWidth; } } moveRight = ()=>{ let next = document.getElementById("screenText").childNodes.item(cursorPosition+1); if(next != null){ cursorPosition++; cursor.style.left = next.getBoundingClientRect().left; cursor.style.width = next.offsetWidth; } } deleteText = ()=>{ //Use the cursorPosition to determine which span element to delete. let deletedElem = document.getElementById("screenText").childNodes.item(cursorPosition); if(deletedElem != null){ deletedElem.parentNode.removeChild(deletedElem); } }
修复方案
核心问题分析
childNodes会包含空白文本节点,导致索引无法匹配目标元素- 光标初始位置未正确设置为屏幕最左侧
- 移动逻辑的边界条件和位置计算错误
- 添加文本时获取元素位置的方式无效
- 删除后未调整光标位置
修复后的完整代码
JavaScript代码
let screen; let cursor; let id; let num = 0; let cursorPosition = 0; // 初始位置对应屏幕最左侧(第一个字符前) const screenText = document.getElementById("screenText"); window.onload = () => { screen = document.getElementById("screen"); fillScreen(); cursor = document.createElement("div"); setCursor(); screenText.appendChild(cursor); blinkCursor(); document.getElementById("textButton").addEventListener("click", addText); document.getElementById("leftButton").addEventListener("click", moveLeft); document.getElementById("rightButton").addEventListener("click", moveRight); document.getElementById("deleteButton").addEventListener("click", deleteText); } fillScreen = () => { let context = screen.getContext("2d"); context.fillStyle = "white"; context.lineWidth = 10; context.fillRect(10, 10, 280, 100); } blinkCursor = () => { clearInterval(id); id = setInterval(() => { if(cursor) cursor.hidden = !cursor.hidden; }, 1000); } setCursor = () => { cursor.id = "cursor"; cursor.style.width = "2px"; cursor.style.height = "20px"; cursor.style.backgroundColor = "black"; cursor.style.opacity = "0.7"; cursor.style.position = "absolute"; cursor.style.top = "0"; // 初始位置设为屏幕最左侧(相对于screenText的left为0) cursor.style.left = "0px"; } addText = () => { const newElem = document.createElement("span"); newElem.style.color = "red"; newElem.textContent = `${num++}`; // 在光标前插入新字符 screenText.insertBefore(newElem, cursor); // 计算光标新位置:新元素的右侧 const elemRect = newElem.getBoundingClientRect(); const parentRect = screenText.getBoundingClientRect(); cursor.style.left = `${elemRect.right - parentRect.left}px`; cursor.style.height = `${elemRect.height}px`; cursorPosition++; } moveLeft = () => { // 光标在最左侧时无法左移 if (cursorPosition <= 0) return; cursorPosition--; const targetElem = screenText.children[cursorPosition]; if (!targetElem) return; const parentRect = screenText.getBoundingClientRect(); cursor.style.left = `${targetElem.getBoundingClientRect().left - parentRect.left}px`; cursor.style.height = `${targetElem.offsetHeight}px`; } moveRight = () => { // 光标在最右侧时无法右移(最后一个元素是光标) if (cursorPosition >= screenText.children.length - 1) return; cursorPosition++; const targetElem = screenText.children[cursorPosition]; if (!targetElem) return; const parentRect = screenText.getBoundingClientRect(); cursor.style.left = `${targetElem.getBoundingClientRect().left - parentRect.left}px`; cursor.style.height = `${targetElem.offsetHeight}px`; } deleteText = () => { // 光标在最左侧时无字符可删 if (cursorPosition <= 0) return; const deletedElem = screenText.children[cursorPosition]; if (deletedElem) { deletedElem.remove(); // 删除后光标左移一位 moveLeft(); } }
HTML代码(仅调整了screenText的样式,确保子元素定位正确)
<!DOCTYPE html> <html> <body> <canvas id="screen" style="background-color:black; border:1px; position:absolute; top:1em; left:40em; width:26em; height:10em;"></canvas> <div id="screenText" style="position:absolute; top:7em; left:41em; white-space: nowrap;"></div> <button id="textButton" style="width:100px; height:100px; position:absolute; top:20em; left:60em;">Press to Enter Text</button> <button id="leftButton" style="width:100px; height:100px; position:absolute; top:30em; left:50em;">←</button> <button id="rightButton" style="width:100px; height:100px; position:absolute; top:30em; left:70em;">→</button> <button id="deleteButton" style="width:100px; height:100px; position:absolute; top:40em; left:60em;">Press to delete character</button> </body> <script src="script.js"></script> </html>
修复说明
- 改用
children属性:避免空白文本节点干扰,确保索引准确对应字符元素 - 正确计算相对位置:使用
getBoundingClientRect()获取元素绝对位置,减去父元素偏移量得到光标相对于screenText的left值 - 调整光标初始位置:设置
left: 0px确保初始在屏幕最左侧 - 修正移动边界逻辑:限制光标在字符范围内移动,避免越界
- 优化删除逻辑:删除字符后自动左移光标,保持操作连贯性
- 调整光标样式:将宽度设为2px更贴近真实光标,高度匹配字符高度
内容的提问来源于stack exchange,提问作者Hamza Beder
相关产品推荐
相关产品推荐

