fc2ブログ

[jQuery] 配列データを一旦格納し、イベント発生時にAjaxで送信する

この前、「後でブログに書きたい」と書いてたことを今更まとめる。

JavaScriptで配列データを作成し、そのデータをすぐに送信するのではなく、送信ボタンを押したときにAjaxで送信するようなケースを考える。
この場合、作成した配列データをどこかに格納しなければいけない。
配列をグローバル変数にして格納してもいいのだが、jQueryのdataメソッドが便利なのでそれを使用する。

処理の概要は、
1. サーバーへ送信する配列データを作成
2. 作成した配列データを jQueryのdataメソッドでDOM要素に格納
3. 送信ボタン押下時に、jQueryのdataメソッドでDOM要素から配列データを取り出す
4. 取り出した配列データを jQuery.ajax の引数に設定してサーバーへ送信
となる。

まず、サーバーへ送信する配列データを作成し、DOM要素に格納する箇所のソースコード例
  var senddata = {};  // サーバーへ送信するデータ
  for(var i=0;i<10;i++){
    // 送信データとして乱数を作成
    senddata[i] = Math.floor(Math.random()*10) + 1;
  }
  // サーバーへ送信ボタン追加
  var $div = $("#div1");
  var $button = $("<button/>").addClass("sbtn").attr("id","sbtn");
  $button.text("サーバーへ送信");
  $button.data("senddata",senddata);  // 送信ボタンに送信データを格納
  $button.click(ajaxsend);
  $div.append($button);
ここで、サーバーへ送信する配列のデータを、大カッコ [ ] ではなく中カッコ { } で宣言している。
jQueryのdataメソッドでは任意のObjectを格納できるのでどちらでも良いのだが、jQuery.ajaxの引数として連想配列(ハッシュ)を設定する必要があるため、ここでは { } で宣言している。
そのため、厳密には配列ではなく「0からの連番をキー値とする連想配列」となる。

次に、送信ボタン押下時にDOM要素から配列データを取り出し、Ajaxで送信する箇所のソースコード例
var ajaxsend = function(){
  $.ajax({url: "./ajaxtest.php",  // 実行されるURL
    type: "POST",
    data: $("#sbtn").data("senddata")  // 送信データ
  });
};

受信した側では、PHPなら
foreach($_POST as $key => $value)
などで配列データを取り出せばよい。

自分が作成した、一通りの動作を確認できるテストコードは以下のとおり。(拡張子zipでアップロードできなかかったので、リンク先を保存して拡張子をzipに変えてください)
jQueryのdataメソッド+Ajaxテストコード
サーバー側のコードとして ajaxtest.php も作ったが、データ送受信確認用コードなので入力チェックやエラー処理などは無い。

以上。この記事を書くためにテストコードを作成したのだが、あまり意味無かったかなぁ。
スポンサーサイト



jQueryでDOM要素を作成し、DOMツリーを構築する方法

「jQuery DOM ツリー 構築 or 生成」などで検索しても、jQueryでDOM要素を作成/生成し、DOMツリーを1から作る方法が出てこなかったので、せっかくだから基礎の基礎をまとめてみる。

1)jQueryオブジェクトを作る
jQueryオブジェクトとは、jQueryの関数(メソッド)が使用できるDOM要素(またはその配列)のこと。DOMツリーに含まれてない要素も含む。
jQueryオブジェクトは、セレクタで抽出して取得することが多いが、1から作ることもできる。
jQueryオブジェクトの作成には、$()関数を使用する。
<例>
// <div></div>という要素のjQueryオブジェクトを作成し、$jqObjに格納する
var $jqObj = $("<div/>");  
変数名の先頭に $ をつける必要は無いが、「jQueryオブジェクトが格納されている変数であること」を明示するために $ を付けた方が、ソースコードを読み易い。

2)jQueryオブジェクトに属性などを追加する
1)で作成したjQueryオブジェクトに、
 attr()で属性を追加
 addClass()でCSSクラスを追加、css()でCSSプロパティを設定
 text()でテキストを追加
などを行い、子要素を作成する。
詳細はAttributes APIなどを参照。

