这篇文章发布于 2022年08月31日,星期三,22:53,归类于 JS实例。 阅读 12005 次, 今日 3 次 5 条评论
by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=10513 鑫空间-鑫生活
本文可以全文转载,无需授权,欢迎分享,欢迎点赞。
一、效果预览
先看demo:https://zhangxinxu.gitee.io/okr-at-mention/
效果如下视频所示(不动点击播放),输入 @ 字符会出现匹配的人名列表,由于是静态页面,所以请求的数据是死的,实际开发是后端根据搜索关键字动态返回的。
鼠标hover悬停在人名上,会出现 popover 提示浮层,可以查看人物相关的信息。
另外,本 JS 项目对复制或者拖拽进来的富文本进行了过滤处理,保证了了输入框里面内容的纯粹。
以及对回车行为进行了劫持,例如你希望回车不换行,而是做其他事情,也是可以的。
项目地址
项目地址:https://gitee.com/zhangxinxu/okr-at-mention
欢迎 Star,欢迎 fork,也欢迎关注我的 gitee 账号,会不定期更新一些工作中遇到的小玩意,或者突然萌发的有趣的想法。
二、使用说明
使用很简单,引入对应的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>
就可以实现对应的功能了。需要注意的是,本 JS 依赖 tributejs 这个知名的原生AT提及 JS,所以,使用的时候,需要保证 atWakaka.js 同目录下有 tributejs。
当然,不同项目对交互细节的要求也不一样,因此,也提供了对于的 API 参数接口。
语法和参数
语法为:
atWakaka(container, options, optionsTribute);
之所以叫做”wakaka”,没有什么特别的原因,纯粹是当时我就有这股莫名的冲动。
其中:
- container
- 可输入的编辑框容器元素,可以是 DOM 元素本身,也可以是元素的 id 字符串。
- options
- 可选参数,下面有注释说明。
{ url: '', // 按下回车键后,如果希望阻止默认的回车换行 // 并做一些事情,这个参数就可以用到 pressEnter: null, // 鼠标经过的提示元素,默认本组件会自己创建 // 也可以可以自己指定具体的元素 popOver: 'auto', // 鼠标经过和移出 @ 元素的处理 // event 是事件对象 // data 是 @元素 对应的请求数据 // popover 是浮层元素 onMouseOver: function (event, data, popover) {}, onMouseOut: function (event, data, popover) {} }
- optionsTribute
- 可选参数。参见 https://github.com/zurb/tribute 中的参数设置,或者源码中对应参数的使用(如下所示)。
{ // 下拉容器类名 containerClass: 'ui-at-drop-x', // 前面不需要有空格 requireLeadingSpace: false, // 是否高亮匹配,这里走的是自己匹配 // 这里其实仅 skip 有效 searchOpts: { pre: '<mark>', post: '</mark>', // 是否服务端搜索数据 skip: true }, // 动态获取匹配的值 values: valuesTribute, // 没数据时候返回的HTML内容 noMatchTemplate: function () {}, // 选中返回到输入框的HTML内容 selectTemplate: function(item) {}, // 下拉菜单每一项的HTML内容 menuItemTemplate: function (item) {} };
大家如果希望改变下拉列表的元素结构,就是使用
optionsTribute
中的可选参数进行设置。
三、实现技巧
这里有三个实现技巧我觉得值得和大家分享下。
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标签实现分隔线如何玩出花”。
2. hover出现浮层交互
Hover出现浮层的交互并不难实现,可如果是在可编辑的 div 内部呢?以及,要是是在 Vue 或者 React 等框架中的。
如果还是按照传统的实现,找到对应的 trigger 元素,然后使用组件包一下,那可能就会出现很多的问题,比方说包不了,又比方说事件绑定不上。
面对这样的场景,解决方法都是类似的,那就是委托。
将mouseover/mouseout的行为绑定在容器上,然后进行定位处理。
因为容器元素是固定的,而里面的元素是多变的,绑定在容器上就能以不变应万变,性能也更好。
具体实现参见 JS 源码。
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 中的“选区”和“光标”。
四、结语
使用<hr>
来模拟 @xxx 效果也并非完美无瑕,也是有所牺牲的,首先就是 @xxx 这样的文字内容是无法框选复制的,因为伪元素生成的文本是无法选择的。
其次,就是数据提交的时候,直接 div.textContent 是不行的,因为会丢失 @xxx 这样的信息,需要在额外处理下。
不过相比弊,带来的利想让是更大的。
好,就说这么多的,希望对遇到类似需求的小伙伴有所帮助。
……
明天9月1号小朋友开学了,本应开心才对,可惜每天要各种码,各种上报,还要天天核酸,口罩长带,想想就脑壳疼。
(本篇完)
- 划词评论与Range开发若干经验分享 (0.380)
- 小tip: 如何让contenteditable元素只能输入纯文本 (0.241)
- 盘点HTML字符串转DOM的各种方法及细节 (0.169)
- 如何使用CSS禁止元素拖拽? (0.169)
- 今日学习CSS文本自定义高亮API (0.169)
- 利用剪切板JS API优化输入框的粘贴体验 (0.127)
- 小tips: JS DOM innerText和textContent的区别 (0.127)
- 几个常见功能重合DOM API的细节差异 (0.127)
- 巧用DOM API实现HTML字符的转义和反转义 (0.127)
- 从今天开始,请叫我Node文本节点处理大师 (0.127)
- HTML5终极备忘大全(图片版+文字版) (RANDOM - 0.072)
大神,求问有没有实时高亮contenteditable里部分文字颜色的方案?
有,最近刚出的 CSS.highlights 和 ::highlight() 伪元素方法
关于富文本粘贴,是否需要考虑 xss 的场景
很实用 ToB场景有机会用到
已star