無料で使えるシステムトレードフレームワーク「Jiji」 をリリースしました!

・OANDA Trade APIを利用した、オープンソースのシステムトレードフレームワークです。
・自分だけの取引アルゴリズムで、誰でも、いますぐ、かんたんに、自動取引を開始できます。

[Javascript] AJAXアプリでオートコンプリート機能を有効化する際の注意事項

ユーザー名とパスワードをブラウザに覚えさせられるオートコンプリート機能ですが、入力されたデータをAJAXで送信して認証するタイプのアプリで使うにはいろいろと工夫が必要です。ということで、AJAXアプリでオートコンプリート機能を有効化する際の注意事項などを調べた範囲でまとめてみました。以下に挙げた手法を適用することで、IEFirefoxではなんとか動作するようになりましたが、Chrome/Safariはまだダメです。

注意: 動作確認は手元にあった以下のブラウザで行っています(いずれもWindow版。)。他のバージョンでどうなのかは未確認です。

Firefox 3.6.17,4.01
IE 8
Chrome 11
Safari 5.0.5

サマリ

      1. IEでの記憶にはwindow.external.AutoCompleteSaveForm(form) を使用する
      2. FormをonLoad後に動的生成しない。
      3. Formにはテキストフィールドとパスワード入力フィールドをひとつだけ用意する。

1. IEでの記憶にはwindow.external.AutoCompleteSaveForm(form) を使用する

