読者です 読者をやめる 読者になる 読者になる

BattleProgrammerShibata

ある日は誰かと戦い、ある日は何かと戦い、そしてある日は自分と戦うのだろう、そういう生き物。

IE11 で開発者ツールを立ち上げていないと ConsoleAPI や Ajax キャッシュ問題で稀によくしぬ話

今更案件 JavaScript Backbone.js

GoogleChrome でも Firefox でも動くのに、 IE11 だけで上手く JavaScript が動かない問題に遭遇した。いずれもサーバとの通信に絡んだ部分である。

IE 独特の動かない JavaScript かなーと思い F12 キーを叩き開発者ツールを立ち上げて動きを確かめてみると、さきほどまで動かなかったはずのあらゆる機能が期待した挙動を始めた。開発者ツールを閉じると、元に戻った。開発者ツールを立ち上げると、起動すると、期待した挙動を始めた。なんだ、ドキュメントモードか? <!doctype html> と宣言していれば最新の標準モードになるのではなかったか? いやしかしユーザーエージェントは最新版のそれだぞ。ええ? 開発者ツールを立ち上げていないとアプリケーションサーバに吐かれるはずのログが吐かれていない??? クソッタレ、俺が何をしたって言うんだ。クソ、もういい分かった IE11 はサポート対象外だ。それがいい、そうしよう。

偉い人「だめでしょ」
僕「ですよね」

原因

結論からいうと、原因は次の2つだった。

  • console オブジェクトの存在
  • Ajax キャッシュ問題

やはりどちらも IE 独特の問題であった。

1.console オブジェクトの存在

WEB アプリケーション開発ではしばしば、デバッグのために console.log('hoge'); を随所に埋め込むことがある。しかし IE を使う場合には注意が必要だ。開発者ツールが起動していないとき、彼らは console.log を理解することができないのである。2015年6月現在、現行の IE11 についても同様のようだ。 根本的開発を図るのであれば「そもそも console を吹き飛ばせ」が一番だが、外部ライブラリにそれらが埋め込まれているパターンもある。CDN で読み込んでいたら編集も出来ない。

そこで console オブジェクト を記述しつつ、その問題を回避する方法をとる。 例えば次のような即時関数をあらかじめ実行しておくと有効である。

(function () {
    // window.console が未定義なら、オブジェクトにする
    if (typeof window.console === "undefined") {
         window.console = {}
    }
    // window.console.log が function でないならば、空の function を代入する
    if (typeof window.console.log !== "function") {
         window.console.log = function () {}
    }
})();

2.Ajax キャッシュ問題

もう一つの IE 独特の挙動として「Ajax 通信の内容をキャッシュする」というものがある。 WEB アプリケーションでキャッシュを回避するといえば、 meta タグ を記述することがあるが…。

<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Cache-Control" content="no-cache">

これはあくまでも「html 文書のキャッシュ回避」であり、 Ajax 通信のキャッシュに対しては無意味らしい。 この問題も JavaScript を用いることで問題解決が図れる。ここでは2つのアプローチを紹介する。

$.ajax メソッド の利用時に属性として定義する

$.ajax({ 
    url: '/hoge', 
    type: 'GET', 
    cache: false, // キャッシュさせない
    success: function (data) { 
        doSomthing(data);
    } 
}); 

属性として直接書いてしまう方法だ。一つ一つに書くのはやや手間ではあるが、「これはキャッシュしない」「ここではキャッシュしても OK」というケースに対応できる。

api.jquery.com

$.ajaxSetup メソッド を利用する

(function() {
    $.ajaxSetup({ cache: false }); 
})();

あらかじめ全ての Ajax 通信についてキャッシュをさせないようにする方法。先の方法と違って、1回実行してしまえばキャッシュを止めることが出来るので、特に理由がなければこちらを使うのが良さそう。 表面上では $.ajax メソッド が出てこない $.load メソッド、また MVC フレームワークの1つである Backbone.js の featch メソッド などに対してもこの方法で対処することができる。

すべての Ajax 通信より先に実行される必要があるが、もちろん jQuery の読み込みより先に実行してしまうと無意味なので注意。

api.jquery.com

ちなみにこの問題は IE7 ごろから存在する問題らしい。IE 無視してるからこうなるんだ、恥ずかしいやつめ。すみません。

問題解決のために Stackoverflow の皆様に助けていただきました。この場を借りて謝意を。 ja.stackoverflow.com