Help us understand the problem. What is going on with this article?

java script(jquery)でのうっかり

More than 3 years have passed since last update.

webサイトでよく使われるjs(jquery)ですが、
ちょっとしたミスや勘違いによって様々な不具合が発生します。
そんなよくあるミスの中から、分かっているけど忘れた頃にうっかりやらかしてしまう。
そんなものを中心に紹介します。

テキストボックスで入力した数字の評価

たとえば入力した数字を比べる必要があり、下記ようにコーディングしました。

index.html
<html>
<head>
  <script type="text/javascript" src="jquery.min.js"></script>
  <script type="text/javascript" src="hoge.js"></script>
</head>
<body>
  A<input type="text" id="a"><br>
  B<input type="text" id="b"><br>
  C<input type="text" id="c"><br>
</body>
</html>
hoge.js
$(function() {
  $('#a, #b').bind('change', function() {
    if ($('#a').val() > $('#b').val()) {
      $('#c').val($('#a').val());
    } else {
      $('#c').val($('#b').val());
    }
  });
});

テキストボックスAとBに数字を入力し、大きいほうをCにセットするシンプルなものです。

このソースA(1) B(2)を入れれば確かにC(2)が入り一見正しいように見えます。
しかし、A(100) B(20)を入れた場合、C(100)ではなくC(20)となります。

何故か?

問題はココにあります。

hoge.js
if ($('#a').val() > $('#b').val()) {

ここで比較されているAとBは、文字列として比較されています。
文字列桁数に関係なく左側から順番に大小の比較がされるため、”100”という文字の”1”と、”20”という文字の”2”が比較され、”2”の方が大きいと判断されてしまいます。

これを回避するには、整数型にキャストする必要があります。
整数型へのキャストはparseInt()を使えばOK!

hoge.js
$(function() {
    if (parseInt($('#a').val(), 10) > parseInt($('#b').val(), 10)) {
      $('#c').val($('#a').val());
    } else {
      $('#c').val($('#b').val());
    }
  });
});

これで解決!
parseIntの第二引数の10は無くてもそれらしく動いてくれますが、
第一引数が10進数として認識してくれない事があるので付けた方が安心!!

ちなみに他にも入力値のチェックをしていない等の問題はありますが、
本筋とは関係ないので省略してあります。

【教訓】ちゃんと変数型を意識しよう!

※それでもついうかっかりやってしまうっていうのが問題なんですが・・・

ajaxの処理順

テキストボックスAに入力した値を2つのAPIに渡し、
そのAPIの結果を呼び出した順に出力したくて以下のコーディングをしました

index.html
<html>
<head>
  <script type="text/javascript" src="jquery.min.js"></script>
  <script type="text/javascript" src="hoge.js"></script>
</head>
<body>
  <input type="text" id="a"><br>
</body>
</html>
hoge.js
$(function() {
  $('#a').bind('change', function() {
    /* A処理 */
    $.ajax({
      type: 'post',
      url:  'http://xxxxxx.jp/a',
      data: {'xxx': $(this).val()},
      success: function(msg) {
        alert('A');
      }
    });
    /* B処理 */
    $.ajax({
      type: 'post',
      url:  'http://xxxxxx.jp/b',
      data: {'xxx': $(this).val()},
      success: function(msg) {
        alert('B');
      }
    });
  });
});

これを実行すると「A」→「B」の順番でalertが出力される事もあるのですが、
「B」→「A」となってしまう事もあります。

何故か?

それは、ajaxがデフォルト設定では非同期処理である事が原因です。

通常の処理(同期処理)では、前の行の処理が完了するのを待ってから次の行が処理されますので、必ず書いた順番で処理がされます。

しかし非同期処理では処理の完了を待たずに次の処理に移ってしまうため、AがBよりレスポンスが遅いとBが先に完了してしまいます。
そのため、Bのalertが先にでてしまうのです。

これを解決するのは大きく分けて2つの方法がります。

1. 同期処理にする
非同期である事が問題として考え、同期処理にしてしまう方法です。
ajaxに同期処理にするoptionが用意されており、これを使うことで大きくソースを変えずに解決することが可能です。

hoge.js
$(function() {
  $('#a').bind('change', function() {
    /* A処理 */
    $.ajax({
      async: false, ←コレ
      type: 'post',
      url:  'http://xxxxxx.jp/a',
      data: {'xxx': $(this).val()},
      success: function(msg) {
        alert('A');
      }
    });
    /* B処理 */
    $.ajax({
      async: false, ←コレ
      type: 'post',
      url:  'http://xxxxxx.jp/b',
      data: {'xxx': $(this).val()},
      success: function(msg) {
        alert('B');
      }
    });
  });
});

2. 入れ子にする
ajaxでレスポンスを受け取ったときに呼ばれるsuccess等の中で次のajaxを記述する方法です。

hoge.js
$(function() {
  $('#a').bind('change', function() {
    /* A処理 */
    $.ajax({
      type: 'post',
      url:  'http://xxxxxx.jp/a',
      data: {'xxx': $(this).val()},
      success: function(msg) {
        alert('A');
        /* B処理 */
        $.ajax({
          type: 'post',
          url:  'http://xxxxxx.jp/b',
          data: {'xxx': $('#a').val()},
            success: function(msg) {
            alert('B');
          }
        });
      }
    });
  });
});

通常はこちらが使われると思います。
階層が深くなってしまう欠点があるので、関数化するなどして可読性を上げるようにしましょう。

【教訓】上から順番に実行されるとは限らない事を意識しよう!

※ajaxのレスポンスが返ってくる前に再度changeイベントが発行されたらどうするの?という問題もありますが、また別の機会に。

まとめ

今回は「テキストボックスで入力した数字の評価」と「ajaxの処理順」についてでした。
「テキストボックスで入力した数字の評価」はシンプルな内容故に・・・
「ajaxの処理順」は処理の複雑化が進んだタイミングで気付かず・・・
そんな内容です。

「こんなの知っていて当たり前」と考えず、いつかやってしまうかもしれないと考え、頭の片隅にとどめておいてください。

Ingward
2008年度の新卒として「株式会社エイチーム」に入社。 紆余曲折を経て現在は「株式会社エイチーム引越し侍」及び「株式会社エイチームコネクト」に所属。 トラックボールをこよなく愛する30代。
a-hikkoshi
当社は、親会社である株式会社エイチームの経営理念をそのままに、引越しの比較サービス開始以降、大切にしてきた「三方よし」の理念を基本として、世の中に求められるサービスの創造を目指します。 一緒に働けるエンジニアを募集しております。下記URLよりご応募ください。 https://bit.ly/3lwf7QJ
https://hikkoshi.a-tm.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした