乐趣区

关于css:没有框架怎么办原生-CSS-JS-实现一个标签输入框

欢送关注我的公众号:前端侦探

最近在我的项目中须要做一个标签输入框,还挺实用的,演示成果如下

次要交互要求是这样的

  1. 点击输入框能够输出内容
  2. 按回车能够生成标签
  3. 按退格键能够删除标签
  4. 点击标签上的敞开按钮能够删除标签

习惯了各种 react 框架或者 UI 库,大家有多久没接触没有原生开发了呢?有时候页面比较简单,没必要引入一个残缺的框架,原生实现就齐全满足了,一起看看吧

一、自适应输入框布局

不论什么组件,布局都是最重要的。这个布局分为标签和输入框两个局部,假如 HTML 如下

<div class="tags-content">
  <tag>CSS<a class="tag-close"></a></tag>    
  <input class="tags-input" placeholder="增加标签">
</div>

简略润饰一下

.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;}

留神几点实现技巧:

  1. 标签的距离能够用 gap 实现
  2. 为了让输入框的区域铺满残余空间,这里用到了flex: auto
  3. 为了让父级处于聚焦状态,这里用到了:focus-within

成果如下

然而这里的输入框用 input 还是有些问题的,如下所示

因为 input 输出内容无奈追随宽度自适应,所以有时候会呈现文字被截断的状况

现实状况下,当输出内容较多时,应该整体换行。如何实现呢?能够用一般的 div 来实现

<div class="tags-content">
  <tag>CSS<a class="tag-close"></a></tag>    
  <div class="tags-input" placeholder="增加标签"></div>
</div>

能够通过增加 contenteditable 或者以下 CSS 来实现

.tags-input{-webkit-user-modify: read-write-plaintext-only;}

这个属性示意只容许输出纯文本,有趣味的能够参考张鑫旭的这篇文章:小 tip: 如何让 contenteditable 元素只能输出纯文本

这样能够自适应内容宽度了

二、输入框占位提醒

因为输入框曾经从 input 换成了一般的 div 标签,并没有 placeholder 个性。不过,咱们依然能够通过其余 CSS 个性来实现占位成果,当输入框没有内容时,就能够匹配到 :empty选择器,而后通过伪元素 ::before 动静生成 placeholder 内容,具体实现如下

.tags-input:empty::before{content: attr(placeholder);
    color: #828282;
}

成果如下

这样就简直和 input 的占位成果统一了

另外还有一种状况,如果须要 仅在没有任何标签的状况下才显示占位,如何实现呢?能够想想,在没有任何标签的状况下,HTML 就变成了这样

<div class="tags-content">
  <div class="tags-input" placeholder="增加标签"></div>
</div>

这种状况,就仅剩输入框惟一元素了 ,惟一元素能够通过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 + '<a class="kalos-tag-close"></a>';
      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(); // 点击任意中央输入框都须要聚焦})

这样就实现了文章结尾的所示成果

残缺代码能够拜访:input-tag

四、抉择框架还是原生?总结一下

整体实现并不算简单,不少交互逻辑 CSS 也能够轻松实现,JS 也就 10 来行代码,这里总结一下实现要点

  1. 一般 div 元素输出纯文本能够应用 -webkit-user-modify: read-write-plaintext-only
  2. 一般 div 元素输出能够自适应内容宽度
  3. 一般 div 元素输入框的 placeholder 占位能够通过 :empty 联合伪元素实现
  4. 回车事件须要阻止默认事件,不然会换行
  5. 在一个元素的后面新增元素能够用 before 办法
  6. 删除一个元素的后面一个元素,能够用 previousElementSibling.remove 办法
  7. 给动静生成的元素绑定事件能够用事件委托的形式

在各种框架大行其道的气氛下,有些原生的属性和办法可能都不太关注了,这也不失为是一种损失。当然,我自身也是各种框架都会用,特地是大型、简单的交互页面,个别比拟小的交互,比方文章这个例子,在 ant design 中有相干的组件,也应用过,因为整体 UI 全是这种格调,设计也是依照这个设计的。起初须要独自开发一个 chrome 插件,也用到了这样一个交互,然而仅仅用了这样一个组件,引入整个框架就过于累赘了,所以还是抉择间接原生实现,简略不便。

最初,如果感觉还不错,对你有帮忙的话,欢送点赞、珍藏、转发❤❤❤

欢送关注我的公众号:前端侦探

退出移动版