3)親要素を取得し、jQueryオブジェクトを子要素として登録する
親要素をセレクタなどで抽出する。
抽出した親要素に対して、2)で作成した子要素をappend()などで登録し、DOMツリーを構築する。
<例>
var bcolorArr = new Array(略);  // 子要素の色
var $div = $("#main");  // 親要素取得
var $jqObj;  // 子要素
for(var i=0;i<bcolorArr.length;i++){
  //  jQueryオブジェクトを作成し、CSSクラス、CSSプロパティ、id属性を付与する
  $jqObj = $("<div/>").addClass("box").css("backgroundColor",bcolorArr[i])
    .attr("id","id" + (i+1));
  // jQueryオブジェクトを子要素として追加する
  $div.append($jqObj);
}
DOM要素を追加する関数(メソッド)の詳細は、Manipulation APIなどを参照。

<body>を親要素とする場合は、
$("body").append($jqObj);
とすればよい。

以上です。
自分が見たいくつかのjQuery入門サイトでは、ここら辺についての記述は無かった。

昔懐かしの記憶力ゲームがやっと完成

7/26の記事に書いた、
「複数のボックスがあり、ボックスが一つずつ点滅する。
ボックスが点滅した順番にクリックすると点数が入る」
という、昔遊んだ気がする記憶力ゲームがやっと完成した。

こちら → 懐かしの記憶力ゲーム

いろいろと回り道したわりに、一週間ちょっとで出来たことには満足かな。
イベントの並列実行に起因するバグをすでに一つ見つけているけど、
仕様を一部変える必要があり、直すのに時間がかかりそうだ。

音が出るようにしたいが、音源を作るところから調べなきゃいけないなぁ。
デザインがかなりショボいが、それは今のところ優先順位が低い。

jQueryで、異なる要素のアニメーションを順番に実行する 改

一昨日書いた記事で「jQueryで、異なる要素のアニメーションを順番に実行する」方法を紹介したが、要素の数が大きくなると期待した動作にならない場合がある。

一昨日の記事の方法は、
for(var i=0;i<3;i++){
  $("#id"+(i+1)).delay(interval).fadeOut(500).fadeIn(500);
  interval += 1500;
}
としているが、この場合
  $("#id"+(i+1)).delay(interval).fadeOut(500).fadeIn(500);
が要素の数だけ並行して実行されることになる。
監視するタイマーが多いだけでも問題なのだが、
CPU使用率が高くなるなどでJavaScriptの処理が不安定になると、要素のdelay時間にバラつきが生じ、各要素のfadeOut/fadeInが等間隔に見えなくなる。

この問題の解決方法として、「jQueryのキューを使用して異なる要素のアニメーション実行順序を制御する方法」を考えた。

アニメーションを実行する要素の例として10個のboxを作る。
更に、キューを作成するダミー要素を一つ作る。
// boxを10個作成 (boxのcssは略)
var $div = $("#main");  // htmlに記述している親ノード
var $box;
for(var i=0;i<10;i++){
  $box = $("<div/>").addClass("box").attr("id","id" + (i+1));
  $div.append($box);
}

// ダミーdiv作成
var $dim = $("<div/>").attr("id","dimdiv")
  .css({"width":"1px","height":"1px","clear":"both"});
$("body").append($dim);

