欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

vue實現(xiàn)At人文本輸入框示例詳解

 更新時間:2022年09月16日 08:59:37   作者:移動培養(yǎng)基  
這篇文章主要為大家介紹了vue實現(xiàn)At人文本輸入框示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

知識前置

基于vue手把手教你實現(xiàn)一個擁有@人功能的文本編輯器(其實就是微信群聊的輸入框)

Selection 對象,表示用戶選擇的文本范圍或插入符號的當前

developer.mozilla.org/zh-CN/docs/…

contenteditable 是一個枚舉屬性,表示元素是否可被用戶編輯。

developer.mozilla.org/zh-CN/docs/…

需求分析

  • 文本框能夠輸入文本(太簡單了)
  • 能夠at人

實現(xiàn)

創(chuàng)建能夠輸入文本的文本框

在這里主要利用 contenteditable 屬性,讓創(chuàng)建的 div 能夠編輯

利用input事件監(jiān)聽數(shù)據(jù)變化,將數(shù)據(jù)同步出去

<!--main.vue!-->
<template>
  <div>
    <Editor v-model="value"/>
  </div>
</template>
<!--editor.vue!-->
<template>
  <div>
    <div
      class="editor"
      contenteditable="true"
      @input="input"
    />
  </div>
</template>
<script>
export default {
  computed: {
    editor() {
      return this.$refs.editor || {}
    }
  },
  methods: {
    input(e) {
      this.$emit('input', this.getEditorHtml())
    },
    getEditorHtml() {
      return this.editor.innerHTML || ''
    }
  }
}
</script>
<style lang="less" scoped>
.editor{
  overflow-y: auto;
  background: #F4F6FB;
  border-radius: 4px;
  border: 1px solid transparent;
  min-height: 40px;
  max-height:200px;
  padding: 14px 9px;
  line-height: 20px;
  &:empty{
    &::before{
      content:'輸入你想對他/她說的話,然后@她!';
      color: #999;
    }
  }
  &:focus{
    outline: none;
    border-color: #3656C6;
    border-radius: 4px;
  }
}
</style>

效果如下圖所示

這個時候我們就實現(xiàn)了一個能夠綁定數(shù)據(jù)的文本輸入框,第一個需求完美實現(xiàn),接下來實現(xiàn)第二個需求(開始折磨)

添加at功能

這里的需求主要分四步走

  • 當用戶輸入@字符時,彈出用戶選擇列表
  • 當用戶點擊@的人時,收回@列表
  • 將@的人嵌入到文本框中
  • 刪除@的人時,要直接整塊刪除

首先我們先實現(xiàn)一個用戶選擇的列表,這里主要涉及到的都是界面的編輯和動畫的設置,不展開描述,直接上效果圖**(完整代碼會在文末給出)**

接著我們要改造input函數(shù),檢測當用戶輸入為@符號時,彈出選擇框

input(e) {
  if (e.data === '@') {
    // 彈出用戶選擇框
    this.$refs.UserList.show()
    // 失去焦點,退出手機的軟體鍵盤
    this.editor.blur()
  }
  this.$emit('input', this.getEditorHtml())
},

當用戶點擊要@的人時,關(guān)閉選擇列表,同時將@人的人插入到文本框中

userItemClick(item) {
  const dom = this.createAtDom(item)
  this.$refs.editor.innerHTML = this.$refs.editor.innerHTML + dom.outerHTML
  this.$refs.UserList.close()
},
createAtDom(item) {
  const dom = document.createElement('span')
  dom.classList.add('active-text')
  // 這里的contenteditable屬性設置為false,刪除時可以整塊刪除
  dom.setAttribute('contenteditable', 'false')
  // 將id存儲在dom元素的標簽上,便于后續(xù)數(shù)據(jù)處理
  dom.setAttribute('data-id', item.id)
  dom.innerHTML = `&nbsp@${item.name}&nbsp`
  return dom
},

效果入下圖所示

相信有不少朋友已經(jīng)發(fā)現(xiàn)問題了,這種方式只能怪將@的人添加到文本的最末尾,但如果我編輯文本的時候,光標的位置不是在文本的最后,而是在文本之間的某個位置,那此時我們這么添加@的人就會有點反直覺。

所以我們在彈出選擇列表的時候,要把當前光標所處的位置標記下來,插入時,就插入到對應的位置上。所以此時就要拋出我們本文最重要的一個對象

Selection 對象

我們要利用 Selection 對象的 anchorOffset 屬性去獲取當前焦點的位置,此時我們改造input函數(shù),添加 saveIndex 方法,在彈出文本框失焦之前,保存當前焦點的位置 。

