You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

实现可悬停删除字符的光标遇移动问题,寻求修复建议

光标移动功能修复建议

需求说明

  • 光标初始需位于屏幕最左侧
  • 输入内容后,光标需始终处于最新字符右侧
  • 按下左右箭头按钮,光标需在相邻字符间切换悬停
  • 按下删除按钮,需删除光标悬停的字符

实现思路

点击「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;">&#8592</button>
        <button id="rightButton" style="width:100px; height:100px; position:absolute; top:30em; left:70em;">&#8594</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);
    }
}

修复方案

核心问题分析

  1. childNodes会包含空白文本节点,导致索引无法匹配目标元素
  2. 光标初始位置未正确设置为屏幕最左侧
  3. 移动逻辑的边界条件和位置计算错误
  4. 添加文本时获取元素位置的方式无效
  5. 删除后未调整光标位置

修复后的完整代码

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;">&#8592</button>
        <button id="rightButton" style="width:100px; height:100px; position:absolute; top:30em; left:70em;">&#8594</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>

修复说明

  1. 改用children属性:避免空白文本节点干扰,确保索引准确对应字符元素
  2. 正确计算相对位置:使用getBoundingClientRect()获取元素绝对位置,减去父元素偏移量得到光标相对于screenText的left值
  3. 调整光标初始位置:设置left: 0px确保初始在屏幕最左侧
  4. 修正移动边界逻辑:限制光标在字符范围内移动,避免越界
  5. 优化删除逻辑:删除字符后自动左移光标,保持操作连贯性
  6. 调整光标样式:将宽度设为2px更贴近真实光标,高度匹配字符高度

内容的提问来源于stack exchange,提问作者Hamza Beder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 20:05:56