--- Title: 'JSからtextareaの文字列を変更した時、Undoに対応できるようにする #dev #javascript' Keywords: - dev - javascript Author: zk33 Web: 'https://mimemo.io/m/mqLXOlJe7ozQ19r' --- [mimemo](https://mimemo.io/)のmarkdownエディタを作っていく上で、リストの補完などのアイデアとコードは一部[esarea](https://github.com/fukayatsu/esarea)のものを下敷きにして書かせていただきました(多謝)。 ただ、esarea(及び[esa](https://esa.io/)のmarkdownエディタ)は、一部のブラウザで補完したあとにCtrl+Zなどでの「取り消し」ができなくなるようでしたので、これはエディタとして致命的、ということでmimemoでは頑張って(?)Undo対応させたので、その辺フィードバックも兼ねたハウツーメモです。 # JSからtextarea書き換えた時にUndo対応させる **2018/01/13追記:** Firefoxでtextareaの仕様が変わったようで、下のコードでFirefoxはundo対応できません。 ### 方法 * Chrome、SafariはexecCommand('insertText')で書き換える形にするとUndoできる形で書き換えられる * ~~Firefoxは何もしないでも元々Undo対応していて、逆にexecCommand使うとうまく動かなかったりする~~ **Firefoxはver55あたり(?)からJSで書き換えるとそれ以前の状態に戻すUndoができなくなり、さらにexecCommand('insertText')がtextareaに対しては使えないので、現状Undo対応する方法はなさそうです。** 何かご存知の方は教えてください…… * MS Edgeは何もしないでも元々Undo対応している模様 * IEではexecCommandで'ms-beginUndoUnit','ms-endUndoUnit'で挟んで書き換えるとUndo対応できる ### 簡単なデモ https://embed.plnkr.co/KSfSIgA1WZplUVKCAKKC/ ### 実際に使ってるコード(抜粋) ```js class EditorInput{ // ... // ※this.inputElmが対象になるtextarea replace(str, fromIdx, toIdx) { let inserted = false; if (str) { let expectedLen = this.inputElm.value.length - Math.abs(toIdx - fromIdx) + str.length; 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がtrueになるので失敗を検知して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) {} } } // ... } ```