摘要:最近為實現一個新功能弄的焦頭爛額的實現,在實現后寫下些心得,供以后會跳入這坑的同志們參考。選人實現主要涉及步驟為。需要修改的代碼,保存選區以及光標信息,用于獲取在光標焦點離開前,光標的位置刪除符號。這樣就完成這一功能了。
最近為實現一個新功能弄的焦頭爛額 @xxx 的實現,在實現后寫下些心得,供以后會跳入這坑的同志們參考。
首先,當讓是考慮使用范圍,由于項目僅僅需要考慮在 WEBKIT 環境下使用,所以可以不用考慮 IE 這也使得代碼少了很多的 if(){}else{} 判斷。在Mozilla 開發者網絡上發現 selection 和 range 這兩個關于選區對象和光標對象,結合 Caret(一個用于判斷當前光標位置的JS插件)后,一個大致的雛形就浮現出來。
大概就長這樣:
先整理思路,捋一捋實現步驟。
大致思路如下:
鍵入 @ 后將選擇框顯示出來
將焦點定位在彈出框中的搜索框中
點擊選擇框中的選項時,返回輸入框
輸入框中顯示 @xxx
將光標定位在 @xxx 之后
刪除 @xxx 時需要整個 @xxx 一起刪除
由于項目使用了 angular 來構建,所以給的 demo 也是用 angular 來搭建的,但是不論用什么框架,想法有了,那么一切就好辦了。
selection 和 range 對象的具體使用請參考 MDN 上的相關文章:
selection
range
DEMO頁
主要涉及的幾個方法:
getSelection(window.getSelectio):獲取光標所在的區域(一個div或是一個textarea);
selection.getRangeAt:獲取光標所在區域中光標選區的信息;
range.setStart:設置光標選區的起始位置;
range.setEnd:設置光標選區的結束位置;
range.deleteContents:將光標選區選中的內容刪除;
range.insertNode:在光標選區中添加內容;
selection.extend:將選區的焦點移動到一個特定的位置;
selection.collapseToEnd:將當前的選區折疊到最末尾的一個點。
html 結構
所有人
樣式相關的CSS代碼就不放上來了,簡要分析下頁面結構,一個 contenteditable="true" 的輸入框和一個 id="selectPerson" 的選人框。
輸入框使用 contenteditable="true" 主要是因為想在輸入框中插入標簽,將 @xxx 內容顯示出不同的顏色(這就需要將 @xxx 放在一個標簽中),綁定 keyIn 的鍵盤輸入事件,用于檢索用戶輸入 @ 和 backspace ,并做出相應的動作;
選人框使用 showSelect 來控制是否顯示,遍歷顯示需要顯示的選人,以及使用 input 中的內容來過濾選人。
實現 @ 選擇相關代碼如下:
$scope.keyIn = function(e) { var selection = getSelection(); var ele = $("#demo"); if (e.code == "Digit2" && e.shiftKey) { $scope.showSelect = true; var offset = ele.caret("offset"); $scope.sPersonPosi = { left: offset.left - 10 + "px", top: offset.top + 20 + "px" }; // 讓選人框中的搜索框獲取焦點 $timeout(function(){ $("#searchPersonInput")[0].focus(); }) } }
實現起來挺簡單,代碼也不復雜,利用 caret 插件獲取到光標位置,將選人框在 @ 符號的下方顯示出來,并同時實現了步驟中的第二步:將焦點放在搜索框中。
選人實現主要涉及步驟為:3、4、5。
當鼠標點擊備選項時需要按順序進行 3、4、5 步驟,所以需將 3、4、5 這 3 個步驟放在一起。
相關代碼如下:
$scope.sPersonDone = function(person) { // 成功選人后,關閉選擇框,讓輸入框獲取焦點。 $scope.showSelect = false; var ele = $("#demo")[0]; ele.focus(); // 獲取之前保留先來的信息。 // 需要修改 keyIn 的代碼,保存選區以及光標信息,用于獲取在光標焦點離開前,光標的位置 var selection = lastSelection.selection; var range = lastSelection.range; var textNode = range.startContainer; // 刪除 @ 符號。 range.setStart(textNode, range.endOffset); range.setEnd(textNode, range.endOffset + 1); range.deleteContents(); // 生成需要顯示的內容,包括一個 span 和一個空格。 var spanNode1 = document.createElement("span"); var spanNode2 = document.createElement("span"); spanNode1.className = "at-text"; spanNode1.innerHTML = "@" + person.fullName; spanNode2.innerHTML = " "; // 將生成內容打包放在 Fragment 中,并獲取生成內容的最后一個節點,也就是空格。 var frag = document.createDocumentFragment(), node, lastNode; frag.appendChild(spanNode1); while ((node = spanNode2.firstChild)) { lastNode = frag.appendChild(node); } // 將 Fragment 中的內容放入 range 中,并將光標放在空格之后。 range.insertNode(frag); selection.extend(lastNode, 1); selection.collapseToEnd(); };
我們需要的效果是在 @ 選人后,將整理好的 @xxx 包裝成一個標簽,放在原先 @ 的位置,所以我們需要對原先的 $scope.keyIn 方法進行改造,保留原先的光標信息,方便在上面的方法中使用。
改造后的 $scope.keyIn 方法如下:
$scope.keyIn = function(e) { var selection = getSelection(); var ele = $("#demo"); if (e.code == "Digit2" && e.shiftKey) { $scope.showSelect = true; // 保存光標信息 lastSelection = { range: selection.getRangeAt(0), offset: selection.focusOffset, selection: selection }; $scope.showSelect = true; // 設置彈出框位置 var offset = ele.caret("offset"); $scope.sPersonPosi = { left: offset.left - 10 + "px", top: offset.top + 20 + "px" }; $timeout(function(){ $("#searchPersonInput")[0].focus(); }) } }
這里估計挺多人會有疑問,為啥要在生成的標簽后面加一個空格,而且這個空格要通過 這樣的方式實現。
首先,先解釋第一個問題:為啥要在標簽后加一個空格?
如果不加空格的話,之后在輸入文字會添加在我們生成的標簽中,也就是說如果不加空格來隔斷我們生成的標簽,我們在文本框里所做的操作就是在我們生成的標簽中進行。而加了個空格就為了避免該問題的發生,使得文本編輯在正確的編輯框中進行。
第二個問題:為啥不能直接加空格 " " ,而是通過 ,不得不說這是個過個悲傷的事實,還是碰到了兼容性的問題,在 chrome 下運行好好的代碼,在 node-webkit 中就會各種報錯。原因在不斷的 defug 后發現了: node-webkit 中,將一個 " " 添加到 contenteditable="true" 的 div 中會沒有啊,坑爹啊有木有!!!呈上之前的代碼來祭奠下。
var spanNode1 = document.createElement("span"); var node = document.createTextNode(" "); spanNode1.className = "at-text"; spanNode1.innerHTML = "@" + person.fullName; var frag = document.createDocumentFragment(); frag.appendChild(spanNode1); frag.appendChild(node); range.insertNode(frag); selection.extend(node, 1);
結果一上 node-webkit 環境各種報錯。真是坑了個大爹。原因是光標定位不準,指定位置超出實際位置,但是 node-webkit 環境確實是可以輸入空格的,一看原來是 而 不能通過 createTextNode 來創建,所以就有了之前的哪個曲線救國的策略了。
刪除實現終于捋到最后一個步驟了,刪除時,需要將一整個標簽一起刪除。由于需要監聽鍵盤的輸入,所以就可與之前 keyIn 的代碼寫在一起。
最終的 keyIn 代碼為:
$scope.keyIn = function(e) { var selection = getSelection(); var ele = document.getElementById("demo"); if (e.code == "Digit2" && e.shiftKey) { // 保存光標信息 lastSelection = { range: selection.getRangeAt(0), offset: selection.focusOffset, selection: selection }; $scope.showSelect = true; // 設置彈出框位置 var offset = $(ele).caret("offset"); $scope.sPersonPosi = { left: offset.left + "px", top: offset.top + 30 + "px" }; $timeout(function(){ $("#searchPersonInput")[0].focus(); }) } else if (e.code == "Backspace") { // 刪除邏輯 // 1 :由于在創建時默認會在 @xxx 后添加一個空格, // 所以當得知光標位于 @xxx 之后的一個第一個字符后并按下刪除按鈕時, // 應該將光標前的 @xxx 給刪除 // 2 :當光標位于 @xxx 中間時,按下刪除按鈕時應該將整個 @xxx 給刪除。 var range = selection.getRangeAt(0); var removeNode = null; if (range.startOffset <= 1 && range.startContainer.parentElement.className != "at-text") removeNode = range.startContainer.previousElementSibling; if (range.startContainer.parentElement.className == "at-text") removeNode = range.startContainer.parentElement; if (removeNode) ele.removeChild(removeNode); } };
代碼的邏輯都寫在注釋里了,這里就不多說了。
這樣就完成 @ 這一功能了。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/86537.html
摘要:因為工作中一直在使用,也一直以來想總結一下自己關于的一些知識經驗。于是把一些想法慢慢整理書寫下來,做成一本開源免費專業簡單的入門級別的小書,提供給社區。本書的后續可能會做成視頻版本,敬請期待。本作品采用署名禁止演繹國際許可協議進行許可 React.js 小書 本文作者:胡子大哈本文原文:React.js 小書 轉載請注明出處,保留原文鏈接以及作者信息 在線閱讀:http://huzi...
摘要:騰訊空間超分辨率技術為用戶節省流量,處理效果和速度超谷歌技術在的標準下,處理速度在提升了,處理效果也有明顯提升。此外,也是業界首次實現移動端使用深度神經網絡進行超分辨率,并保證圖片能夠實時進行處理。值得一提的是的對應指標也在名單里。 團隊分享 魔幻語言 JavaScript 系列之 call、bind 以及上下文 從一行代碼來看看 JavaScript 是一門多么魔幻的語言,順便談談 ...
摘要:的語言的動態性意味著我們可以使用以上種數據類型表示變換過渡動畫實現案例前端掘金以下所有效果的實現方式均為個人見解,如有不對的地方還請一一指出。 讀 zepto 源碼之工具函數 - 掘金Zepto 提供了豐富的工具函數,下面來一一解讀。 源碼版本 本文閱讀的源碼為 zepto1.2.0 $.extend $.extend 方法可以用來擴展目標對象的屬性。目標對象的同名屬性會被源對象的屬性...
閱讀 2788·2023-04-26 01:47
閱讀 3597·2023-04-25 23:45
閱讀 2472·2021-10-13 09:39
閱讀 612·2021-10-09 09:44
閱讀 1799·2021-09-22 15:59
閱讀 2770·2021-09-13 10:33
閱讀 1723·2021-09-03 10:30
閱讀 662·2019-08-30 15:53