基于原生CSS+JS實現(xiàn)一個標簽輸入框
最近在項目中需要做一個標簽輸入框,還挺實用的,演示效果如下:
主要交互要求是這樣的:
- 點擊輸入框可以輸入內容。
- 按回車可以生成標簽。
- 按退格鍵可以刪除標簽。
- 點擊標簽上的關閉按鈕可以刪除標簽。
習慣了各種 react 框架或者UI庫,大家有多久沒接觸沒有原生開發(fā)了呢?有時候頁面比較簡單,沒必要引入一個完整的框架,原生實現(xiàn)就完全滿足了,一起看看吧!
一、自適應輸入框布局
不管什么組件,布局都是最重要的。這個布局分為標簽和輸入框兩個部分,假設 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; }
注意幾點實現(xiàn)技巧:
- 標簽的間隔可以用 gap 實現(xiàn)。
- 為了讓輸入框的區(qū)域鋪滿剩余空間,這里用到了flex: auto。
- 為了讓父級處于聚焦狀態(tài),這里用到了:focus-within。
效果如下:
但是這里的輸入框用 input 還是有些問題的,如下所示:
由于 input 輸入內容無法跟隨寬度自適應,所以有時候會出現(xiàn)文字被截斷的情況:
理想情況下,當輸入內容較多時,應該整體換行。如何實現(xiàn)呢?可以用普通的 div 來實現(xiàn)。
<div class="tags-content"> <tag>CSS<a class="tag-close"></a></tag> <div class="tags-input" placeholder="添加標簽"></div> </div>
可以通過添加contenteditable或者以下 CSS 來實現(xiàn):
.tags-input{ -webkit-user-modify: read-write-plaintext-only; }
這個屬性表示只允許輸入純文本,有興趣的可以參考張鑫旭的這篇文章:小tip: 如何讓contenteditable元素只能輸入純文本[1]。
這樣可以自適應內容寬度了。
二、輸入框占位提示
由于輸入框已經從 input 換成了普通的 div 標簽,并沒有 placeholder 特性。不過,我們仍然可以通過其他 CSS 特性來實現(xiàn)占位效果,當輸入框沒有內容時,就可以匹配到 :empty選擇器,然后通過偽元素::before動態(tài)生成 placeholder 內容,具體實現(xiàn)如下:
.tags-input:empty::before{ content: attr(placeholder); color: #828282; }
效果如下:
這樣就幾乎和 input 的占位效果一致了。
另外還有一種情況,如果需要僅在沒有任何標簽的情況下才顯示占位,如何實現(xiàn)呢?可以想想,在沒有任何標簽的情況下,HTML 就變成了這樣:
<div class="tags-content"> <div class="tags-input" placeholder="添加標簽"></div> </div>
這種情況,就僅剩輸入框唯一元素了,唯一元素可以通過:only-child來匹配,所以實現(xiàn)如下:
.tags-input:only-child:empty::before{ content: attr(placeholder); color: #828282; }
這樣添加一個偽類就解決了。
兩種需求都符合認知,看設計如何決定了。
三、標簽的輸入與刪除
要實現(xiàn)標簽的輸入與刪除就需要 JS 出馬了,只需要監(jiān)聽鍵盤的“回車”和“退格”兩個鍵值。需要注意的是,默認情況下,普通 contenteditable元素在回車時,會出現(xiàn)換行,如下:
因此,在監(jiān)聽鍵盤事件時需要阻止默認事件,然后動態(tài)創(chuàng)建標簽元素,通過 before添加到輸入框前面,具體實現(xiàn)如下:
// 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 = ''; } } })
這樣就能正常創(chuàng)建標簽了。
然后是標簽的刪除。
這里有兩種途徑,首先看鍵盤的刪除,具體邏輯是當輸入框內容為空時刪除標簽,很簡單,刪除的標簽就是輸入框的前面一個元素,通過previousElementSibling獲取,具體實現(xiàn)如下:
TagInput.addEventListener('keydown', function(ev) { if (ev.key === 'Backspace' && !this.innerText) { this.previousElementSibling?.remove(); // 需要判斷前一個元素是否存在 } })
然后是點擊刪除圖標的刪除。由于標簽是動態(tài)生成的,所以這里需要用事件委托的方式來添加刪除事件。
// TagContent是父級容器 TagContent.addEventListener('click', function(ev) { if (ev.target.className === 'tag-close') { ev.target.parentNode.remove(); } TagInput.focus(); //點擊任意地方輸入框都需要聚焦 })
這樣就實現(xiàn)了文章開頭的所示效果:
四、選擇框架還是原生
總結一下!
整體實現(xiàn)并不算復雜,不少交互邏輯 CSS 也可以輕松實現(xiàn),JS 也就 10 來行代碼,這里總結一下實現(xiàn)要點:
- 普通 div 元素輸入純文本可以使用 -webkit-user-modify: read-write-plaintext-only
- 普通 div 元素輸入可以自適應內容寬度
- 普通 div 元素輸入框的 placeholder 占位可以通過 :empty 結合偽元素實現(xiàn)
- 回車事件需要阻止默認事件,不然會換行
- 在一個元素的前面新增元素可以用 before 方法
- 刪除一個元素的前面一個元素,可以用 previousElementSibling.remove 方法
- 給動態(tài)生成的元素綁定事件可以用事件委托的方式
在各種框架大行其道的氛圍下,有些原生的屬性和方法可能都不太關注了,這也不失為是一種損失。當然,我本身也是各種框架都會用,特別是大型、復雜的交互頁面,一般比較小的交互,比如文章這個例子,在 ant design 中有相關的組件,也使用過,因為整體 UI 全是這種風格,設計也是按照這個設計的。后來需要單獨開發(fā)一個 chrome 插件,也用到了這樣一個交互,但是僅僅用了這樣一個組件,引入整個框架就過于累贅了,所以還是選擇直接原生實現(xiàn),簡單方便。
以上就是基于原生CSS+JS實現(xiàn)一個標簽輸入框的詳細內容,更多關于JS標簽輸入框的資料請關注腳本之家其它相關文章!
相關文章
JS擴展String.prototype.format字符串拼接的功能
這篇文章主要介紹了JS擴展String.prototype.format字符串拼接的功能,需要的朋友可以參考下2018-03-03JavaScript創(chuàng)建對象的四種常用模式實例分析
這篇文章主要介紹了JavaScript創(chuàng)建對象的四種常用模式,結合實例形式分析了javascript使用工廠模式、構造函數(shù)模式、原型模式及動態(tài)原型模式創(chuàng)建對象的相關操作技巧與注意事項,需要的朋友可以參考下2019-01-01JS/jquery實現(xiàn)一個網頁內同時調用多個倒計時的方法
這篇文章主要介紹了JS/jquery實現(xiàn)一個網頁內同時調用多個倒計時的方法,涉及js與jQuery基于定時器的時間相關操作技巧,需要的朋友可以參考下2017-04-04