webサイトでよく使われるjs(jquery)ですが、
ちょっとしたミスや勘違いによって様々な不具合が発生します。
そんなよくあるミスの中から、分かっているけど忘れた頃にうっかりやらかしてしまう。
そんなものを中心に紹介します。
テキストボックスで入力した数字の評価
たとえば入力した数字を比べる必要があり、下記ようにコーディングしました。
<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>
$(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)となります。
何故か?
問題はココにあります。
if ($('#a').val() > $('#b').val()) {
ここで比較されているAとBは、文字列として比較されています。
文字列桁数に関係なく左側から順番に大小の比較がされるため、”100”という文字の”1”と、”20”という文字の”2”が比較され、”2”の方が大きいと判断されてしまいます。
これを回避するには、整数型にキャストする必要があります。
整数型へのキャストはparseInt()を使えばOK!
$(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の結果を呼び出した順に出力したくて以下のコーディングをしました
<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>
$(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が用意されており、これを使うことで大きくソースを変えずに解決することが可能です。
$(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を記述する方法です。
$(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の処理順」は処理の複雑化が進んだタイミングで気付かず・・・
そんな内容です。
「こんなの知っていて当たり前」と考えず、いつかやってしまうかもしれないと考え、頭の片隅にとどめておいてください。