boxを、id1からid10まで順番に点滅させる関数は以下のようになる。
lockflg = false;  // true ならアニメーション中。アニメーション中はイベントを無視する
var blinkbox = function(){
  // アニメーション中はイベントを無視
  if(lockflg){return false;}
  lockflg = true;
  
  var blinkseq = [];  // 点滅するboxの番号を順番に並べた配列
  for(var i=0;i<10;i++){
    blinkseq[i] = i+1;
  }
  var blinktime = 400;  // fadeIn/fadeOutそれぞれに要する時間(ミリ秒)
  var interval = blinktime * 3;  // 次の点滅処理を行うまでのwait時間
  var $dim = $("#dimdiv");  // ダミーdivタグ
  var calledno = 0;  // callbackfが何度呼ばれたかカウント用
  // ダミーdivタグのアニメーション後に呼ばれるcallback関数
  var callbackf = function(){$("#id"+ blinkseq[calledno]).fadeOut(blinktime).fadeIn(blinktime);
                calledno++;};
  
  // boxを順番に点滅させる
  for(var i=0;i<blinkseq.length;i++){
    $dim.toggle(1,callbackf);
    if(i==(blinkseq.length - 1)){
      // 最後にlockflgをfalseにする
      $dim.toggle(blinktime * 2,function(){lockflg = false;});
    }else{
      $dim.delay(interval);
    }
  }
}
ダミー要素で無害なアニメーションを実行し、そのcallback関数として行ないたいアニメーションを実行する。アニメーション実行順序は、jQueryが管理するダミー要素のキューにより保証されている。
ダミー要素の toggle(1) 実行後に、callback関数の処理と delay(interval) が並行して実行される。
PCの高負荷などで少々実行時間に狂いが生じても、次の toggle(1) 実行後のcallback関数の処理と delay(interval) は同時に実行されるので、見た目上のアニメーション処理間隔に大きな狂いは生じない。
同時に監視するタイマーは、boxの数にかかわらず2つ(callback関数のアニメーション処理と delay(interval) )で済む。
<2010/8/3 追記>
アニメーション中にlockflgがfalseになる問題を修正。
最後のdurationを1からblinktime*2に修正。
ダミーdivタグのアニメーションがfadeOutだと、非表示の場合アニメーションがスキップされるので、toggleに修正。
<追記ここまで>

なお、callback関数で
var calledno = 0;  // callbackfが何度呼ばれたかカウント用
// ダミーdivタグのアニメーション後に呼ばれるcallback関数
var callbackf = function(){$("#id"+ blinkseq[calledno]).fadeOut(blinktime).fadeIn(blinktime);
              calledno++;};
と関数の外で宣言した変数 calledno を使っているのは、クロージャの性質を利用している。
ループカウンタを引数として関数に渡しても、callback関数は実行時のループカウンタを参照するため期待した動作にならない。
勉強しててよかったクロージャ。この記事の(2)にクロージャの説明があります。

jQueryで、異なる要素のアニメーションを順番に実行する

jQueryでアニメーションを実行する処理でいろいろとハマったので、
昨日と今日と2日がかりで調べたことをまとめる。

jQueryのEffects APIには、animateやslideUp/slideDownなど、durationを指定することでアニメーションを行うメソッドがいくつかある。
同一要素に対してアニメーションを実行すると、アニメーションが順番に実行される。
これは、jQueryが要素ごとにアニメーション処理用のキューを管理しているからで、キューに格納された処理は前の処理が終わらないと実行されない。

アニメーションをメソッドチェーンでつなぐ必要は無く、
$("#id1").fadeOut(500).fadeIn(500).hide(500).show(500)
  .fadeOut(500).fadeIn(500);
でも
$("#id1").fadeOut(500);
$("#id1").fadeIn(500);
$("#id1").hide(500);
$("#id1").show(500);
$("#id1").fadeOut(500);
$("#id1").fadeIn(500);
でも順番に実行される。

ただし、(少なくともjquery-1.4.2では)durationを指定しない処理はキューに格納されないようで、
$("#id1").fadeOut(500).fadeIn(500).hide().show(500)
  .fadeOut(500).fadeIn(500);
とすると hide() と fadeOut(500) が同時に実行されて期待した動きにならない。

さて、本題の「異なる要素のアニメーションを順番に実行する方法」。
要素が異なるとキューも異なるので、
for(var i=0;i<3;i++){
  $("#id"+(i+1)).fadeOut(500).fadeIn(500);
}
とすると、3つの要素が同時に点滅してしまう。

delay()メソッドを使用することで、3つの要素を順番に点滅させることができる。
<注意:delay()メソッドが使用できるのは version 1.4 以降のjQuery>
指定した数字(単位はミリ秒)だけキューの処理を遅延させることができるので、
var interval = 0;
for(var i=0;i<3;i++){
  $("#id"+(i+1)).delay(interval).fadeOut(500).fadeIn(500);
  interval += 1500;
}
とすればいい。
interval に追加する数字により、次の要素が処理するまでの間隔を制御できる。
上記は interval += 1500 なので、次の要素が点滅するまでの間隔は500ミリ秒となる。
<2010/8/1追記>
上記の方法では、要素の数が大きくなると点滅が等間隔にならない場合があります。
修正版を jQueryで、異なる要素のアニメーションを順番に実行する 改 に記述しています。
<追記ここまで>

