输入框实现@提及项目
一、效果预览
先看demo: https://zhangxinxu.gitee.io/okr-at-mention/
项目地址
项目地址: https://gitee.com/zhangxinxu/okr-at-mention
使用说明
使用很简单,引入对应的CSS和JS,然后按照暴露的方法进行调用就可以了。
例如:
<link rel="stylesheet" href="./src/atMention.css">
假设有容器元素(也就是输入框元素):
<div id="container"></div>
则对应的 JavaScript 代码则可以这么使用:
<script type="module">
import atWakaka from './src/atWakaka.js';
atWakaka('container', {
url: './cgi/data.json'
});
</script>
二、实现技巧
这里有三个实现技巧我觉得值得和大家分享下。
1. @描述整删整加
在可编辑的 div 元素中,要想让里面某段文字不能编辑,有个简单的方法,就是设置 contenteditable="false"
,例如,下面 HTML 代码中的 <span>
元素就无法编辑,里面的文字五毒不侵。
<div contenteditable="true">
我是文字,可逐个删除,<span style="display:inline-block;" contenteditable="false">我只能整体删除</span>!
</div>
然后,上面的实现看似完美,实际上有个很头疼的问题,设置了 contenteditable="false"
的元素后面是不能光标定位的,这就导致我想定位在 @xxx 的后面,然后删除,做不到,要么 JS 实时观察并改变光标位置,要么在后面插入一个零宽空格。
上面无论哪个方法,成本都比较高。
在本 JS 的实现中,创新的采用了单标签元素 <hr>
来模拟 @xxx 效果,由于单标签元素本身内容 textContent 是空的,因此,无需设置 contenteditable="false"
,就能实现删除只能删整体。
在所有浏览器中,<hr>
元素都支持 ::before/::after
伪元素,因此,可以创建丰富的内容和图形生成,有兴趣的同学可以看看我之前的这篇文章:“666,看hr标签实现分隔线如何玩出花”[1]。
2. hover出现浮层交互
Hover出现浮层的交互并不难实现,可如果是在可编辑的 div 内部呢?以及,要是是在 Vue 或者 React 等框架中的。
如果还是按照传统的实现,找到对应的 trigger 元素,然后使用组件包一下,那可能就会出现很多的问题,比方说包不了,又比方说事件绑定不上。
面对这样的场景,解决方法都是类似的,那就是委托。
将mouseover
/mouseout
的行为绑定在容器上,然后进行定位处理。
因为容器元素是固定的,而里面的元素是多变的,绑定在容器上就能以不变应万变,性能也更好。
3. 复制粘贴或者拖拽进去的都是纯文本
富文本编辑机纯手打应该是打不了富文本的,但是粘贴和拖拽却能将富文本弄进去。
有没有什么办法过滤富文本,让用户粘贴或拖拽的内容默认就是纯文本呢?
有的哈!
浏览器其实提供了原生的能力。
包括获取剪切板里面的文本和富文本内容,获取拖拽内容中的文本和富文本,此时,我们就可以阻止默认行为,将纯文本内容插入就可以了。
来一招神不知鬼不觉的移花接木。
相关代码如下所示(拖拽和粘贴二合一了,因为 API 类似):
const doStripHtml = function (event) {
var dataInput = event.clipboardData || event.dataTransfer;
// 富文本
let htmlOrigin = dataInput.getData('text/html');
// 纯文本
let textOrigin = dataInput.getData('text');
// 如果包含富文本
if (htmlOrigin) {
// 手动插入
// 阻止默认的行为
event.preventDefault();
// 只插入纯文本
let lastRange = window.getSelection().getRangeAt(0);
const newNode = document.createTextNode(textOrigin);
lastRange.deleteContents();
lastRange.insertNode(newNode);
lastRange.setStartAfter(newNode);
event.target.focus();
}
};
其中,插入内容这段代码对于任意的富文本编辑器都是受用的,关于光标和选区更多知识可以参见这篇公众号文章:Web 中的“选区”和“光标”[3]。
结语
使用 <hr>
来模拟 @xxx
效果也并非完美无瑕,也是有所牺牲的,首先就是 @xxx
这样的文字内容是无法框选复制的,因为伪元素生成的文本是无法选择的。
其次,就是数据提交的时候,直接 div.textContent
是不行的,因为会丢失 @xxx
这样的信息,需要在额外处理下。
不过相比弊,带来的利想让是更大的。