微信聊天输入框的解决方案

想知道微信聊天输入框是怎么实现的

阅读 5.4k
1 个回答
新手上路,请多包涵
IM输入框 技术实现思路 Web版
日常水帖前奏

不知道现在对于你这边还有没有用, 正好我们最近在做一个Web版的IM即时通讯工具.其中就有涉及到输入框的处理...

主要的有一些小困难的可能是对于文本和图片已经文件的混排.
例如: 在文本中插Emoji 插入图片 插入文件 或者是输入link的时候需要自动解析等等


主要技术点

对于这样的需求其实是很合理的, 毕竟谁都不喜欢选择个文件都不确定一下就自动发送出去了; 要是一不小心发错了那不是很尴尬..

来说说技术技术点吧:
对于传统 InputTextarea 那是不能这样的满足了, 那看样子我们现在需要做的是实现一个可编辑的输入框了, 要做到这一点有两种方式.

实现一个输入框

CSS实现
给元素加上user-modify属性就可以对元素内容进行编辑了, 参考MDN地址 user-modify, 不过MDN也标注很清楚了This property has been replaced by the contenteditable attribute. 这种方式已经被contenteditable所替代了, 所以就不推荐了...
当然如果你想了解, 可以查看 张鑫旭博客

contenteditable实现
这是一个全局的枚举属性, 只需要你在想编辑的元素上加上 contenteditable=true 就可以了.就像这样

<div contenteditable></div>

那么你就可以在这个div上随意编写内容了, 就感觉很神奇...
参考文档: MDN Contenteditable

看样子输入框是可以搞定了, 那么接下来就应该像输入框里面插入东西了~~~~

怎么像输入框插入Emoji或者是图片

这里也需要用到两个东西, 第一个是document.execCommand 第二个是 Window.getSelection()

document.execCommand
这个东西允许运行命令来操纵可编辑内容区域的元素, 就意味着你如果是一个可编辑内容区域, 他就可以进行一些像 加粗 插入节点/文本 创建链接 包括 缩进等操作, 看样子这是我们需要的东西哈...
参考链接: MDN execCommand

比如粘贴的时候我只需要纯文本, 你可以这样...

 // 粘贴事件
    const handleOnPaste = (e) => {
        e.preventDefault();
        const text = e.clipboardData.getData('text');
        document.execCommand('insertText', false, text);
    };

当然你也可以使用他来插入一些节点, 具体可以查看 MDN,
那么看起来好像一切都可以实现了...
其实不然, 这家伙也是需要在 可以编辑 并 聚焦的时候 在可以插入的, 有什么问题呢?

插入图片和emoji
我们知道, 如果我们需要选择文件时候可以使用 input 并将 type="file" 就可以实现文件上传.不过这家伙一般是张这样的
upload.png

如果你喜欢这样的样式, 恭喜你可以使用上面 document.execCommand 插入, 不过我猜很多人是不喜欢的, 那我看样子我们处理自己实现 可输入区域 还需实现 文件上传 呀.

额, 这倒是不用...
我们可以将这个原有的 input 隐藏了, 然后我们自己搞一个元素, 点击他的时候打开 input 的上传是不是就可以了呢?

import React, { useRef } from 'react';

function test() {
    const imgUpload = useRef('');

    const handleClickImage = () => {
        imgUpload.current.click();
    };

    const imgOnChange = () => {
        // 文件操作
    };

    return (
        <div>
            <button type="button" onClick={handleClickImage}>image</button>
            <input type="file" hidden ref={imgUpload} onChange={imgOnChange} />
        </div>
    );
}

export default test;

但是你会发现上面那玩意不起作用了呀, 解决方案...

Window.getSelection()
MDN的说明是 该对象表示用户选择的文本范围或插入符号的当前位置.
我们可以利用 Window.getSelection() 下面的 getRangeAt(0)来获取需要插入的位置.

并使用下面方式来插入一个节点

const img = document.createElement('img')
img.src = 'xxx'

const range = Window.getSelection().getRangeAt(0)
ange.insertNode(img);
其他问题

如果你遇到点击失去焦点,插入错误的情况...
很简单, 只需要取输入框的range对象就可以了

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题