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

vue實現At人文本輸入框示例詳解

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

知識前置

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

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

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

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

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

需求分析

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

實現

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

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

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

<!--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>

效果如下圖所示

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

添加at功能

這里的需求主要分四步走

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

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

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

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ù)數據處理
  dom.setAttribute('data-id', item.id)
  dom.innerHTML = `&nbsp@${item.name}&nbsp`
  return dom
},

效果入下圖所示

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

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

Selection 對象

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

//改造input函數
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函數
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ā)現,@多人的時候,出現問題了

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

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

我們在插入dom節(jié)點之前,文本框的所有內容都是屬于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é)點的里面,所以就出現了上文亂碼的問題。

所以我們解決的方案,就是在保存焦點位置的時候,同時保存當前編輯的那個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()) {
    // 這里第二個參數要配置成true,沒配置有其他的一些小bug,這里不展開講,詳細可以看文檔
    if (selection.containsNode(value, true)) {
      this.dom = value
      this.domIndex = index
    }
  }
},

現在當前編輯的節(jié)點和編輯的位置都已經保存下來了,剩下的就是把@人的節(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
},

然而,當我們再次運行代碼調試的時候,出現了我們預期外的結果

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

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

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

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

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

后記

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

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

以上就是vue實現At人文本輸入框示例詳解的詳細內容,更多關于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表情占位符的操作方法

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

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

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

    vue cli3適配所有端方案的實現

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

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

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

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

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

    Vue 實現輪播圖功能的示例代碼

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

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

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

    vue實現在線翻譯功能

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

最新評論

南宁市| 阿克陶县| 厦门市| 新沂市| 公主岭市| 凤阳县| 九江县| 山西省| 曲麻莱县| 吉安市| 汉沽区| 舟山市| 阿克| 丰台区| 延寿县| 六枝特区| 日照市| 牙克石市| 辽阳市| 深圳市| 石柱| 鹰潭市| 普格县| 湘西| 大足县| 曲麻莱县| 龙泉市| 固镇县| 离岛区| 隆化县| 九台市| 德阳市| 林芝县| 云龙县| 石门县| 科尔| 浙江省| 东宁县| 武邑县| 敖汉旗| 海淀区|