讀 pen by sofish

小魚大大的Pen好像不少粉的樣子,這幾天看了看,學習了一下git

編輯的原理依賴 div 上的 contenteditable 屬性
開啓和關閉功能是下面的destroy rebuild函數github

Pen.prototype.destroy = function(isAJoke) {
    var destroy = isAJoke ? false : true
      , attr = isAJoke ? 'setAttribute' : 'removeAttribute';

    if(!isAJoke) {
      this._sel.removeAllRanges();
      this._menu.style.display = 'none';
    }
    this._isDestroyed = destroy;
    this.config.editor[attr]('contenteditable', '');

    return this;
  };

  Pen.prototype.rebuild = function() {
    return this.destroy('it\'s a joke');
  };

若是是低端瀏覽器,兼容方案爲設置div.pen.innerHTML = 一個textarea
stay功能的實現數組

window.onbeforeunload = function() {
        if(!that._isDestroyed) return 'Are you going to leave here?';
      };

menu函數使得選中文字後 toolbar 居中顯示瀏覽器

// show menu
  Pen.prototype.menu = function() {

    var offset = this._range.getBoundingClientRect()
      , top = offset.top - 10
      , left = offset.left + (offset.width / 2)
      , menu = this._menu;

    // display block to caculate it's width & height
    menu.style.display = 'block';
    menu.style.top = top - menu.clientHeight + 'px';
    menu.style.left = left - (menu.clientWidth/2) + 'px';

    return this;
  };

好了 正式流程爲:markdown

  1. 設置div contenteditable
  2. 獲取選擇的文字 doc.getSelection();
  3. 初始化 this.actions() 生成內置處理函數this._actions
  4. 初始化 this.toolbar() 生成 this._menu 插入dom 調用 menu() 顯示出來
  5. 若是 resize scroll 都將從新繪製 toolbar的位置
  6. mouseup keyup 鼠標和鍵盤選擇完畢以後 觸發 toolbar的顯示 [根據是否有選擇內容判斷]
  7. 點擊toolbar上任意功能 觸發 this._actions highligh
  8. highlight是個複雜的過程 須要根據選擇文字父元素的標籤內容 判斷其所擁應該被高亮的狀態
  9. 最後的核心 this._actions來了 行 塊 形式判斷不一樣的操做目標節點 而後執行 document.execCommand命令

固然他還有markdown插件
1. 每次 keypress 的時候會觸發 parse
2. parse 對輸入push到數組covertor.stack
3. 判斷爲空格按下 則把 stack中的命令取出 執行valid
4. valid經過的,則進入 action環節 渲染輸出不一樣的格式dom

寫的很是粗糙,主要爲了記錄一下流程
還有我以爲Pen大量的時間精力都在處理toolbar highlight和節點查找 判斷上
雖然短短300行,依然提供了較好的體驗.源碼值得一看
附上簡短的3個util :D函數

// type detect
  utils.is = function(obj, type) {
    return Object.prototype.toString.call(obj).slice(8, -1) === type;
  };
  // 判斷類型

  // copy props from a obj
  utils.copy = function(defaults, source) {
    for(var p in source) {
      if(source.hasOwnProperty(p)) {
        var val = source[p];
        defaults[p] = this.is(val, 'Object') ? this.copy({}, val) :
          this.is(val, 'Array') ? this.copy([], val) : val;
      }
      // 深層複製
    }
    return defaults;
  };

  // log
  utils.log = function(message, force) {
    if(window._pen_debug_mode_on || force) console.log('%cPEN DEBUGGER: %c' + message, 'font-family:arial,sans-serif;color:#1abf89;line-height:2em;', 'font-family:cursor,monospace;color:#333;');
  };
相關文章
相關標籤/搜索