Libon

实现一个标签输入框

使用原生 CSS + JS 实现一个可交互的标签输入框

代码如下:

<div class="tags-content">
  <div class="tags-input" placeholder="添加标签"></div>
</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;
}
/* 设置了 editable 的输入框 */
.tags-input {
  flex: auto;
  border: 0;
  outline: 0;
  padding: 4px 0;
  line-height: 24px;
  font-size: 16px;
  /* 可以让元素变得可输入,且只能输入纯文本 */
  -webkit-user-modify: read-write-plaintext-only;
}

/* 占位文本,当只有输入框没有标签的时候才展示 */
.tags-input:only-child:empty::before {
  content: attr(placeholder);
  color: #828282;
}
/* 当外层容器获取到焦点时展示边框 */
.tags-content:focus-within,
.tags-content:active {
  outline: auto #4F46E5;
}
const TagContent = document.querySelector('.tags-content')
const TagInput = document.querySelector('.tags-input')

// 监听按下的 回车键 和 删除键
TagInput.addEventListener('keydown', function (ev) {
  if (ev.key === 'Enter') {
    ev.preventDefault()
    // 如果是回车键则阻止默认行为,防止内容换行
    if (this.innerText) { // 输入框内容通过 innerText 获取
      const tag = Object.assign(document.createElement('span'), { className: 'tag' })
      // 设置标签内容
      tag.innerHTML = this.innerText + '<a class="tag-close"></a>'
      // 将标签插入到输入框之前
      this.before(tag)
      // 清空当前的输入框内容
      this.innerText = ''
    }
  } else 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() //点击任意地方输入框都需要聚焦
})
cd ../