Nwht0xn1

フォームの変更内容がページ遷移で失われそうな場合に警告を出すCreated on 2016-04-29 by r7kamura

概要

変更内容が失われそうなときに警告が出るようになりました - WikiHub Help で説明した機能の実装について。

beforeunload

beforeunload というイベントがあるので、このイベントのコールバックを利用すれば、ページ遷移時の挙動を変更できる。

ページ遷移時に変更を検知すべきかどうか

全てのページにおいて変更を検知しようと試みる必要は無い。今回は、特定のセレクタを持つ要素が存在する場合に、変更検知を試みることにした。今回の例では .js-transition-warning がそれにあたる。このセレクタを持つ要素がページ内に存在する場合にのみ、変更検知を試みる。

<div class="js-transition-warning"></div>

警告時に表示するメッセージ

ブラウザのポップアップを利用して警告を表示するが、そのときに表示するメッセージを用意しなければならない。JavaScriptのコード上に直接用意しても良いが、HTMLを描画するサーバ側からメッセージを渡すほうがi18n対応の面で都合が良いと考え、上述のセレクタを持つ要素のdata属性を経由してメッセージを受け渡すことにした。今回の例では data-transition-warning-message という名前の属性を使う。

<div class="js-transition-warning data-transition-warning-message="このページから移動すると、変更内容は失われます。"></div>

submitするときは警告しない

form要素のsubmitイベントを監視して、このイベントが発行されたときに、beforeunloadイベントからコールバックを取り除くと良い。この都合から、上述したセレクタはform要素に付けておくと都合が良い。

<form ... class="js-transition-warning data-transition-warning-message="このページから移動すると、変更内容は失われます。">
  ...
</form>

変更があった場合だけ警告したい

フォームに何も変更が無い場合は、何も失われないので警告する必要は無い。そこで、フォームの元々の値を残しておいて、現在の値と差分を取って変更を検出している。

<input ... data-transition-warning-original-value="..." />

まとめ

以上の要素をまとめて適用してjQueryで適当に書いた結果が以下のコード。

jQuery(function ($) {
  $('.js-transition-warning').each(function () {
    var $this = $(this);
    var message = $this.attr('data-transition-warning-message');
    var handler = function () {
      var isAnyFieldChanged = $this.find('.js-transition-warning-tracked').is(function () {
        var $this = $(this);
        return $this.attr('data-transition-warning-original-value') !== $this.val();
      });
      if (isAnyFieldChanged) {
        return message;
      }
    };
    $(window).on('beforeunload', handler);
    $this.one('submit', function () {
      $(window).off('beforeunload', handler);
    });
  });
});