1. 程式人生 > >自己動手實現簡易的div可編輯富文字框及按下tab鍵後增加4個空格功能

自己動手實現簡易的div可編輯富文字框及按下tab鍵後增加4個空格功能

需求分析

最近需要製作一個簡單的使用者評論輸入框,在網上找了一些富文字輸入框,但是它們的功能太多,不適合自己的需求,於是決定自己動手實現一個簡易的富文字輸入框。

第一步:

想要實現富文字輸入框並不是難事,在<div>標籤內加入   contenteditable="true" ,這個div元素就可以編輯了,而且它的innerhtml 就是保留格式的html文字。

第二步:

只需要寫一些css程式碼給div元素設計一些簡單的樣式,就可以讓輸入框變得美觀。

實現按下tab鍵後增加四個空格的功能:

在不寫額外程式碼的情況下,按下tab鍵,游標會離開編輯框,這是預設行為。所以需要監聽按下tab的事件,在處理函式中取消瀏覽器的預設行為。

然後要想在游標處按下tab鍵,插入四個空格,就需要了解瀏覽器的游標物件。

在瀏覽器中,如果我們選中一片區域,就是看到的變藍色的區域,這塊區域是一個selection物件,selection在ff和chrome瀏覽器可以直接用 window.getSelection()獲取,在HTML裡面,selection只有一個的,它是有開始和結束的。現在在頁面上隨意選中一些元素,按F12,在console 中輸入window.getSelection(); 就可以看到這個selection物件的全部成員。


其中anchorNode (baseNode)是選擇區域的開始節點,focusNode (extendNode)是選擇區域的結束節點,注意: 這裡的開始表示按下滑鼠的位置,結束指的是放開滑鼠的位置,anchoNode不一定在focusNode的前面,因為有的區域可能是從後往前選的。

anchorOffset 返回一個數字,其表示的是選區起點在 anchorNode 中的位置偏移量。

focusOffset 返回一個數字,其表示的是選區終點在 focusNode 中的位置偏移量。

isCollapsed 返回一個布林值,用於判斷選區的起始點和終點是否在同一個位置。

rangeCount 返回該選區所包含的連續範圍的數量。

從type可以看到selection的種類其實是range物件

具體的資訊可以在 具體文件 中檢視,基本包含所有資訊。

只得到selection物件是不能對選區進行操作的,很多屬性都是隻讀許可權,要對選區進行修改,就需要獲得選取的range物件,range提供了很多的函式,可與對選取進行修改。

對選取的修改有兩種方式:

1、獲取 range物件 是通過 window.getselection().getRangeAt(0) 獲取的,然後呼叫range的成員函式進行修改

2、直接建立一個新的range物件,如下:range = document.createRange();

    然後對新建的range物件進行需要的修改,最後將selection的原有range刪除,將新建的range物件加進去,替換掉原來的。

selection.removeAllRanges();
selection.addRange(range);

再來補充range的知識:


commonAncestorContainer 是選中元素共同的祖先元素

endContainer是結束元素 endOffset是在endContainer中的結束位置

startContainer是開始元素的位置     startOffset是 startContainer 中的開始點位置。

collapsed 是表示起始和終止是否在同一個位置,如果不再,表現的是選中的區域,如果在同一個位置,顯示的就是輸入游標。

具體的成員函式和變數的使用方法詳見 傳送門 

..........................................................................................................................................................................................................................................

我實現按下tab 增加4個空格的思路是 : 自己構造一個文字標籤,裡面的內容是4個 &nbsp ,將這個元素插入到游標之後,就可以實現四個空格的縮排。(前提是這個文字框是富文字編輯框)

以下我附上自己實現的富文字編輯框的全部程式碼,使用的vue,可以直接放在vue的一個元件裡, 我實現的文字框還帶有一些收縮展開的css動畫。

<!--html部分-->
<template>
  <div class="edit-line">
    <div contenteditable="true" class="edit-content" placeholder="吐槽一下.." @focus="distransparent"
         @blur="transparent" ref="edit" @keydown.tab.stopdefault="tab"></div>
    <button @click="send" ref="send" style="opacity: 0.5">提交</button>
  </div>
</template>

以下是js部分,有詳細的註釋

實現按下tab進行縮排的是tab()函式 ,event是監聽的原生的tab按鍵物件

<script>
  export default {
    name: 'editor',
    methods: {
      transparent () {
        this.$refs.send.$el.style = 'opacity:0.5;';
      },
      distransparent () {
        this.$refs.send.$el.style = 'opacity:1;';
      },
      tab (event) {
        // 阻止預設切換元素的行為
        if (event && event.preventDefault) {
          event.preventDefault()
        } else {
          window.event.returnValue = false
        }
        // 獲取游標的range物件 event.view 是一個window物件
        let range = event.view.getSelection().getRangeAt(0);
        // 游標的偏移位置
        let offset = range.startOffset;
        // 新建一個span元素
        let span = document.createElement('span');
        // 四個 表示四個空格
        span.innerHTML = '    ';
        // 建立一個新的range物件
        let newrange = document.createRange();
        // 設定新的range的位置,也是插入元素的位置
        newrange.setStart(range.startContainer, offset);
        newrange.setEnd(range.startContainer, offset);
        newrange.collapse(true);
        newrange.insertNode(span);
        // 去掉舊的range物件,用新的range物件替換
        event.view.getSelection().removeAllRanges();
        event.view.getSelection().addRange(range);
        // 將游標的位置向後移動一個偏移量,放到加入的四個空格後面
        range.setStart(span, 1);
        range.setEnd(span, 1);
      }
    }
  }
</script>
最後是css程式碼
<style scoped>
  .edit-line {
    width: 100%;
    height: 100%;
    line-height: 25px;
    display: flex;
    align-items: center;
  }

  .edit-content {
    transition: width 400ms ease-in;
    width: 80%;
    /*max-height: 45px;*/
    height: 100%;
    outline: 0;
    padding: 0 4%;
    border: 1px solid #dddddd;
    border-radius: 12px;
    margin: 0 2%;
    overflow: auto;
    background-color: rgba(255, 255, 255, 0.4);
    font-size: larger;
    /*!*保證只能寫入文字資訊,不能是富文字*!*/
    /*-webkit-user-modify: read-write-plaintext-only*/
  }

  .edit-content:empty {
    width: 35%;
    color: lightgray;
  }

  .edit-content:empty::before {
    content: attr(placeholder);
  }

  .edit-content:focus {
    background-color: white;
    width: 80%;
  }
</style>

通過了解selection物件和range物件,以後想要給自己的編輯器新增定製的功能,都是比較類似的用法了。