Welcome to my public account: front-end detective
Recently, I need to make a label input box in the project, which is quite practical. The demonstration effect is as follows
The main interaction requirements are as follows
- Click the input box to enter the content
- Press enter to generate tags
- Press backspace to delete a label
- Click the close button on the label to delete the label
Accustomed to various react frameworks or UI libraries, how long have you been in contact with no native development? Sometimes the page is relatively simple, there is no need to introduce a complete framework, the native implementation is completely satisfied, let's take a look
1. Adaptive input box layout
Regardless of the component, the layout is the most important. This layout is divided into two parts: label and input box, assuming the HTML is as follows
<div class="tags-content">
<tag>CSS<a class="tag-close"></a></tag>
<input class="tags-input" placeholder="添加标签">
</div>
Just make up
.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;
}
Note a few implementation tips:
- The spacing of labels can be achieved with
gap
- In order to fill the area of the input box with the remaining space,
flex: auto
is used here - In order to keep the parent in focus,
:focus-within
is used here
The effect is as follows
But there are still some problems with the input box here, as shown below
Since the input content cannot follow the width adaptively, sometimes the text will be truncated
Ideally, when there is a lot of input, it should wrap as a whole. How to achieve it? It can be done with a normal div
<div class="tags-content">
<tag>CSS<a class="tag-close"></a></tag>
<div class="tags-input" placeholder="添加标签"></div>
</div>
This can be achieved by adding contenteditable
or the following CSS
.tags-input{
-webkit-user-modify: read-write-plaintext-only;
}
This attribute indicates that only plain text is allowed. If you are interested, you can refer to this article by Zhang Xinxu: Tip : How to make the contenteditable element only enter plain text
This can adapt to the content width
2. Input box placeholder prompt
Since the input box has been replaced by a normal div tag, there is no placeholder feature. However, we can still achieve the placeholder effect through other CSS features. When the input box has no content, it can match the :empty
selector, and then generate the placeholder dynamically through the pseudo element ::before
Content, the specific implementation is as follows
.tags-input:empty::before{
content: attr(placeholder);
color: #828282;
}
The effect is as follows
This is almost the same as the placeholder effect of input
There is another situation, if you need to display the placeholder only when there is no label , how to achieve it? Think about it, without any tags, HTML becomes like this
<div class="tags-content">
<div class="tags-input" placeholder="添加标签"></div>
</div>
In this case, only the only element of the input box is left , and the only element can be matched by only-child
, so the implementation is as follows
.tags-input:only-child:empty::before{
content: attr(placeholder);
color: #828282;
}
Adding a pseudo-class like this solves the problem
Both requirements are in line with cognition, depending on how the design is determined
3. Input and deletion of labels
To realize the input and deletion of tags, JS is required, and only two key values of "enter" and "backspace" need to be monitored on the keyboard. It should be noted that, by default, the normal contenteditable
element will have a newline when the carriage returns, as follows
Therefore, when listening to keyboard events, you need to prevent the default event, and then dynamically create a label element, which is added to the front of the input box through before
. The specific implementation is as follows
// 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 = '';
}
}
})
This will create the label normally
Then there is the removal of the tag.
There are two ways here. First, look at the deletion of the keyboard. The specific logic is to delete the label when the content of the input box is empty. It is very simple. The deleted label is the previous element of the input box, which is obtained through previousElementSibling
. The implementation is as follows
TagInput.addEventListener('keydown', function(ev) {
if (ev.key === 'Backspace' && !this.innerText) {
this.previousElementSibling?.remove(); // 需要判断前一个元素是否存在
}
})
Then click the delete icon to delete. Since the tags are dynamically generated, we need to use event delegation to add and delete events here.
// TagContent是父级容器
TagContent.addEventListener('click', function(ev) {
if (ev.target.className === 'tag-close') {
ev.target.parentNode.remove();
}
TagInput.focus(); //点击任意地方输入框都需要聚焦
})
This achieves the effect shown at the beginning of the article
The full code can be accessed at: input-tag
Fourth, choose framework or native? in conclusion
The overall implementation is not complicated, a lot of interactive logic CSS can also be easily implemented, JS only 10 lines of code, here is a summary of the implementation points
- Ordinary div elements can use -webkit-user-modify: read-write-plaintext-only to enter plain text
- Normal div element input can adapt to content width
- The placeholder of the input box of the ordinary div element can be realized by :empty combined with the pseudo element
- The carriage return event needs to block the default event, otherwise it will wrap
- To add an element in front of an element, you can use the before method
- To remove the previous element of an element, you can use the previousElementSibling.remove method
- Binding events to dynamically generated elements can use event delegation
In the atmosphere where various frameworks are popular, some native properties and methods may not be paid much attention to, which is also a loss. Of course, I also use various frameworks myself, especially for large and complex interactive pages, which generally have relatively small interactions. For example, in the example of this article, there are related components in ant design, and I have also used them, because the overall UI is all this A style, the design is also designed according to this. Later, it was necessary to develop a chrome plug-in separately, and such an interaction was also used, but it would be too cumbersome to introduce the entire framework with only such a component, so we chose to directly implement it natively, which is simple and convenient.
Finally, if you think it's good and helpful to you, please like, bookmark, and forward ❤❤❤
Welcome to my public account: front-end detective
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。