JSからtextareaの文字列を変更した時、Undoに対応できるようにする #dev #javascript version 1

2016/05/01 23:04 by zk33 zk33
  :追加された部分   :削除された部分
(差分が大きい場合、文字単位では表示しません)
JSからtextareaの文字列を変更した時、Ctrl+Zの取り消しに対応できるようにする #dev #javascript
mimemoのmarkdownエディタを作っていく上で、リストの補完など一部のアイデアはesareaから拝借しています(ありがたや)。
ただ、esarea(及びesaのmarkdownエディタ)は、ブラウザによって補完したあとにCtrl+Zなどでの「取り消し」ができないので、これはエディタとして致命的、ということでmimemoでは頑張って対応させたので、その辺フィードバックも兼ねたハウツーメモです。

# textarea書き換え時にできるだけUndo対応させる

* Firefoxは元々Undo対応している
* ChromeなどはexecCommandで'insertText'を呼んで書き換える形にするとUndoできる形で書き換えられる
* IEではexecCommandで'ms-beginUndoUnit','ms-endUndoUnit'で挟んで書き換えするとUndo対応できる

```js
  replace(str, fromIdx, toIdx) {
    let inserted = false;

    if (str) {
      let expectedLen = this.inputElm.value.length - Math.abs(toIdx - fromIdx) + str.length;
      this.inputElm.contentEditable = true;
      this.inputElm.focus();
      this.inputElm.selectionStart = fromIdx;
      this.inputElm.selectionEnd = toIdx;
      try {
        inserted = document.execCommand('insertText', false, str);
      } catch (e) {
        inserted = false
      }
      if (inserted && (this.inputElm.value.length !== expectedLen || this.inputElm.value.substr(fromIdx, str.length) !== str)) {
        //firefoxでなぜかうまくいってないくせにinsertedにfalseが返ってこなくなっているので失敗を検知してfalseにする
        inserted = false;
      }
    }

    if (!inserted) {
      try {
        document.execCommand('ms-beginUndoUnit');
      } catch (e) {}
      let value = this.inputElm.value;
      this.inputElm.value = '' + value.substring(0, fromIdx) + str + value.substring(toIdx);
      try {
        document.execCommand('ms-endUndoUnit');
      } catch (e) {}
    }
    this._updated();
  }
```      

mimemoのmarkdownエディタを作っていく上で、リストの補完など一部のアイデアはesareaから拝借しています(ありがたや)。
ただ、esarea(及びesaのmarkdownエディタ)は、ブラウザによって補完したあとにCtrl+Zなどでの「取り消し」ができないので、これはエディタとして致命的、ということでmimemoでは頑張って対応させたので、その辺フィードバックも兼ねたハウツーメモです。

textarea書き換え時にできるだけUndo対応させる

  • Firefoxは元々Undo対応している
  • ChromeなどはexecCommandで'insertText'を呼んで書き換える形にするとUndoできる形で書き換えられる
  • IEではexecCommandで'ms-beginUndoUnit','ms-endUndoUnit'で挟んで書き換えするとUndo対応できる
  replace(str, fromIdx, toIdx) {
    let inserted = false;

    if (str) {
      let expectedLen = this.inputElm.value.length - Math.abs(toIdx - fromIdx) + str.length;
      this.inputElm.contentEditable = true;
      this.inputElm.focus();
      this.inputElm.selectionStart = fromIdx;
      this.inputElm.selectionEnd = toIdx;
      try {
        inserted = document.execCommand('insertText', false, str);
      } catch (e) {
        inserted = false
      }
      if (inserted && (this.inputElm.value.length !== expectedLen || this.inputElm.value.substr(fromIdx, str.length) !== str)) {
        //firefoxでなぜかうまくいってないくせにinsertedにfalseが返ってこなくなっているので失敗を検知してfalseにする
        inserted = false;
      }
    }

    if (!inserted) {
      try {
        document.execCommand('ms-beginUndoUnit');
      } catch (e) {}
      let value = this.inputElm.value;
      this.inputElm.value = '' + value.substring(0, fromIdx) + str + value.substring(toIdx);
      try {
        document.execCommand('ms-endUndoUnit');
      } catch (e) {}
    }
    this._updated();
  }