delay()はあくまでもキューの実行を遅延するので、durationを指定しない処理には効果が無いことに注意する必要がある。
durationを指定しない処理や、アニメーション以外の処理を制御したい場合は、setTimeout等を使ってキューを自作するか、以下のようなプラグインを使用する。
指定した処理をキューに追加して後でまとめて実行する jQuery Delay プラグイン

せっかくなので、処理実行中はロックをかけてイベントを受け付けない処理を、キューを利用して作ってみた。
// lockflgは、既にグローバル変数として初期値falseで宣言されている。
if(lockflg){return false;}  // lockflg == true なら処理しない
lockflg = true;
var interval = 0;
for(var i=0;i<3;i++){
  $("#id"+(i+1)).delay(interval).fadeOut(500).fadeIn(500);
  interval += 1500;
}
$("#id3").fadeIn(1,function(){lockflg = false;});  // lockflgをfalseにするためのダミー処理
最後に処理される要素のキューの最後尾に、害の無いダミーのアニメーションを追加し、そのcallback関数でフラグをオフにする。
こういったダミー処理で行うやり方は推奨されないんだろうなぁ。

ダミーのアニメーションを使わない処理も作ってみた
if(lockflg){return false;}  
lockflg = true;
var interval = 0;
for(var i=0;i<3;i++){
  if(i==2){
    // 最後にlockflgをfalseにする
    $("#id"+(i+1)).delay(interval).fadeOut(500).fadeIn(500,function(){lockflg = false;});
  }else{
    $("#id"+(i+1)).delay(interval).fadeOut(500).fadeIn(500);
  }
  interval += 1500;
}
ダミーを使う場合に比べると、ループのたびに余計な判定が入るが、多分些細な負荷だろう。

jQueryを使って、9個のボックスを3×3に配置

以下のようなページを作ろうと考えた。
「複数のボックスがあり、ボックスが一つずつ点滅する。
ボックスが点滅した順番にクリックすると点数が入る」
昔こんな記憶力ゲームがあったなぁと思って。

とりあえず、ボックスを9個、3×3に配置することにした。
やりかたの候補は以下の3つ。

・テーブルを使って配置
display: table; display: table-row; display: table-cell;
border-collapse: separate; border-spacing: 3px;
あたりを使う。

・floatを使って配置
float: left; clear: left;
margin-left: 3px; margin-top: 3px;
あたりを使う。

・positionを使って配置
position: absolute;
top: なんたらpx; left なんたらpx;
あたりを使う。

jQueryを使わずに、手打ちでHTMLとCSSを組んでやってみたところ、
テーブルもfloatも、行が変わるごとに display: table-row; や clear: left; を入れるのが何か気に入らない。
あと、テーブルもfloatもレイアウトの自由度が低い。
というわけで、positionを採用。

例によってHTMLには
<div class="main"></div>
だけ記述して、jQueryでボックスを配置した。

CSSは以下のとおり。
/* 背景 */
.main {
  display: block;
  border-style: solid;
  border-width: 1px;
  position: relative;
  margin-left: auto;
  margin-right: auto;
}

/* ボックス */
.box {
  display: block;
  border-style: solid;
  position: absolute;
}

