UI改善!?jQueryで作るクールな保存ボタンのレシピ

jQueryを使って、Gmailっぽく処理完了メッセージを表示する方法を紹介します。

「保存(成功)」ボタンをクリックすると、非同期でサーバに通信を開始します。通信中は「保存中...」というメッセージを表示させておきます。

通信が完了したら、「保存しました。」というメッセージを表示させます。このメッセージは1秒後に消えます。

gmsave11

また、「保存(失敗)」ボタンをクリックすると、「保存できませんでした。」というメッセージを表示させます。このメッセージは5秒後に消えます。

イメージをつかむためのサンプル

以下のフォームのボタンをクリックしていただくと、実際の動作イメージをつかみやすいと思います。

別のタブで動作確認する場合は、こちらをクリックしてください!

レイアウト作成

まずはレイアウトを作成していきます。以下のように、form.htmlstyle.cssを同じディレクトリに作成します。

form.html

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <link rel="stylesheet" href="./style.css" />
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
    <script src="./jquery.timers.min.js"></script>
    <script src="./common.js"></script>
  </head>
  <body>
    <div id="wrapper">
      <form action="#" method="post">
        <div class="field_wrapper">
          <input type="text" name="dummy" size="32" maxlength="32" placeholder="名前" autofocus />
        </div>
        <div class="button_wrapper">
          <ul>
            <li>
              <button type="button" value="success">保存 (成功)</button>
            </li>
            <li>
              <button type="button" value="error">保存 (失敗)</button>
            </li>
          </ul>
        </div>
      </form>
    </div>
    <div id="message_box">
      <span></span>
    </div>
  </body>
</html>

style.css

/*
レイアウトを整える
*/
form {
  margin-top: 20px;
  border: 2px solid #ccc;
  background-color: #f0f0f0;
  padding: 8px 15px;
}
form input[type=text] {
  font-size: 18px;
}
form button {
  font-size: 18px;
  border: 1px solid #999;
  cursor: pointer;
  height: 30px;
}
form button:hover {
  border: 1px solid #666;
  cursor: pointer;
}
form .field_wrapper {
  margin-bottom: 10px;
}
form .button_wrapper > ul {
  margin: 0;
  padding: 0;
  list-style-type: none;
}
form .button_wrapper > ul > li {
  display: inline;
  margin-right: 8px;
}
form .button_wrapper > ul > li:last-child {
  margin-right: 0;
}
div#wrapper {
  width: 600px;
  margin: 0 auto;
}

レイアウトの確認

完成すると、以下の画像のようなフォームになります。

gmsave00

メッセージボックスの追加

通信中などに表示するメッセージボックスを追加します。先ほどのform.htmlのBODYタグの直前に、以下のコードを追加します。

form.htmlにメッセージボックス追加

    <div id="message_box">
      <span></span>
    </div>

また、メッセージボックス用のスタイルをstyle.cssに追加します。

style.cssにメッセージボックス用のスタイルを追加

/*
メッセージボックス
*/
#message_box {
  top: 10px;
  position: fixed;
  width: 100%;
  font-size: 15px;
  font-weight: bold;
  text-align: center;
  display: none;
}
#message_box span {
  padding: 6px 14px;
  text-align: center;
  box-shadow: 1px 1px 3px #ccc;
}
#message_box.progress span {
  background-color: #ffc;
  border: 2px solid #fc9;
}
#message_box.success span {
  background-color: #9f9;
  border: 2px solid #093;
}
#message_box.error span {
  background-color: #f99;
  border: 2px solid #f03;
}

メッセージボックスの確認

この段階で、ブラウザからメッセージボックスを確認することができます。ただし、メッセージボックスは非表示状態になっているので、確認するときはform.htmlのメッセージボックスにclassstyleを追加します。

メッセージボックス(処理中)の確認

    <div id="message_box" class="progress" style="display: block;">
      <span>保存中...</span>
    </div>

gmsave01

メッセージボックス(成功)の確認

    <div id="message_box" class="success" style="display: block;">
      <span>保存しました。</span>
    </div>

gmsave02

メッセージボックス(失敗)の確認

    <div id="message_box" class="error" style="display: block;">
      <span>保存できませんでした。</span>
    </div>

gmsave03

以上でレイアウトが完成しました。

ボタンクリックで通信中

レイアウトが完成したら、ボタンクリック時の動きをつけていきます。なお、この段階では、ボタンをクリックしたら、通信中表示させるまでを作成します。

form.htmlと同じディレクトリにcommon.jsを作成します。

common.jsの作成(「保存中...」表示まで)

