Vue實(shí)現(xiàn)輸入框@功能的示例代碼
前言
前幾篇文章中分別介紹了如何實(shí)現(xiàn)聊天輸入框的雙向綁定、回車鍵發(fā)送、粘貼文本圖片等功能,本著完善輸入框的目的,文本重點(diǎn)介紹聊天框如何實(shí)現(xiàn)@功能。
文章回顧:
Vue實(shí)現(xiàn)contenteditable元素雙向綁定的方法詳解
Vue實(shí)現(xiàn)輸入框回車發(fā)送和粘貼文本與圖片功能
首先需要先理清思路:
- 成員列表組件,需要根據(jù)光標(biāo)的位置調(diào)整,點(diǎn)擊成員項(xiàng)時(shí)回調(diào)成員信息
- 獲取光標(biāo)的位置坐標(biāo)(x值,y值)
- 輸入框失焦時(shí)記錄光標(biāo)
- 成員列表回調(diào)時(shí),將成員信息插入到光標(biāo)位置,在此之前需要先恢復(fù)之前的光標(biāo)
- 將光標(biāo)移動(dòng)到新的位置
既然有了思路,那么就可以一步一步實(shí)現(xiàn)。
成員列表
創(chuàng)建
實(shí)現(xiàn)成員列表的方式比較簡(jiǎn)單,其實(shí)就是一個(gè)列表,一個(gè)簡(jiǎn)單的v-for循環(huán)就可以搞定,點(diǎn)擊時(shí)將當(dāng)前選擇的成員項(xiàng)回調(diào)給父組件。
新增一個(gè)AtPop.vue
文件:
<template> <div class="at-pop-index"> <div v-for="(item, index) in listData" :key="index" class="at-pop-item" @click.stop="clickItem(item)">{{item.name}}</div> </div> </template> <script> export default { props: { // 傳入需要被@的成員數(shù)組 listData: { type: Array, default: () => [] } }, methods: { clickItem(item) { this.$emit('onSelect', item); // 調(diào)用父組件處理成員選擇的方法,并回傳當(dāng)前選擇項(xiàng) } } } </script>
使用
在父組件中,注冊(cè)并使用成員列表組件。我們需要在用戶輸入@的時(shí)候彈出成員列表,因此需要監(jiān)聽(tīng)用戶的輸入,然后在用戶選擇成員后需要關(guān)閉。關(guān)鍵是獲取光標(biāo)位置,這個(gè)由輸入框獲取,在父組件只需要使用即可。
// 核心代碼 ... // 選擇成員時(shí)插入數(shù)據(jù),并關(guān)閉彈窗 onSelect(item) { console.log('onSelect', item); this.$refs.inputBox.insertContent(`${item.name} `); // 有空格 this.isShowAt = false; }, // 輸入框輸入時(shí)回調(diào)函數(shù) inputFunc(data, event) { console.log('inputFunc', data, event); if (event.data === '@') { this.isShowAt = true; // 顯示彈窗 this.$nextTick(() => { let dom = document.getElementsByClassName('at-pop-index')[0]; // 獲取成員列表彈窗,需要放在nextTick中 // 設(shè)置位置 dom.style.position = 'fixed'; dom.style.left = Math.floor(data.left + 10) + 'px'; dom.style.top = Math.floor(data.top) + 'px'; dom.style.zIndex = 9999; }) } else { this.isShowAt = false; } }, ...
輸入框
輸入框需要處理光標(biāo)位置的獲取、將值插入到光標(biāo)的位置等,是本次功能實(shí)現(xiàn)的核心。
當(dāng)輸入框聚焦時(shí),我們會(huì)看到光標(biāo)閃動(dòng),想要獲取光標(biāo)的位置以便于插入數(shù)據(jù),則需要借助Selection
對(duì)象。Selection
表示用戶選擇的一段文本范圍或者插入數(shù)據(jù)的當(dāng)前位置。既然是獲取選取范圍,那當(dāng)前選擇范圍的index=0
就是當(dāng)前光標(biāo)的位置。我們想要實(shí)現(xiàn)的效果是成員列表跟隨光標(biāo)移動(dòng),因此就需要獲取光標(biāo)的坐標(biāo)值。
獲取光標(biāo)的坐標(biāo)
let range = window.getSelection().getRangeAt(0); // 獲取當(dāng)前光標(biāo) let position = range.getBoundingClientRect(); // 獲取當(dāng)前光標(biāo)的位置
getBoundingClientRect()
方法會(huì)返回一個(gè)DOMRect
矩形對(duì)象,其包含矩形區(qū)域的坐標(biāo)值。將獲取到的坐標(biāo)值回調(diào)給父組件的方法,顯示成員列表。
當(dāng)我們?cè)谳斎肟蜉斎隌的時(shí)候,頁(yè)面會(huì)出現(xiàn)成員列表,此時(shí)輸入框還是聚焦的。但是如果我們點(diǎn)擊了成員列表的某一項(xiàng),此時(shí)輸入框已經(jīng)失焦了,雖然我們可以獲取選擇的成員并插入,如果只是簡(jiǎn)單的字符串追加的話,光標(biāo)會(huì)在下次輸入時(shí)默認(rèn)定位到開(kāi)頭;或者我們需要在中間插入選擇的成員,會(huì)發(fā)現(xiàn)沒(méi)有位置可以插入。因此我們需要在失焦的時(shí)候先保存當(dāng)前光標(biāo),并在插入時(shí)還原光標(biāo)。
保存光標(biāo)
// DivEditable.vue // 失焦 inputBlur(event) { this.selection = this.saveSelection(); this.$emit('blurFunc', event); }, // 失焦時(shí)保存光標(biāo) saveSelection() { if (!window.getSelection) { return null; } let sel = window.getSelection(); if (sel.getRangeAt && sel.rangeCount) { return sel.getRangeAt(0); } },
光標(biāo)暫存了,我們需要將插入成員封裝成方法,并可以給父組件調(diào)用,這樣父組件在獲取到成員信息后就可以直接調(diào)用。接下來(lái)需要使用上面已經(jīng)保存的光標(biāo)位置:
插入文本
// DivEditable.vue // 恢復(fù)光標(biāo) restoreSelection(range) { if (range) { let sel = window.getSelection(); sel.removeAllRanges(); sel.addRange(range); } }, // 插入數(shù)據(jù) insertContent(value) { // this.$refs.editor.focus(); let range, node; this.restoreSelection(this.selection); // 還原失焦前的光標(biāo)位置 range = window.getSelection().getRangeAt(0); range.collapse(false); // 光標(biāo)移動(dòng)到最后 ? node = range.createContextualFragment(value); let child = node.lastChild; console.log('lastChild', child); range.insertNode(node); // 將光標(biāo)的起始位置設(shè)置在當(dāng)前插入的元素后面 if (child) { range.setEndAfter(child); range.setStartAfter(child); } ? let sel = window.getSelection(); sel.removeAllRanges(); sel.addRange(range); this.$emit('input', this.$refs.editor.innerHTML); },
其實(shí)到這里就基本實(shí)現(xiàn)了@功能,但是還有一個(gè)問(wèn)題,當(dāng)輸入框失焦時(shí)回調(diào)了父組件的blurFunc
方法,導(dǎo)致成員列表關(guān)閉了,但是數(shù)據(jù)還沒(méi)有拿到。處理這種問(wèn)題,有兩種思路:
- 使用setTimeout設(shè)置定時(shí)器,延后執(zhí)行關(guān)閉成員列表操作
- 修改取數(shù)邏輯為異步操作,等到數(shù)據(jù)拿到后才關(guān)閉成員列表
簡(jiǎn)單點(diǎn),就采用setTimeout實(shí)現(xiàn)。
// InputBox.vue blurFunc(event) { // 失焦時(shí)延時(shí)關(guān)閉彈窗,避免還未拿到數(shù)據(jù) if (this.isShowAt) { setTimeout(() => { this.isShowAt = false; }, 500); } },
運(yùn)行結(jié)果
輸入框輸入@,在光標(biāo)位置附近彈出成員列表
選擇成員后,將成員信息插入到輸入框中
總結(jié)
本文介紹了實(shí)現(xiàn)輸入框@功能的方法,簡(jiǎn)單易上手
主要使用了Selection
和Range
對(duì)象的相關(guān)方法,完成對(duì)光標(biāo)的處理以及輸入的插入
本文實(shí)現(xiàn)的@功能,無(wú)法刪除時(shí)整體刪除。目前有兩種思路:將@xxx
轉(zhuǎn)換為圖片并插入到輸入框,筆者簡(jiǎn)單寫了demo驗(yàn)證了一下,效率不高,且會(huì)有卡頓;另一種方式就是監(jiān)聽(tīng)退格鍵,刪除時(shí)判斷刪除對(duì)象是否被包含著有意義的@xxx
中,如果是則整體刪除,如果不是則默認(rèn)的方式刪除,這種方式筆者沒(méi)有嘗試,難度比較大。
至此,輸入框的功能就基本完善了。需要看完整代碼的可移步 項(xiàng)目地址
以上就是Vue實(shí)現(xiàn)輸入框@功能的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于Vue輸入框@功能的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue2和elementUI?實(shí)現(xiàn)落日余暉登錄頁(yè)和滑塊校驗(yàn)功能
這篇文章主要介紹了vue2和elementUI打造落日余暉登錄頁(yè)和滑塊校驗(yàn),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2023-06-06vue3與elementui封裝一個(gè)便捷Loading
這篇文章主要介紹了vue3與elementui封裝一個(gè)便捷Loading,,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09詳解Vue中Computed與watch的用法與區(qū)別
這篇文章主要介紹了Vue中computed和watch的使用與區(qū)別,文中通過(guò)示例為大家進(jìn)行了詳細(xì)講解,對(duì)Vue感興趣的同學(xué),可以學(xué)習(xí)一下2022-04-04Vue Element前端應(yīng)用開(kāi)發(fā)之功能點(diǎn)管理及權(quán)限控制
在一個(gè)業(yè)務(wù)管理系統(tǒng)中,如果我們需要實(shí)現(xiàn)權(quán)限控制功能,我們需要定義好對(duì)應(yīng)的權(quán)限功能點(diǎn),然后在界面中對(duì)界面元素的功能點(diǎn)進(jìn)行綁定,這樣就可以在后臺(tái)動(dòng)態(tài)分配權(quán)限進(jìn)行動(dòng)態(tài)控制了,權(quán)限功能點(diǎn)是針對(duì)角色進(jìn)行控制的,也就是簡(jiǎn)稱RBAC(Role Based Access Control)。2021-05-05vue項(xiàng)目如何解決數(shù)字計(jì)算精度問(wèn)題
這篇文章主要介紹了vue項(xiàng)目如何解決數(shù)字計(jì)算精度問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10Vue數(shù)據(jù)雙向綁定的實(shí)現(xiàn)方式講解
Vue數(shù)據(jù)雙向綁定原理:Vue內(nèi)部通過(guò)Object.defineProperty方法屬性攔截的方式,把data對(duì)象里每個(gè)數(shù)據(jù)的讀寫轉(zhuǎn)化成getter/setter,當(dāng)數(shù)據(jù)變化時(shí)通知視圖更新2022-08-08使用Vue3+PDF.js實(shí)現(xiàn)PDF預(yù)覽功能
項(xiàng)目中有一個(gè)需要預(yù)覽下載pdf的需求,網(wǎng)上找了很久,決定使用 pdf.js 完成,下面這篇文章主要給大家介紹了關(guān)于使用Vue3+PDF.js實(shí)現(xiàn)PDF預(yù)覽功能的相關(guān)資料,需要的朋友可以參考下2022-12-12vue?proxytable代理根路徑的同時(shí)增加其他代理方式
這篇文章主要介紹了vue?proxytable代理根路徑的同時(shí)增加其他代理方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-04-04