分享一個冷門知識——文本框的選擇文本在業務中的應用

上週產品經理提出一個需求:javascript

在編輯框中,用戶雙擊或直接選中左括號(右引號)或左引號(有引號),對其包含的全部字符(含括號或引號字符在內)進行高亮;前端

聽到這個需求的時候,個人心理活動是這樣的:「臥槽,這什麼需求,歷來沒見過。前端作得了嗎?」。 臉上笑嘻嘻,內心媽賣批,僞裝淡定的對產品經理說,沒那麼快作,排到3月份的計劃去吧~java

雙手撓頭,開始頭腦風暴
首先想到的是,搜索時,在搜索結果中對關鍵詞的進行高亮。這個思路很簡單,就是在搜索結果的文本中匹配關鍵詞,用個span標籤包裹,並給span標籤加個樣式便可。這個,好像跟需求相去甚遠啊!
百度了半天也沒找到相關的案例,沒事,咱們還有時間,能夠本身來造個輪子。數組

正確思路:spa

選擇(select)事件

  • 從事件入手。高亮是文本被選中時觸發,文本框有個DOM事件——select,表示文本框文本被選中時觸發的事件。

若是取得選中的文本:

  • 兩個屬性:selectionStart、selectionEnd。 這兩個屬性中保存的是基於 0 的數值,表示所選擇 文本的範圍(即文本選區開頭和結尾的偏移量)。
  • 要得到選中的文本,可使用以下代碼:
target.value.substring(selectionStart, selectionEnd);  // target是文本對象
複製代碼
  • IE9+、Firefox、Safari、Chrome 和 Opera 都支持這兩個屬性。IE8 及以前版本不支持這兩個屬性, 而是提供了另外一種方案,這裏不作介紹

如何選擇部分文本

  • 全部文本框都有一個 setSelectionRange()方法。這個方法接收兩個參數:要選擇的第一個字符的索引和要選擇的最後一個字符以後的字符的索引。

實現

知道了怎麼獲取選中的文本和如何選中部分文本,接下來就是去實現了。設計

  • 括號跟引號的匹配邏輯是不一樣的,括號要考慮嵌套問題,而引號不用,由於引號的嵌套沒有語義。
  • 左括號跟右括號的匹配邏輯也是不一樣的,若是用戶選中的是左括號,則要從前向後匹配。若是是右括號,則要從後往前匹配。
  • 引號相似,只是不須要考慮嵌套關係

有了思路,實現就不難了,代碼很簡單,相信你們應該看一下就懂了,我就不在囉嗦了。
源碼:code

/** * 選中括號、引號,對其包含的全部字符進行高亮顯示 * @param target 目標對象 */
function keywordHighlight(target) {
  var selectionStart = target.selectionStart,  // 選中的文本的起始位置
    selectionEnd = target.selectionEnd,        // 選中的文本的結束位置
    text = target.value,  // 輸入框中的文本內容
    selectValue = target.value.substring(selectionStart, selectionEnd); // 選中的文本

  var tmpValue,   // 待匹配的文本
    tmpArr = [],  // 用於存放切割後的待匹配文本的數組
    stack = [],   // 棧
    endIndex;     // 結束位置在tmpArr中的下標

  // selectValue === '(',從前向後匹配
  if (selectValue === '(') {
    tmpValue = text.slice(selectionStart);
    tmpArr = tmpValue.split("");

    for (var i = 0, len = tmpArr.length; i < len; i++) {
      if (tmpArr[i] === '(') {
        stack.push(tmpArr[i]);
      } else if (tmpArr[i] === ')') {
        stack.pop();
        if (stack.length === 0) {
          endIndex = i + 1;
          break;
        }
      }
    }
    if (endIndex) {
      target.setSelectionRange(selectionStart, selectionStart + endIndex);
    }
  }
  // selectValue === ')',從後向前匹配
  if (selectValue.trim() === ')') {
    tmpValue = text.slice(0, selectionEnd);
    tmpArr = tmpValue.split("");

    for (var j = tmpArr.length - 1; j >= 0; j--) {
      if (tmpArr[j] === ')') {
        stack.push(tmpArr[j]);
      } else if (tmpArr[j] === '(') {
        stack.pop();
        if (stack.length === 0) {
          endIndex = j;
          break;
        }
      }
    }
    if (endIndex || endIndex === 0) {
      target.setSelectionRange(endIndex, selectionEnd);
    }
  }

  if (selectValue.trim() === '"') {
    // 獲取引號出現次數
    var count = (text.slice(0, selectionEnd).split('"')).length-1;

    if (count % 2 !== 0) { // 引號出現次數爲奇數,從前向後匹配
      // slice獲取匹配文本時length+1,獲取匹配下標時length+1,故最後 +1+1
      endIndex = text.slice(selectionStart + 1,text.length).indexOf('"') + 1 + 1;

      if (endIndex) {
        target.setSelectionRange(selectionStart, selectionStart + endIndex);
      }
    } else { // 引號出現次數爲偶數,從後向前匹配
      tmpValue = text.slice(0, selectionEnd).trim();
      endIndex = tmpValue.slice(0,tmpValue.length-1).lastIndexOf('"');

      if (endIndex || endIndex === 0) {
        target.setSelectionRange(endIndex, selectionEnd);
      }
    }
  }
};
複製代碼

參考文獻:《javascript高級程序設計》第三版對象

相關文章
相關標籤/搜索