「jQueryを使えばいいじゃない」とよく言われるが、たかが外部サーバーからデータを取得するだけの事を自分で行えないのが嫌だったので、以前から自作したライブラリを使っていたが、この度、不具合を発見したので、修正版を公開しておきます。
過去の記事
WEBページ内で外部サーバーに設置してあるTEXTファイルを読んだり、書いたりするために、URLに対してデータをリクエストして、返り値を取得した後
イベント処理として関数を実行できるライブラリを作っていた。
[プログラム学習] JavaScript #4 「ファイル読み込み」
[プログラム学習] JavaScript #5 「ファイル書き込み」
改修版
(function(){
var $$ = {};
/**
* Ajax
*/
$$.ajax = {
createHttpRequest:function(){
//Win ie用
if(window.ActiveXObject){
try {
//MSXML2以降用;
return new ActiveXObject("Msxml2.XMLHTTP")
}
catch(e){
try {
//旧MSXML用;
return new ActiveXObject("Microsoft.XMLHTTP")
}
catch(e2){
return null
}
}
}
//Win ie以外のXMLHttpRequestオブジェクト実装ブラウザ用;
else if(window.XMLHttpRequest){
return new XMLHttpRequest()
}
else{
return null
}
},
/**
* XMLHttpRequestオブジェクト生成
*/
set:function( option ){
if(!option){return}
var httpoj = new $$.ajax.createHttpRequest();
if(!httpoj){return}
//open メソッド;
httpoj.open( option.method , option.url , option.async );
httpoj.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
//受信時に起動するイベント;
httpoj.onreadystatechange = function(){
//readyState値は4で受信完了;
if (httpoj.readyState==4){
//コールバック
option.onSuccess(httpoj.responseText);
}
};
//query整形
var data = [];
if(typeof(option.query)!="undefined"){
for(var i in option.query){
data.push(i+"="+encodeURIComponent(option.query[i]));
}
}
if(typeof(option.querys)!="undefined"){
for(var i=0;i<option.querys.length;i++){
data.push(option.querys[i][0]+"="+encodeURIComponent(option.querys[i][1]));
}
}
//send メソッド
if(data.length){
httpoj.send( data.join("&") );
}
else{
httpoj.send();
}
},
//コールバック関数 ( 受信時に実行されます );
onSuccess:function(oj){
//レスポンスを取得;
var res = oj.responseText;
//console.log(res);
//ダイアログで表示;
if(res && res.match(/^[a-z|$]/)){
eval(res);
}
}
};
//path-info Ex):p=location.href
$$.pathinfo = function(p){
var basename="",
dirname=[],
filename=[],
ext="";
var p2 = p.split("?");
var urls = p2[0].split("/");
for(var i=0; i<urls.length-1; i++){
dirname.push(urls[i]);
}
basename = urls[urls.length-1];
var basenames = basename.split(".");
for(var i=0;i<basenames.length-1;i++){
filename.push(basenames[i]);
}
ext = basenames[basenames.length-1];
return {
"hostname":urls[2],
"basename":basename,
"dirname":dirname.join("/"),
"filename":filename.join("."),
"extension":ext,
"query":(p2[1])?p2[1]:"",
"path":p2[0]
};
};
$$.urlinfo=function(uri){
if(!uri){uri = location.href}
var data={};
//URLとクエリ分離分解;
var query=[];
if(uri.indexOf("?")!=-1){query = uri.split("?")}
else if(uri.indexOf(";")!=-1){query = uri.split(";")}
else{
query[0] = uri;
query[1] = '';
}
//基本情報取得;
var sp = query[0].split("/");
var data={
url:query[0],
dir:this.pathinfo(uri).dirname,
domain:sp[2],
protocol:sp[0].replace(":",""),
query:(query[1])?(function(q){
var data=[];
var sp = q.split("&");
for(var i=0;i<sp.length;i++){
var kv = sp[i].split("=");
if(!kv[0]){continue}
data[kv[0]]=kv[1];
}
return data;
})(query[1]):[],
};
return data;
};
/**
* 直上の対象エレメントを取得
* param @ elm : target-element
* param @ type : [class , id]
* param @ value : string
**/
$$.getElementByParentNode = function(elm,type,value){
if(!elm){return null}
if(type === "class"){
if(elm.parentNode.className.indexOf(value)!=-1){
return elm.parentNode;
}
else{
return $$.getElementByParentNode(elm.parentNode,type,value);
}
}
else if(type === "id"){
if(elm.parentNode.id && elm.parentNode.id === value){
return elm.parentNode;
}
else{
return $$.getElementByParentNode(elm.parentNode,type,value);
}
}
};
window.$$LIB = $$;
return $$;
})();
解説
今回発見した問題点というのは、このAJAX処理を同一ページ内で連続して実行した時に、ライブラリ内で使っている変数が、後から実行した処理が上書きしてしまうという不具合を見つけた。
例えば、複数のリスト型エレメントがあった場合、そのリスト1つずつにサーバーに問い合わせてそれぞれ別の値を返す処理を入れた時に、通常のfor分内でこのajax処理を行なってしまうと、楽勝で結果がバグってしまうのである。
このバグを修正するのは、ぶつかっている変数の「httpoj」の部分をグローバル変数になってしまっているので、これをローカル変数にして、関数が呼ばれる度に完結させなければいけない。
そして、「createHttpRequest」関数をnew演算子を使用して、これにより関数を呼び出す度にインスタンス化されるので、結果グローバルから上書きされることがなくなるのだ。
以前のバージョンと変えたのは、set関数の中身だけで、使い方は互換性をもたせているので、ライブラリを更新するだけでバグが無くなるハズ。
使い方
ajax.set({
url:"http://hoge.com/***.php", //対象URL
methos:"post", //POST or GET
async:true, //true or false
query:{"data":"abcdefg"}, //queryをjson記述で書ける。
onSuccess:function(res){console.log(res)}
});
上記をサンプルとして、適宜内容を変更することでサーバー通信が可能になる。
今回のライブラリ改修で、連続アクセスも可能になるので、より強固なライブラリになったという事だ。
なんか、バグが治って、少しうれしい・・・
追記 : 送信Query仕様
POST送信を行う場合にQuery情報の登録方法は2通りの仕様を採用しています。
1. 配列方式
Ex) querys:[ “aa[]=1” , “aa[]=2” , “aa[]=3” ]
この方法のメリットは、PHPサーバーへのクエリ送信を行う場合にExの様に同じkey名をセットする事ができます。
2. オブジェクト方式
Ex) quert:{ “aa”:1 , “bb”:2 , “cc”:3 }
スタンダードな方法なので、通常はこちらを使うほうが分かりやすいかもしれません。
注意点
クエリ設定を行なってmethodを「GET」にすると、ライブラリ内で自動的にURLにクエリを追加する方式になりますが、古いブラウザなどは数KbのURLしか対応していないものもあるので、長文送信の場合はPOSTで行いましょう。
また、urlの箇所にGET記述を行う場合は、POST設定を行なってもurl部分はGET記述で送信されます。
これはサーバーセキュリティでPOSTとGETを切り分けている場合を想定しているので、ライブラリでの対応範囲を広げるための処置なので、ご自身で切り分けてご使用ください。
ajax処理で不具合を見つけたので改修をしました。
issue
「content-type」が「application/x-www-form-urlencoded」に固定されている。
dealing with
optionに「type」を追加して、「content-type」を個別にセットできるようにした。
※デフォルト(指定なし)は「application/x-www-form-urlencoded」
source
$$.ajax = {
createHttpRequest:function(){
//Win ie用
if(w.ActiveXObject){
//MSXML2以降用;
try{return new ActiveXObject("Msxml2.XMLHTTP")}
catch(e){
//旧MSXML用;
try{return new ActiveXObject("Microsoft.XMLHTTP")}
catch(e2){return null}
}
}
//Win ie以外のXMLHttpRequestオブジェクト実装ブラウザ用;
else if(w.XMLHttpRequest){return new XMLHttpRequest()}
else{return null}
},
/**
* XMLHttpRequestオブジェクト生成
*/
set:function(option){
if(!option){return}
var httpoj = new $$.ajax.createHttpRequest();
if(!httpoj){return;}
//open メソッド;
httpoj.open( option.method , option.url , option.async );
if(typeof option.type != "undefined"){
httpoj.setRequestHeader('Content-Type', option.type);
}
else{
httpoj.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
}
//受信時に起動するイベント;
httpoj.onreadystatechange = function(){
//readyState値は4で受信完了;
if (httpoj.readyState==4){
//コールバック
option.onSuccess(httpoj.responseText);
}
};
//query整形
var data = [];
if(typeof(option.query)!="undefined"){
for(var i in option.query){
data.push(i+"="+encodeURIComponent(option.query[i]));
}
}
if(typeof(option.querys)!="undefined"){
for(var i=0;i<option .querys.length;i++){
data.push(option.querys[i][0]+"="+encodeURIComponent(option.querys[i][1]));
}
}
//send メソッド
if(data.length){
httpoj.send( data.join("&") );
}
else{
httpoj.send();
}
},
//コールバック関数 ( 受信時に実行されます );
onSuccess:function(res){console.log(res)}
};
$$.ajax.set({
"url" :scriptPath + "service/protech/js/common.js",
"method":"get",
"async" :"true",
"type" :"text/plain",
"onSuccess":function(res){
eval(res);
}
});
注意点
type指定する時は、methodを「get」にしてください。
0 件のコメント:
コメントを投稿