フォームなどで保存されていない内容を含むときに,ページを離れようとしている場合に次のようなアラートを出す.
Turbolinksを有効にしていて,form_for
でつくられた普通のフォームや,WYSIWYGエディタckeditorを使っているケースに対応してみる.
jQuery製の AreYouSure や dirtyForms でもいいが,細かい調整したかったので自作した.
検証環境
- Rails 5.1.4
- Turbolinks 5
- WYSIWYGとしてckeditorを使用している
普通フォームのDirtyCheck
beforeunload
イベントで window.appName.formDirty
だけチェックしていると,フォームがサブミットされたタイミングでもDirtyとみなしてしまうので, window.appName.formSubmit
でサブミットかどうかをチェックしている.
$(window)
// フォームの状態を初期化
.on('turbolinks:load', function () {
if (!window.appName) window.appName = {};
window.appName.formDirty = false;
window.appName.formSubmit = false;
window.appName.dirtyMessage = '保存していない内容が失われますが、よろしいですか';
$(':input').change(function () {
if (!window.appName.formDirty) window.appName.formDirty = true;
});
$('form').submit(function () {
window.appName.formSubmit = true;
});
})
// Turbolinksでページが切り替わるタイミング
.on("page:before-change turbolinks:before-visit", function () {
if (window.appName.formDirty) {
return confirm(window.appName.dirtyMessage);
}
})
// リロードされたり,タブやウインドウが閉じられる前のタイミング
.bind("beforeunload", function (event) {
if (!window.appName.formSubmit && window.appName.formDirty) {
event.returnValue = window.appName.dirtyMessage;
return event.returnValue;
}
});
CKEditorのDirtyチェック
すべてのCKEDITORのDirtyをチェックするカスタムメソッド hasDirtyForm
を実装する.
CKEDITOR.instancesは checkDirty メソッドを持つので,そのどれかがDirtyかチェックしているだけ.
//= require ckeditor/init.js.erb
//= require ./config
//`app/assets/ckeditor/config.js` では `CKEDITOR.editorConfig` を設定したり
// `dialogDefinition` イベントを設定しているが内容は割愛.
// .ckeditorが付いている要素をWYSIWYG化する
$(document).on('turbolinks:load', function () {
$('.ckeditor').each(function () {
CKEDITOR.replace($(this).attr('id'))
});
});
// すべてのCKEDITORのDirtyをチェックするカスタムメソッドを実装
CKEDITOR.hasDirtyForm = function () {
var editors = this.instances;
return Object.keys(editors).some(function (key) {
return editors[key].checkDirty();
});
};
page:before-change turbolinks:before-visit
と beforeunload
イベント時に CKEDITOR.hasDirtyForm()
のチェックをするようにする.
$(window)
•••
.on("page:before-change turbolinks:before-visit", function () {
if (window.appName.formDirty || CKEDITOR.hasDirtyForm()) {
return confirm(window.appName.dirtyMessage);
}
})
.bind("beforeunload", function (event) {
if (!window.appName.formSubmit && (window.appName.formDirty || CKEDITOR.hasDirtyForm()) {
event.returnValue = window.appName.dirtyMessage;
return event.returnValue;
}
});