最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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())
},

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

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)容,更多關于vue At人文本輸入框的資料請關注腳本之家其它相關文章!

相關文章

  • 一文搞懂Vue中computed和watch的區(qū)別

    一文搞懂Vue中computed和watch的區(qū)別

    這篇文章主要和大家詳細介紹一下Vue中computed和watch的使用與區(qū)別,文中通過示例為大家進行了詳細講解,對Vue感興趣的同學,可以學習一下
    2022-11-11
  • vue在頁面中如何使用window全局變量

    vue在頁面中如何使用window全局變量

    這篇文章主要介紹了vue在頁面中如何使用window全局變量問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • vue處理emoji表情占位符的操作方法

    vue處理emoji表情占位符的操作方法

    在計算字符數(shù)時,一般情況下,英文字符、數(shù)字和大部分符號都可以被視為占一個字符長度,因為它們是單個字節(jié),然而,對于某些特殊字符,如表情符號和部分非英文字符,會被認為占據(jù)了多個字符長度,本文給介紹了vue處理emoji表情占位符的操作方法,需要的朋友可以參考下
    2024-06-06
  • vue中使用帶隱藏文本信息的圖片、圖片水印的方法

    vue中使用帶隱藏文本信息的圖片、圖片水印的方法

    這篇文章主要介紹了vue中使用帶隱藏文本信息的圖片、圖片水印的方法,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-04-04
  • vue cli3適配所有端方案的實現(xiàn)

    vue cli3適配所有端方案的實現(xiàn)

    這篇文章主要介紹了vue cli3適配所有端方案的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-04-04
  • 自定義Vue組件打包、發(fā)布到npm及使用教程

    自定義Vue組件打包、發(fā)布到npm及使用教程

    這篇文章主要介紹了自定義Vue組件打包、發(fā)布到npm及使用教程 ,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-05-05
  • vue中使用echarts刷新可以正常渲染但路由跳轉(zhuǎn)不顯示的問題解決

    vue中使用echarts刷新可以正常渲染但路由跳轉(zhuǎn)不顯示的問題解決

    在?Vue?中使用?ECharts?組件時,遇到路由跳轉(zhuǎn)后圖表不顯示的問題可能是因為組件銷毀和重新創(chuàng)建的原因,所以本文給大家介紹了vue中使用echarts刷新可以正常渲染但路由跳轉(zhuǎn)不顯示問題的解決方法,需要的朋友可以參考下
    2024-02-02
  • Vue 實現(xiàn)輪播圖功能的示例代碼

    Vue 實現(xiàn)輪播圖功能的示例代碼

    Vue是一款流行的前端框架,它提供了一系列的工具和組件,使得開發(fā)者可以更加便捷地創(chuàng)建交互式的Web應用程序,輪播圖是Web應用程序中常見的一種交互式組件,本文將介紹如何使用Vue和第三方組件庫 Element UI實現(xiàn)輪播圖功能,需要的朋友可以參考下
    2023-05-05
  • 淺談angular4.0中路由傳遞參數(shù)、獲取參數(shù)最nice的寫法

    淺談angular4.0中路由傳遞參數(shù)、獲取參數(shù)最nice的寫法

    下面小編就為大家分享一篇淺談angular4.0中路由傳遞參數(shù)、獲取參數(shù)最nice的寫法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-03-03
  • vue實現(xiàn)在線翻譯功能

    vue實現(xiàn)在線翻譯功能

    這篇文章主要為大家詳細介紹了vue實現(xiàn)在線翻譯功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-09-09

最新評論

宁德市| 古田县| 宝应县| 苍溪县| 浦城县| 百色市| 休宁县| 满洲里市| 开化县| 安远县| 晋中市| 新泰市| 郴州市| 黔西县| 阿合奇县| 吉林省| 定襄县| 宁晋县| 额敏县| 龙门县| 定襄县| 衡山县| 定远县| 镇原县| 苍梧县| 彭泽县| 石柱| 宕昌县| 景泰县| 孝义市| 南木林县| 锡林郭勒盟| 社旗县| 邢台县| 武城县| 陇川县| 鹤壁市| 东兰县| 渝北区| 客服| 三亚市|