基于原生CSS+JS实现一个标签输入框
目录
- 一、自适应输入框布局
- 二、输入框占位提示
- 三、标签的输入与删除
- 四、选择框架还是原生

文章图片
主要交互要求是这样的:
- 点击输入框可以输入内容。
- 按回车可以生成标签。
- 按退格键可以删除标签。
- 点击标签上的关闭按钮可以删除标签。
一、自适应输入框布局 不管什么组件,布局都是最重要的。这个布局分为标签和输入框两个部分,假设 HTML 如下:
CSS
简单修饰一下:
.tags-content{display: flex; flex-wrap: wrap; align-items: flex-start; gap: 6px; width: 400px; box-sizing: border-box; padding: 8px 12px; border: 1px solid #D9D9D9; border-radius: 4px; font-size: 16px; line-height: 24px; color: #333; outline-color: #4F46E5; overflow: auto; cursor: text; }tag{display: flex; align-items: center; padding: 4px 0 4px 8px; font-size: 16px; line-height: 24px; background: #F5F5F5; color: rgba(0, 0, 0, 0.85); cursor: default; }tag-close{width: 18px; height: 18px; cursor: pointer; background: url("data:image/svg+xml,%3Csvg width='10' height='10' viewBox='0 0 10 10' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M5.578 5l2.93-3.493a.089.089 0 0 0-.068-.146h-.891a.182.182 0 0 0-.137.064l-2.417 2.88-2.416-2.88a.178.178 0 0 0-.137-.064h-.89a.089.089 0 0 0-.069.146L4.413 5l-2.93 3.493a.089.089 0 0 0 .068.146h.89a.182.182 0 0 0 .138-.064l2.416-2.88 2.417 2.88c.033.04.083.064.137.064h.89a.089.089 0 0 0 .069-.146l-2.93-3.493z' fill='%23000' fill-opacity='.45'/%3E%3C/svg%3E") center no-repeat; }.tags-input{flex: auto; border: 0; outline: 0; padding: 4px 0; line-height: 24px; font-size: 16px; }.tags-content:focus-within,.tags-content:active{outline: auto #4F46E5; }
注意几点实现技巧:
- 标签的间隔可以用 gap 实现。
- 为了让输入框的区域铺满剩余空间,这里用到了flex: auto。
- 为了让父级处于聚焦状态,这里用到了:focus-within。

文章图片
但是这里的输入框用 input 还是有些问题的,如下所示:

文章图片
由于 input 输入内容无法跟随宽度自适应,所以有时候会出现文字被截断的情况:

文章图片
【基于原生CSS+JS实现一个标签输入框】理想情况下,当输入内容较多时,应该整体换行。如何实现呢?可以用普通的 div 来实现。
CSS
可以通过添加contenteditable或者以下 CSS 来实现:
.tags-input{-webkit-user-modify: read-write-plaintext-only; }
这个属性表示只允许输入纯文本,有兴趣的可以参考张鑫旭的这篇文章:小tip: 如何让contenteditable元素只能输入纯文本[1]。

文章图片
这样可以自适应内容宽度了。
二、输入框占位提示 由于输入框已经从 input 换成了普通的 div 标签,并没有 placeholder 特性。不过,我们仍然可以通过其他 CSS 特性来实现占位效果,当输入框没有内容时,就可以匹配到 :empty选择器,然后通过伪元素::before动态生成 placeholder 内容,具体实现如下:
.tags-input:empty::before{content: attr(placeholder); color: #828282; }
效果如下:

文章图片
这样就几乎和 input 的占位效果一致了。
另外还有一种情况,如果需要仅在没有任何标签的情况下才显示占位,如何实现呢?可以想想,在没有任何标签的情况下,HTML 就变成了这样:
这种情况,就仅剩输入框唯一元素了,唯一元素可以通过:only-child来匹配,所以实现如下:
.tags-input:only-child:empty::before{content: attr(placeholder); color: #828282; }
这样添加一个伪类就解决了。

文章图片
两种需求都符合认知,看设计如何决定了。
三、标签的输入与删除 要实现标签的输入与删除就需要 JS 出马了,只需要监听键盘的“回车”和“退格”两个键值。需要注意的是,默认情况下,普通 contenteditable元素在回车时,会出现换行,如下:

文章图片
因此,在监听键盘事件时需要阻止默认事件,然后动态创建标签元素,通过 before添加到输入框前面,具体实现如下:
// TagInput是输入框TagInput.addEventListener('keydown', function(ev) {if (ev.key === 'Enter') {ev.preventDefault()if (this.innerText) { // 输入框内容通过 innerText 获取const tag = document.createElement('TAG'); tag.innerHTML = this.innerText + ''; this.before(tag); this.innerText = ''; }}})
这样就能正常创建标签了。

文章图片
然后是标签的删除。
这里有两种途径,首先看键盘的删除,具体逻辑是当输入框内容为空时删除标签,很简单,删除的标签就是输入框的前面一个元素,通过previousElementSibling获取,具体实现如下:
TagInput.addEventListener('keydown', function(ev) {if (ev.key === 'Backspace' && !this.innerText) {this.previousElementSibling?.remove(); // 需要判断前一个元素是否存在}})
然后是点击删除图标的删除。由于标签是动态生成的,所以这里需要用事件委托的方式来添加删除事件。
// TagContent是父级容器TagContent.addEventListener('click', function(ev) {if (ev.target.className === 'tag-close') {ev.target.parentNode.remove(); }TagInput.focus(); //点击任意地方输入框都需要聚焦})
这样就实现了文章开头的所示效果:

文章图片
四、选择框架还是原生 总结一下!
整体实现并不算复杂,不少交互逻辑 CSS 也可以轻松实现,JS 也就 10 来行代码,这里总结一下实现要点:
- 普通 div 元素输入纯文本可以使用 -webkit-user-modify: read-write-plaintext-only
- 普通 div 元素输入可以自适应内容宽度
- 普通 div 元素输入框的 placeholder 占位可以通过 :empty 结合伪元素实现
- 回车事件需要阻止默认事件,不然会换行
- 在一个元素的前面新增元素可以用 before 方法
- 删除一个元素的前面一个元素,可以用 previousElementSibling.remove 方法
- 给动态生成的元素绑定事件可以用事件委托的方式
以上就是基于原生CSS+JS实现一个标签输入框的详细内容,更多关于JS标签输入框的资料请关注脚本之家其它相关文章!
推荐阅读
- 腾讯研发体系新产品,云原生+低代码佐你easy|腾讯研发体系新产品,云原生+低代码佐你easy coding
- 【N32G457】基于RT-Thread和N32G457的墨水屏日历
- 毕业设计|基于springboot的学生毕业选题管理系统
- 毕业设计|基于Springboot的心理健康平台
- 毕业设计|基于Springboot的医院管理系统
- 基于Python实现对比Exce的工具
- 基于springboot与axios的整合问题
- Golang原生rpc(rpc服务端源码解读)
- 云栖号技术分享|专访李飞飞 (从清华附中高材生到阿里飞刀,一口井钻出「云原生」)
- 我对云原生软件架构的观察与思考