オートコンプリートでの記憶のトリガはフォームのサブミット操作であるため、内部的にはXHRで認証する場合でもform要素を用意し、「action="javascript:void(0)"」等にした上でsubmitする必要があります。Firefoxに関してはこれで問題なく記憶されるのですが、IEでは不具合により保存処理が実行されません。(参考:http://support.microsoft.com/kb/329156/ja) また、Safari/Chromeもダメです。

<div>
  <h3>Formをスクリプトでsubmitする</h3>
  <p>
    「action="javascript:void(0);」とした上でFormをsubmitする。<br/>
    Firefoxでは動作するがIEでは保存が行われない。
  </p>
  <form id="test_form" action="javascript:void(0);" >
    <input type="text" name="username" id="username" />
    <input type="password" name="password" id="password" />
  </form>
  <a href="javascript:submit()">submit</a>
</div>

<script type="text/javascript">
  function submit() {
      var form = document.getElementById("test_form");
      form.submit();
      alert("submit!");
  }
</script>

確認はこちらからFirefoxでは動作しますが、IE/Chrome/Safariでは保存が行われません。

対策として、IEではwindow.external.AutoCompleteSaveForm(form)を明示的に呼び出し、記憶させる必要があります。

function submit() {
    var form = document.getElementById("test_form");
    if (navigator.userAgent.toLowerCase().indexOf("msie") != -1) {
      // ↑ブラウザ判定は、JQuery等のフレームワークで提供される機能を使ってくださいね。
      window.external.AutoCompleteSaveForm(form);
  } else {
      form.submit();
  }
    alert("submit!");
}

確認はこちらから。これでIEでも動作するようになったはず。

また、非表示のiframeを使う方法もあり、こちらであればFirefox/Safariで動作します。ただし、ネットワークにフォームデータが送付されてしまうことになるのでご注意。

<div>
  <h3>Formをスクリプトでsubmitする:iframeを使う版</h3>
  <p>
    Formのtargetを非表示のiframeにしてsubmitする。<br/>
    Safari/Firefoxで動作するがユーザー名・パスワードがサーバーに送出されてしまうので注意。
  </p>
  <form id="test_form" action="./dummy" target="dummy" >
    <input type="text" name="username" id="username" />
    <input type="password" name="password" id="password" />
  </form>
  <a href="javascript:submit()">submit</a>
  <iframe name="dummy" style="display:none;"></iframe>
</div>
<script type="text/javascript">
  function submit() {
    var form = document.getElementById("test_form");
    if (navigator.userAgent.toLowerCase().indexOf("msie") != -1) { 
      window.external.AutoCompleteSaveForm(form);
    } else {
      form.submit();
    }
    alert("submit!");
  }
</script>

確認はこちらから

2. FormをonLoad後に動的生成しない。

Firefoxだとドキュメントのロード時にオートコンプリートが適用されるらしく、スクリプト等を用いてロード後に動的に生成したformでは、ユーザー名・パスワードが保存されていても、パスワードの方が自動入力されてくれません。(参考:http://stackoverflow.com/questions/2267543/do-browsers-support-autocomplete-for-ajax-loaded-login-forms-at-all)スクリプトでの要素の動的生成はあきらめるか、onLoadの前に生成しておく必要があります。

以下はうまく動かない例。ユーザー名・パスワードを記憶させると、オートコンプリートのポップアップでユーザー名が選択できるようになりますが、スクリプトで動的に生成したフォームでは選択してもパスワードが補完されてくれません。HTMLに埋め込んだフォームや、onLoad前に生成したフォームでは問題なし。

<div>
  <h3>HTMLに埋め込んだForm要素/オートコンプリートが働く</h3>
  <form id="test_form1" action="javascript:void(0);" >
    <input type="text" name="username" id="form1_username" />
    <input type="password" name="password" id="form1_password" />
  </form>
  <a href="javascript:submit('test_form1')">submit.</a>
</div>
<div id="test2"></div>
<div id="test3"></div>
<script type="text/javascript">

  function submit( formId ) {
     var form = document.getElementById(formId);
     if (navigator.userAgent.toLowerCase().indexOf("msie") != -1) {
       window.external.AutoCompleteSaveForm(form);
     } else {
       form.submit();
     }
     alert("submit!");
  }
  
  function createForm( no, title ) {
    return "<div>" +
     "  <h3>" + title + "</h3>" +
     "  <form id='test_form" + no + "' action='javascript:void(0);' >" +
     "    <input type='text' name='username' id='form"+no+"_username' />" +
     "    <input type='password' name='password' id='form"+no+"_password' />" +
     "  </form>" +
     "  <a href=\"javascript:submit('test_form"+no+"')\">submit.</a>" +
     "</div>";
  }
  
  // onloadの後に生成 / パスワードが補完されない
  window.onload = function() {
    document.getElementById("test2").innerHTML = createForm( "2", 
        "onloadの後にスクリプトで生成したForm要素/パスワードが補完されない");
  };
  
  // onloadの前に生成 / こちらは期待通り動作する。
  document.getElementById("test3").innerHTML = createForm( "3", 
        "onloadの前にスクリプトで生成したForm要素/オートコンプリートか働く"); 
  
</script>

確認はこちらから

3.Formにはテキストフィールドとパスワード入力フィールドをひとつだけ用意する。

これはAJAXなWebアプリケーションに限らずですが、オートコンプリートでユーザー名・パスワードを記憶させたい場合には、formに含まれるコントロールをテキストフィールドとパスワード入力フィールドをそれぞれひとつだけにする必要があります。テキストフィールドが複数あったりすると、IEではユーザー名・パスワードの入力フォームとみなされず、ユーザー名しか記憶してくれません。(参考:http://support.microsoft.com/kb/917458/ja)

<div>
  <h3>Formにテキストフィールドが2つあると動作しない。</h3>
  <form id="test_form" action="javascript:void(0);" >
    <input type="text" name="username" id="username" />
    <input type="text" name="testfield" id="testfield" />
    <input type="password" name="password" id="password" />
  </form>
  <a href="javascript:submit()">submit</a>
</div>
<script type="text/javascript">
  function submit() {
    var form = document.getElementById("test_form");
    if (navigator.userAgent.toLowerCase().indexOf("msie") != -1) { 
      window.external.AutoCompleteSaveForm(form);
    } else {
      form.submit();
    }
    alert("submit!");
  }
</script>

確認はこちらからFirefoxでは記憶自体は行われますが、ユーザー名の値が"testfield"の方に入力した値になってたり・・。

おまけ: ユーザー名をスクリプトで挿入した時にパスワードを補完させたい場合の黒魔法

ユーザー名をドロップダウンで選べるようにしたい!などとなった場合、スクリプトでユーザー名を挿入すると同時にパスワードもオートコンプリートで記憶されていれば補完させたい感じになります。そんなときは、挿入後に以下の操作を行えばOK。

  1. フォーカスを「ユーザー名フィールド」→「パスワードフィールド」→「ユーザー名フィールド」 の順に移動。
  2. input要素に対してイベントを発火する。

サンプルは以下。普通に挿入しただけでは補完されませんが、

<div>
  <h3>ユーザー名をスクリプトで挿入</h3>
  <p>
    insertでプロンプトを表示し、入力されたユーザー名をフォームに挿入する。<br/>
    普通に挿入しただけだと、パスワードが補完されない。
  </p>
  <form id="test_form" action="javascript:void(0);" >
    <input type="text" name="username" id="username" />
    <input type="password" name="password" id="password" />
  </form>
  <a href="javascript:submit()">submit</a>|
  <a href="javascript:insert()">insert</a>
</div>
<script type="text/javascript">
  function submit() {
    var form = document.getElementById("test_form");
    if (navigator.userAgent.toLowerCase().indexOf("msie") != -1) { 
      window.external.AutoCompleteSaveForm(form);
    } else {
      form.submit();
    }
    alert("submit!");
  }
  function insert() {
    var user = prompt("ユーザー名を入力してください", "");
    if ( user ) {
      document.getElementById("username").value = user;
    }
  }
</script>

確認はこちら

insert関数を以下のように変更すると、ユーザー名挿入時にパスワードも補完されるようになります。

function insert() {
  var user = prompt("ユーザー名を入力してください", "");
  if ( user ) {
    var f = document.getElementById("username");
    f.value = user;
    
    // パスワードも補完させるための黒魔法。
    // フォーカスを移動して戻す。
    f.focus();
    document.getElementById("password").focus();
    f.focus();
    // IEではさらに以下も必要。
    if (navigator.userAgent.toLowerCase().indexOf("msie") != -1) { 
   f.fireEvent("onkeypress"); // キーイベントを発火
    }
  }
}

確認はこちら

手元にあったIE8,Firefox3.6,4では動作しましたが、若干黒いので、製品で使う場合動作確認は念入りに。