$(function() {
  var $messageBox = $('#message_box');
  var $messageBoxInner = $messageBox.find('span');

  // ボタンを押せるようにします。
  $('button').removeAttr('disabled');

  $('button').click(function() {
    // 保存中のメッセージを表示します。
    requestStart('保存中...');
  });

  /**
    * 通信開始時の処理を行います。
    *   1. ボタンを無効にします。
    *   2. メッセージボックスのクラスをprogressにします。
    *   3. jQueryTimerを停止します。
    *   4. メッセージを設定して表示します。
    */
  function requestStart(message) {
    // ボタンを無効にします。
    $('button').attr('disabled', 'disabled');

    // メッセージの色をprogressにします。
    changeFormMessageClass('progress');

    // jQuery Timersのイベントを削除します。
    // ※ 削除しないと、次の通信の途中でメッセージが消える場合があります。
    $messageBox.stopTime();

    // メッセージを設定してから表示します。
    $messageBoxInner.text(message);
    $messageBox.fadeIn();
  }

  /**
    * メッセージボックスに設定されているクラスを変更します。
    */
  function changeFormMessageClass(className) {
    $messageBox.removeClass()
               .addClass(className);
  }
});

jquery.timers.jsの設定

このサンプルでは、「メッセージボックスを自動的に非表示にする処理」で、jQuery Timersというプラグインを使っています。そのため、ここから最新版をダウンロードし、jquery.timers.min.jsform.htmlと同じディレクトリに配置します。

jQuery Timersの使い方については、昔書いた記事があるので参考になれば幸いです。

この手順まで終えると、「保存(成功)」をクリックすると、「保存中...」と表示されるようになります。

JSONレスポンスの作成

「保存(成功)」・「保存(失敗)」ボタンをクリックしたときに非同期に呼ばれる、URLのレスポンス用のファイルを作成します。form.htmlと同じ階層に、success.jsonerror.jsonというファイルを配置します。

success.json

{"is_success":true}

error.json

{"is_success":false}

完成

最後に、非同期に./success.json./error.jsonを呼び、結果に応じてメッセージボックスを変更させれば完成です。完成形のcommon.jsのソースは以下のようになります。

$(function() {
  var $messageBox = $('#message_box');
  var $messageBoxInner = $messageBox.find('span');

  // ボタンを押せるようにします。
  $('button').removeAttr('disabled');

  $('button').click(function() {
    // 処理するURLを取得します。
    var url = './' + $(this).val() + '.json';
    // 保存中のメッセージを表示します。
    requestStart('保存中...');

    // 非同期でリクエストを投げます。
    $.ajax({
      dataType: 'json',
      url: url,
      success: function(res) {
        if (res.is_success) {
          // 通信終了時の処理をします。
          requestEnd('保存しました。', true, 1000);
        } else {
          // 通信終了時の処理をします。
          requestEnd('保存できませんでした。', false, 5000);
        }
      }
    });
  });

  /**
    * 通信開始時の処理を行います。
    *   1. ボタンを無効にします。
    *   2. メッセージボックスのクラスをprogressにします。
    *   3. jQueryTimerを停止します。
    *   4. メッセージを設定して表示します。
    */
  function requestStart(message) {
    // ボタンを無効にします。
    $('button').attr('disabled', 'disabled');

    // メッセージの色をprogressにします。
    changeFormMessageClass('progress');

    // jQuery Timersのイベントを削除します。
    // ※ 削除しないと、次の通信の途中でメッセージが消える場合があります。
    $messageBox.stopTime();

    // メッセージを設定してから表示します。
    $messageBoxInner.text(message);
    $messageBox.fadeIn();
  }

  /**
    * 通信終了時の処理をします。
    *   1. 表示するメッセージを設定します。
    *   2. 成功、失敗に応じてクラスを設定します。
    *   3. 規定の間隔後にメッセージを非表示にします。
    *   4. ボタンを有効にします。
    */
  function requestEnd(message, isSuccess, displayDuration) {
    // メッセージを設定します。
    $messageBoxInner.text(message);

    // メッセージボックスのクラスを切り替えます。
    changeFormMessageClass(isSuccess ? 'success': 'error')

    // メッセージ表示して、規定のミリ秒後に非表示にします。
    $messageBox.oneTime(displayDuration, function() {
      $(this).fadeOut(300);
    });

    // ボタンを押せるようにします。
    $('button').removeAttr('disabled');
  }

  /**
    * メッセージボックスに設定されているクラスを変更します。
    */
  function changeFormMessageClass(className) {
    $messageBox.removeClass()
               .addClass(className);
  }
});

ソースコード

今回ソースコードをgithub上に公開しました。

https://github.com/yusukemurayama/blog-samples/tree/master/201506/gmsave

備考

この記事に埋め込んだサンプルの注意点

この記事に埋め込んだフォームでは、「保存中...」の表示を確認しやすくするために、githubに公開したソースとほんの少しだけ変えています。具体的には、レスポンスを受け取ってからsetTimeoutで1秒程スリープさせています。

      success: function(res) {
        setTimeout(function() {  // 動作確認用に、1秒スリープさせています。
          if (res.is_success) {
            // 通信終了時の処理をします。
            requestEnd('保存しました。', true, 1000);
          } else {
            // 通信終了時の処理をします。
            requestEnd('保存できませんでした。', false, 5000);
          }
        }, 1000);
      }

この記事が役に立った場合、シェアしていただけると励みになります!!