//改造input函數(shù)
input(e) { 
  if (e.data === '@') {
    // 保存焦點位置
    this.saveIndex()
    // 彈出用戶選擇框 
    this.$refs.UserList.show() 
    // 失去焦點,退出手機的軟體鍵盤
    this.editor.blur() 
  }
  this.$emit('input', this.getEditorHtml()) 
},
// 添加saveIndex方法
async saveIndex() {
  // 獲取selection對象
  const selection = getSelection()
  // 保存當前焦點的位置
  this.selectionIndex = selection.anchorOffset
},
// 改造userItemClick函數(shù)
userItemClick(item) {
  const dom = this.createAtDom(item)
  this.addData(item)
  this.$refs.UserList.close()
},
// 添加dom節(jié)點到指定位置
addData(item){
  const html = this.editor.innerHTML
  const leftInnerHtml = html.substring(0, this.selectionIndex - 1)
  const dom = this.createAtDom(item)
  const rightInnerHtml = html.substring(this.selectionIndex, html.length)
  this.editor.innerHTML = leftInnerHtml + dom.outerHTML + rightInnerHtml
}

這個時候我們就可以把@的人添加到我們之前光標的位置了,效果如下如所示

但在某天,你突發(fā)奇想,想同時對很多個女神發(fā)出邀請,這個時候你發(fā)現(xiàn),@多人的時候,出現(xiàn)問題了

我們插入的@人的節(jié)點被硬生生拆成了字符串,這很明顯跟我們的預期有差別呀,這個時候我們應該分析一下我們編輯時的dom結(jié)構(gòu),如下圖所示

為了便于理解我畫了個簡單的圖

我們在插入dom節(jié)點之前,文本框的所有內(nèi)容都是屬于editor節(jié)點下唯一一個textNode節(jié)點,插入dom節(jié)點之后,editor節(jié)點新增了一個子節(jié)點,而 Selection.anchorOffset 這個屬性獲取到的焦點位置,實際上是相對于當前所處node節(jié)點而言的(←理解這個概念,非常重要)

也就是說

我們第一次插入dom節(jié)點,焦點位置是相對于當前節(jié)點,也就是editor節(jié)點下的唯一一個textNode節(jié)點計算

第二次插入dom節(jié)點,焦點位置是相對于當前節(jié)點,也就是當前textNode節(jié)點計算

后續(xù)插入的dom節(jié)點,焦點位置計算方式同上

所以當我們有如下需求的時候

Selection.anchorOffset 的返回值是5,而我們的addData方法,實際上是從editor.innerHtml的第一個位置開始算,第五個位置剛好插到了span節(jié)點的里面,所以就出現(xiàn)了上文亂碼的問題。

所以我們解決的方案,就是在保存焦點位置的時候,同時保存當前編輯的那個textNode節(jié)點,那我們怎么找到當前正在編輯的那個textNode節(jié)點呢?

Selection 對象提供了一個方法 Selection.containsNode()

mdn文檔是這么描述的:判斷指定的節(jié)點是否包含在 Selection 中 (是否被選中)

在我們這個場景中,通俗點講就是,我這個節(jié)點到底是不是編輯的節(jié)點?是你就返回true,不是就false

所以我們可以在彈出用戶選擇框之前,遍歷一下editor節(jié)點的子節(jié)點,找出我們當前編輯的那個textNode節(jié)點

// 改造一下saveIndex
async saveIndex() {
  const selection = getSelection()
  this.selectionIndex = selection.anchorOffset
  const nodeList = this.editor.childNodes
  // 保存當前編輯的dom節(jié)點
  for (const [index, value] of nodeList.entries()) {
    // 這里第二個參數(shù)要配置成true,沒配置有其他的一些小bug,這里不展開講,詳細可以看文檔
    if (selection.containsNode(value, true)) {
      this.dom = value
      this.domIndex = index
    }
  }
},

現(xiàn)在當前編輯的節(jié)點和編輯的位置都已經(jīng)保存下來了,剩下的就是把@人的節(jié)點插入到我們編輯的那個textNode節(jié)點里面就完成了。

// 改造一下addData方法
addData(item) {
  const html = this.dom.textContent
  const leftText = html.substring(0, this.selectionIndex - 1)
  const dom = this.createAtDom(item)
  const rightText = html.substring(this.selectionIndex, html.length)
  this.dom.textContent = leftText + dom.outerHTML + rightText
},

然而,當我們再次運行代碼調(diào)試的時候,出現(xiàn)了我們預期外的結(jié)果

是我們代碼有問題嗎?說是其實不算是,說不是,其實也算是(廢話)

