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

contenteditable div聊天输入框插入图片表情后光标位置异常的解决问询

修复Contenteditable输入框插入表情后光标位置异常问题

问题描述

开发简易聊天功能时,采用「父span背景图+子span隐藏真实emoji」的方式实现自定义表情。但将表情插入contenteditable的输入框后,光标会出现在表情元素前方而非预期的后方:比如输入框原有内容hello,插入表情后变为hello(光标)(表情),再次插入则变为hello(光标)(表情)(表情)。

修复方案

问题根源在于原代码仅处理了表情后方存在文本节点的情况,未覆盖表情为最后一个元素的场景。只需修改表情插入后的光标定位逻辑,强制将光标移至表情元素的后方即可。

修改后的完整JS代码

const chatInput = document.querySelector("div#chat-input");
const grinningFace = document.querySelector("span#grinning-face");
const rocket = document.querySelector("span#rocket");

chatInput.addEventListener("keydown", function(event) {
    if(event.key === "Enter"){
        event.preventDefault();
        const message = document.createElement("div");
        message.textContent = chatInput.innerText;
        chatInput.innerHTML = "";
    }
})

grinningFace.addEventListener("mousedown", function(event) {
    event.preventDefault();
});

grinningFace.addEventListener("click", function() {
    const content = grinningFace.querySelector("span")

    const range = document.getSelection().getRangeAt(0);
    const emojiImage = document.createElement("span");
    const emojiImageContent = document.createElement("span");
    emojiImage.classList.add("emoji-image");
    emojiImage.style.backgroundImage = `${getComputedStyle(grinningFace).backgroundImage}`;
    emojiImage.style.backgroundSize = "contain";
    emojiImageContent.textContent = content.textContent;
    emojiImage.appendChild(emojiImageContent);

    range.insertNode(emojiImage);
    
    // 修复:强制将光标定位到表情元素后方
    const selection = window.getSelection();
    const newRange = document.createRange();
    newRange.setStartAfter(emojiImage);
    newRange.collapse(true);
    selection.removeAllRanges();
    selection.addRange(newRange);
    chatInput.focus();
});

rocket.addEventListener("mousedown", function(event) {
    event.preventDefault();
    chatInput.focus();
});

关键修改说明

原代码仅在表情后方存在文本节点时才调整光标位置,而使用setStartAfter(emojiImage)可以直接将光标锚点设置在表情元素的末尾,无论表情后面是文本节点、元素节点还是没有任何节点,都能确保光标出现在表情后方。

补充:完善火箭表情的插入逻辑

如果需要让火箭表情也具备相同功能,可以复制笑脸表情的点击事件逻辑:

rocket.addEventListener("click", function() {
    const content = rocket.querySelector("span")

    const range = document.getSelection().getRangeAt(0);
    const emojiImage = document.createElement("span");
    const emojiImageContent = document.createElement("span");
    emojiImage.classList.add("emoji-image");
    emojiImage.style.backgroundImage = `${getComputedStyle(rocket).backgroundImage}`;
    emojiImage.style.backgroundSize = "contain";
    emojiImageContent.textContent = content.textContent;
    emojiImage.appendChild(emojiImageContent);

    range.insertNode(emojiImage);
    
    // 同样设置光标到表情后方
    const selection = window.getSelection();
    const newRange = document.createRange();
    newRange.setStartAfter(emojiImage);
    newRange.collapse(true);
    selection.removeAllRanges();
    selection.addRange(newRange);
    chatInput.focus();
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 23:55:53