Zepto.js の $.ajax にオブジェクトの配列を渡した時の不具合を直す

Zepto.jsjQuery と同じように使えるように互換性を保ちつつ、機能削減や軽量化・高速化が図られているライブラリ。ホワイトスペースなどを除去すれば 20kB 強くらいのサイズになるので、読み込み容量がシビアなモバイル向けのウェブサイトを作るときには特に重宝する。

Zepto は基本的には jQuery と互換性があるが、ときどき微妙なところで挙動が異なっているのが玉にきず。 Ajax リクエストを行うための関数である $.ajax もそんな傷入りの機能のひとつのようだ。

今回取り扱うのは、 $.ajax のリクエストパラメーターとしてオブジェクトの配列を渡した時の挙動が、 Zepto と jQuery とで異なっている不具合。ちなみに v1.0 時点での不具合なので、もっとあとのバージョンでは修正されているかもしれない。

jQuery

Zepto.js

パラメータにオブジェクトの配列を渡した時に、出力される文字列が異なっているのがわかる。

上のコードにも書かれているけど、 $.ajax の内部では $.param という関数が呼ばれている。この関数はオブジェクトを受け取って、 Ajax リクエストを行うための形式に変換する関数で、まさに挙動の差の原因となっている部分。 Zepto.js の吐き出し方は pAryObj[][a]=bcpAryObj[][a]=gh がそれぞれ何番目のオブジェクトに属するプロパティなのか判別しようがないので、 PHP でパラメーターを受け取るとおかしくなる(と思う。未検証だけど)。ここは jQuery の出力形式に統一したい。

挙動の差を埋める

以下のコードを Zepto.js の当該コードと置き換えればよろしい。Zepto.js v1.0 のコードを置換することを想定しているので、それ以外のバージョンの場合はお気をつけて。

function serialize(params, obj, traditional, scope){
  var type, objIsArray = $.isArray(obj), objIsObject = $.isPlainObject(obj) 
  $.each(obj, function(key, value) {
    type = $.type(value)
    if (scope) key = traditional ? scope : scope + '[' + (objIsObject || type == 'object' || type == 'array' ? key : '') + ']'
    // handle data in serializeArray() format
    if (!scope && objIsArray) params.add(value.name, value.value)
    // recurse into nested objects
    else if (type == "array" || (!traditional && type == "object"))
      serialize(params, value, traditional, key)
    else params.add(key, value)
  })
}

$.param = function(obj, traditional){
  var params = []
  params.add = function(k, v){ this.push(escape(k) + '=' + escape(v)) }
  serialize(params, obj, traditional)
  return params.join('&').replace(/%20/g, '+')
}

上記のコードは、 Zepto.js の GitHub リポジトリに上がっていたプルリクエストから抜粋した。6ヶ月前に起票されたプルリクだけれども今のところ取り込まれていない。しかし Zepto.js のコミッターの人も口出ししているので、そのうち取り込まれるのではないかと思う。

多くの人は、ライブラリの JS ファイルに直接手を入れるのは気が進まないと思う。その場合は以下のような感じでムリヤリオーバーライドする感じでいいのではないかと。