其實是因為我們編輯的是textNode節(jié)點,而textNode節(jié)點就算包含了dom結(jié)構(gòu),他也是把結(jié)構(gòu)當成文本輸出到頁面上,所以在這里

  • 我們應該創(chuàng)建一個新的結(jié)構(gòu),也就是我們的文檔片段DocumentFragment
  • 然后把我們的節(jié)點結(jié)構(gòu)插入到DocumentFragment
  • 接著利用Node.insertBefore()方法,把DocumentFragment插入到原來編輯的textNode節(jié)點之前,再用Node.removeChild()方法把原來編輯的textNode節(jié)點刪除
  • 這樣就可以實現(xiàn)正常的插入

為了方便理解,可以看一下流程圖

addData(item) {
  const text = document.createDocumentFragment()
  const span = document.createElement('span')
  const html = this.dom.textContent
  // 左邊的節(jié)點
  const textLeft = document.createTextNode(html.substring(0, this.selectionIndex - 1) + '')
  // 這里如果textLeft是個空的文本節(jié)點,會導致@用戶無法刪除,這里添加一個判斷,如果是空,則插入一個空的span節(jié)點
  text.appendChild(textLeft.textContent ? textLeft : span)
  // 加入@人的節(jié)點
  text.appendChild(this.createAtDom(item))
  // 右邊的節(jié)點
  const textRight = document.createTextNode(html.substring(this.selectionIndex, html.length))
  textRight.textContent && text.appendChild(textRight)
  this.editor.insertBefore(text, this.dom)
  this.editor.removeChild(this.dom)
},

當我們處理到這里時,就可以多次at想要at的人,效果如圖

后續(xù)我們要將數(shù)據(jù)提取出來,可以根據(jù)v-model綁定的value進行解析,把插在標簽里的數(shù)據(jù)提取出來,也可以根據(jù)自己的業(yè)務插入一些數(shù)據(jù),這里不是重點,也不展開講

后記

基本上文本編輯器的核心邏輯到這里就講完了,但是這個demo在做的過程中,有好幾個地方做了優(yōu)化,特別是針對移動端軟體鍵盤的進入和離開,還有焦點的對焦和失焦,都做了一些處理,但是在文章里頭沒有展開講

想要詳細了解的大佬們可以到我github倉庫下載源碼 github.com/adouni1996/…

以上就是vue實現(xiàn)At人文本輸入框示例詳解的詳細內(nèi)容,更多關(guān)于vue At人文本輸入框的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Vue插件使用方法詳情分享

    Vue插件使用方法詳情分享

    這篇文章主要介紹了Vue插件使用方法詳情分享,使用插件之前顯示定義,下文通過js插件定義展開詳細文章介紹,具有一定的參考價值,需要的小伙伴可以參考一下
    2022-03-03
  • vant中的toast層級改變操作

    vant中的toast層級改變操作

    這篇文章主要介紹了vant中的toast層級改變操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-11-11
  • ant design的table組件實現(xiàn)全選功能以及自定義分頁

    ant design的table組件實現(xiàn)全選功能以及自定義分頁

    這篇文章主要介紹了ant design的table組件實現(xiàn)全選功能以及自定義分頁,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-11-11
  • vue實現(xiàn)圖片路徑轉(zhuǎn)二進制文件流(binary)

    vue實現(xiàn)圖片路徑轉(zhuǎn)二進制文件流(binary)

    這篇文章主要介紹了vue實現(xiàn)圖片路徑轉(zhuǎn)二進制文件流(binary),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • vue實例配置對象中el、template、render的用法

    vue實例配置對象中el、template、render的用法

    這篇文章主要介紹了vue實例配置對象中el、template、render的用法,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • vue實現(xiàn)表格打印功能

    vue實現(xiàn)表格打印功能

    常見的打印有JavaScript打印、jQuery、vue打印,這里主要講述vue使用vue-print-nb進行打印,廢話不多說,直接手摸手上代碼,感興趣的朋友可以參考下
    2024-01-01
  • 如何利用vue3實現(xiàn)放大鏡效果實例詳解

    如何利用vue3實現(xiàn)放大鏡效果實例詳解

    最近有項目需要用到對圖片進行局部放大,類似淘寶商品頁的放大鏡效果,經(jīng)過一番研究功能可用,下面這篇文章主要給大家介紹了關(guān)于如何利用vue3實現(xiàn)放大鏡效果的相關(guān)資料,需要的朋友可以參考下
    2021-09-09
  • 詳解Vue中添加過渡效果

    詳解Vue中添加過渡效果

    本篇文章主要介紹了詳解Vue中添加過渡效果 ,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-03-03
  • Vue SSR 組件加載問題

    Vue SSR 組件加載問題

    這篇文章主要介紹了Vue SSR 組件加載問題,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2018-05-05
  • vue給對象動態(tài)添加屬性和值的實例

    vue給對象動態(tài)添加屬性和值的實例

    今天小編就為大家分享一篇vue給對象動態(tài)添加屬性和值的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-09-09

最新評論