IE9のXDomainRequestでCORSするときにjQuery.noop使うと通信中断するバグ

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

IE9とjQueryの相性が悪い

今日はこれに悩まされました。ある特殊な状況で発生する発見しづらいバグです。以前もお伝えしましたが、重要なので繰り返しますと、IE 6,7はCORSに対応していません。IE 8,9は特殊対応しています。IE 10から正式対応する予定です。ここでいう、特殊対応とは、IE独自のオブジェクトを使ってクロスドメイン通信をします。XDomainRequestオブジェクトです。そして、こいつがやらかしましたw。常に動かないならまだしも、動いたり動かなかったり微妙な感じなのです。。。

jQueryでは空を表すためにjQuery.noopを使う

これはjQueryでイベントハンドラを書くときによく使う書き方で、ハンドラの中身に何も処理が無いのであれば、何も無いを明示するためにjQuery.noopを使っています。これは、function(){}と同じ事なのですが、XDomainRequestくんは違うものと認識してCORSを中断してしまいます。Nullと空文字みたいな感じかな。

IE 8,9でCORSをするためにxdr.jsを使ってjQueryを拡張する

xdr.jsは、jQueryの記述をほとんど変更することなく、IE 8,9に対してCORS命令を書く事ができる優れものの拡張表現です。

jaubourg / ajaxHooks

jQueryのロード後に宣言することで有効になります。基本的にはコレでOKです。

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
<script src="xdr.js" type="text/javascript"></script>

jQuery.noop処理でCORS通信中断を回避

現在公開されているxdr.jsは、jQuery.noopによるバグ対策が施されていません。そこで、xdr.jsにパッチを充てました。

satoshi7 / ajaxHooks

以下は修正したコードです。

if ( window.XDomainRequest ) {
	jQuery.ajaxTransport(function( s ) {
		if ( s.crossDomain && s.async ) {
			if ( s.timeout ) {
				s.xdrTimeout = s.timeout;
				delete s.timeout;
			}
			var xdr;
			return {
				send: function( _, complete ) {
					function callback( status, statusText, responses, responseHeaders ) {
						xdr.onload = xdr.onerror = xdr.ontimeout = function() { };
						xdr = undefined;
						complete( status, statusText, responses, responseHeaders );
					}
					xdr = new XDomainRequest();
					xdr.onload = function() {
						callback( 200, "OK", { text: xdr.responseText }, "Content-Type: " + xdr.contentType );
					};
					xdr.onerror = function() {
						callback( 404, "Not Found" );
					};
					xdr.onprogress = function() { };
					xdr.ontimeout = function() {
						callback( 0, "timeout" );
					};
					xdr.timeout = s.xdrTimeout || Number.MAX_VALUE;
					xdr.open( s.type, s.url );
					xdr.send( ( s.hasContent && s.data ) || null );
				},
				abort: function() {
					if ( xdr ) {
						xdr.onerror = function() { };
						xdr.abort();
					}
				}
			};
		}
	});
}

まとめ

エンジニアの肩書きがあるならソーシャルコーディングして片っ端から問題解決しようよぜー!

参考情報

IE, XDomainRequest not always work

jQuery BUG TRACKER - BUILT-IN SUPPORT FOR XDOMAINREQUEST