JavaScriptのソースコードは以下のようになった。
当然と言えば当然なのだが、処理のほとんどがtopとleftの値を求める処理だ。
(function($) {
/* 定数 */
var aconst = {
  boxsidenum: 3,  // boxの一辺の個数 3x3
  boxwidth: 100,  // boxノードの width と height
  boxmargine: 3,  // boxノードの上左マージン
  boxbordwdh: 1  // boxノードの線の太さ
}
var boxcolord = new Array(略);  // boxの色

/* boxの行番号と列番号を求める
* boxnum boxの番号(1からの連番)
* return {raw: 行番号, col: 列番号}
*/
var getposition = function(boxnum){
  var rtnobj = {raw: 0, col: 0};
  if(isNaN(boxnum)){return rtnobj;}
  rtnobj.raw = Math.floor((boxnum -1) / aconst.boxsidenum) + 1;
  var remainder = boxnum % aconst.boxsidenum;
  if (remainder == 0){
    rtnobj.col = aconst.boxsidenum;
  }else{
    rtnobj.col = remainder;
  }
  return rtnobj;
};

$(document).ready(function() {
  // 親ノードを作成
  var $div = $("div.main:first");  // 親ノード
  var mainwidth;  // 親ノードの width と height
  mainwidth = aconst.boxsidenum * (aconst.boxwidth + aconst.boxmargine + aconst.boxbordwdh * 2) + aconst.boxmargine;
  $div.css({"width": mainwidth + "px", "height": mainwidth + "px"});
      
  // boxノードを作成
  var $box;  // boxノード
  var boxnum = aconst.boxsidenum * aconst.boxsidenum;  // boxの個数
  var boxposition;  // ボックスの行番号と列番号 {raw: 行番号, col: 列番号}
  var topval,leftval;  // boxノードのtopとleftの値
  for(var i = 0; i < boxnum; i++){
    $box = $("<div/>").addClass("box");
    boxposition = getposition(i+1);
    topval = (boxposition.raw -1) * (aconst.boxwidth + aconst.boxmargine + aconst.boxbordwdh * 2) + aconst.boxmargine;
    leftval = (boxposition.col -1) * (aconst.boxwidth + aconst.boxmargine + aconst.boxbordwdh * 2) + aconst.boxmargine;
    $box.css({"backgroundColor": boxcolord[i],
        "width": aconst.boxwidth + "px", "height": aconst.boxwidth + "px",
        "borderWidth": aconst.boxbordwdh + "px",
        "top": topval + "px", "left": leftval + "px"});
    $div.append($box);
  }
});

})(jQuery);
うわ、同名の変数boxnumを別の意味で使ってる。

これだけのことをやるのに1時間半かかりました。明日はどこまでできるかな。

jQueryでDOM要素を作成・追加してanimate

昨日の続き。

HTMLには
<div class="main"></div>
だけ記述して、クリックして動かすボックスをjQueryで作成することにした。

<2010/8/6 追記>
このページはソースコードを紹介しているだけです。
jQueryでDOM要素を作成し、DOMツリーを構築する方法については、こちらの記事で詳細をまとめました。
<追記ここまで>

cssは以下のとおり。
/* 背景 */
.main {
  display: block;
  width: 500px;
  height: 300px;
  position: relative;
  margin-left: auto;
  margin-right: auto;
}
/* 動かすボックス */
.box {
  display: block;
  width: 100px;
  height: 100px;
  position: absolute;
  left: 0px;
}

ボックス作成部分のソースコードは以下のようになった。
var boxcolor = new Array("#66FFFF","#66CCFF","#6699FF");  // 動かすboxの色

$(document).ready(function() {
  // 動かすboxを構築
  var $div = $("div.main:first");  // 親ノード
  var $box;  // boxノード
  for(var i = 0; i < boxcolor.length; i++){
    $box = $("<div/>").addClass("box");
    $box.css("background-color",boxcolor[i]);
    $box.css("top",String(100 * i) + "px");
    $div.append($box);
  }
  $("div.box").click(movebox);
});

ボックスをクリックしたときの動作部分は以下のとおり。
右側に動いたのをクリックすると左側に戻る処理をどうやって実装しようかと考えたが、安易にフラグを使用した。
/* 定数 */
var aconst = {
  moveAttr: "moved",    // 要素にセットする属性名
  moveAfter: "400px",    // move後の offset left
  moveBefore: "0px",    // move前の offset left

  pdur: "normal",    // animateのduration 数値で指定する場合はミリ秒
  peasing: "swing"  // animateのeasing
}

var movebox = function(){
  var movedflg = $(this).attr(aconst.moveAttr);  // move後 "true" move前 "false"
  var leftval;  // animate後のleftの値
  if(movedflg == "true"){
    // move後
    leftval = aconst.moveBefore;
    movedflg = "false";
  }else{
    // move前
    leftval = aconst.moveAfter;
    movedflg = "true";
  }
  $(this).animate({left: leftval}, aconst.pdur, aconst.peasing,
    function(){$(this).attr(aconst.moveAttr,movedflg);} );
}

true/false のフラグを該当ボックスの属性にセットするのだが、セットするとbooleanではなくstringになるので怪しいソースになってしまった。フラグの値を"right","left"にした方がよかったか。

今日は大してハマらずにサクサクできた。jQueryのプラグインを1つ読んだことがかなり効いてる。

jQueryで何か動かすものを作ってみたが・・・

jQueryの animate 関数を使って何か動くページを作ることにした。
とりあえず、<div>でボックスを作ってそれを動かすことにする。

append関数などを使って、jQueryでガシガシとDOMを構築しようと思ったが、思ったイメージを実現するのに時間がかかり断念。
ゼロから作るのだから、細かく分けて少しずつ実現していこう。
ということで、ボックスはHTMLに直接書いて、属性はCSSに直接書いた。

書いたJavaScriptコードはこれだけ。
(function($) {

var animetest = {
  movebox: function(){
  $(this).animate({left: "400px"});
  }
}

$(document).ready(function() {
  $("div.box1").click(animetest.movebox);
});

})(jQuery);

わざわざ animetest.movebox を外部で定義しなくても、これだけなら
$("div.box1").click(function(){$(this).animate({left: "400px"});});
でいいんだけど、これから機能を追加していく予定なので。

Firebugを使ってテストしてみたら見事にハマった。
animate関数を使うところにブレイクポイントをセットして、jQueryの中へステップインするとアニメーションされない。
そして一時的にCPU使用率が100%になって固まる。
実行ステップを見てると、何となく変な動きをしている。
jQueryの中へステップインしなければきちんとアニメーションされる。
ネットで調べてみたところ、Firebugのバグっぽい?よくわからないので放置。

今日調べてわかったCSSのテクニック
・text-align: center; のように、ブロック要素を中央揃えで表示する方法
margin-left: auto;
margin-right: auto;
をCSSで設定する。
ただしIE6では中央揃えが行われない場合がある。
IE6対策方法もあったけど、CSSが複雑になるので多分自分は使わない。

jQueryプラグインのソースコードを読む(4日目にして完了)

昨日に引き続き、あるjQueryプラグインのソースコードを読んだ。
調べることもあまり無く、244行(空行コメント行含む)を1時間かけて読み、晴れてソースコードを読了した。

4日間(計9時間)かけてソースコードを読んで
 var $obj = $('<div/>') のようにjQueryオブジェクトを作成して、
 DOM操作系のjQuery関数をガンガン使ってDOMを構築するテクニック
を学んだ気になった。

CSSと animate 関数を使って何か作りたいけど、明日は時間取れるかなぁ。

jQueryプラグインのソースコードを読む(3日目)

昨日に引き続き、あるjQueryプラグインのソースコードを読んだ。
jQueryの関数をたくさん調べたが、動作確認をすることも無く、昨日に比べるとサクサク読むことができた。

今日わかったこと
・jQueryの animate 関数を使って、アニメーションでstyleの値を変化させることができる
それ以外にも、細かいテクニックなどを個人用チートシートに追加したが割愛

昨日は3時間で160行しか読めなかったが、今日は3時間で463行読めた。約3倍だ。
(どちらも空行コメント行含む)
このペースでいけば、明日でソースコードを読了できる。

とりあえずの目標は「動きのあるページを作ること」なので、ソースコードを読み終わったらCSSの勉強をして、それからjQueryを使って何かを作りたい。
プロフィール

himax64

Author: 南西
30代後半の無職です。
就活もせずダラダラ生きてます。
作ったもの

最新記事
人気記事
検索フォーム
カテゴリ
月別アーカイブ
最新コメント
最新トラックバック
RSSリンクの表示
QRコード
QRコード
カウンター