web-dev-qa-db-ja.com

Chrome=を使用して、AJAXを使用してログインするときにパスワードを保存するように要求する)

注:この質問は、元のバージョンから大幅に編集されています。この問題は大幅に簡素化されました。

同様の質問がこれまでに数回、さまざまな形式で行われています。たとえば、

パスワードを保存するプロンプトをブラウザに表示するにはどうすればよいですか?

ブラウザはユーザーにパスワードの保存を促すタイミングをどのように知るのですか?

ただし、この質問は、Chromeの機能の非常に具体的な側面に焦点を当てているため、その点でまったく異なります。

過去の回答から判断すると、Chromeパスワード保存のプロンプトを表示する最良の方法は、実際にログインしながらフォームをダミーのiframeに送信することですAJAXexample 。それは理にかなっているので、ここ数日はいくつかのサンプルコードをいじっていますが、これに関するChromeの動作は意味がありません私です。それで質問です。

何らかの理由で、Chromeは、ダミーiframeに送信するフォームが存在する場合、またはその直後にパスワードを保存することを要求しませんonDomReady

Jsfiddleは here にありますが、で説明されている動作を確認するにはdummy.htmlをローカルに作成する必要があるため、ほとんど役に立ちません。そのため、これを確認する最良の方法は、完全なhtmlを独自のindex.htmlにコピーしてから、dummy.htmlファイルも作成することです。

index.htmlの完全なコードは次のとおりです。 3つのアプローチは、(1)(2)(3)として強調表示されます。 (2)のみが、ユーザーにパスワードの保存を求めるプロンプトを表示し、(3)が機能しないという事実は、私にとって特に困惑しています。

<html>
<head>
<title>Chrome: Remember Password</title>
<script type="text/javascript" src="http://code.jquery.com/jquery-2.0.3.min.js"></script>

<script type="text/javascript">  

    $(function(){

        function create_login_form()
        {
            $('#login_form').html(
            "<input type='text' name='email'>" +
            "<input type='password' name='password'>" +
            "<input id='login-button' type='submit' value='Login'/>");
        }

        // (1) this does not work. chrome seems to require time after "onDomReady" to process
        // the forms that are present on the page. if the form fields exist while that processing
        // is going on, they will not generate the "Save Password?" Prompt.
        //create_login_form();

        // (2) This works. chrome has obviously finished its "work" on the form fields by now so
        // we can add our content to the form and, upon submit, it will Prompt "Save Password?"

        setTimeout(create_login_form,500);

    });

</script>
</head>
<body>

<!-- Our dummy iframe where the form submits to -->
<iframe src="dummy.html" name="dummy" style="display: none"></iframe>

<form action="" method="post" target="dummy" id="login_form">

    <!-- (3) fails. form content cannot be present on pageload -->
    <!--
    <input type='text' name='email'>
    <input type='password' name='password'>
    <input id='login-button' type='submit' value='Login'/>      
    -->

</form>

</body> 
</html>

ここで何が起こっているのかを誰かが説明できるとしたら、私は最も感謝しています。

EDIT:Chromeを使用したこの「パスワードの問題の保存」は、Googleが開発したAngularJSを使用している人々にも拡張されていることに注意してください: Github

33
EleventyOne

この問題を徹底的に調査した後、最終レポートを以下に示します。

まず、質問で強調された問題は、実際にはニシンです。フォームを誤ってiframeに送信していました(これは arty が強調表示されていますが、ドットを接続しませんでした)。この問題に対する私のアプローチは、 この例 に基づいていました。これは、他の関連する回答の一部でも参照されていました。正しいアプローチは here にあります。基本的に、actionformは、srciframeに設定する必要があります(@artyが提案したとおりです)。

そうすると、ページがまったくリロードされないため、質問で強調表示された特定の問題はなくなります(これは理にかなっています-iframeに送信しているときにページをリロードする必要があるのはなぜですか? 、それは私をひっくり返したはずだった)。とにかく、ページがリロードされないので、Chromeは、onDomReadyの後にフォームを表示するのにどれだけ時間がかかっても、パスワードの保存を要求しません。

したがって、iframeに(適切に)送信しても、Chromeがパスワードの保存を要求することはありません。 Chromeは、フォームを含むページがリロードされた場合にのみ実行されます(注:ここでの古い投稿とは異なり、Chromeは、フォームが動的に作成された場合、パスワードの保存を求めます) 。

そのため、唯一の解決策は、フォームが送信されたときにページの再読み込みを強制することです。しかし、どうやってそれを行い、AJAX/SPA構造をそのまま維持するのでしょうか?ここに私の解決策があります:

(1)SPAを2つの部分に分割します。(1)ログインしていないユーザー用、(2)ログインしているユーザー用。これは、より大きなサイトを使用している場合には実行できない場合があります。 そして、私はこれを永続的な解決策とは考えていません-Chrome、お願いします...このバグを修正してください。

(2)フォーム上の2つのイベント、onClickSaveButtononFormSubmitをキャプチャします。特にログインフォームの場合、onClickSaveButtonのユーザー詳細を取得し、AJAX呼び出しを行って情報を確認します。その情報が渡されたら、手動でformName.submit()を呼び出しますonFormSubmitでは、onClickSaveButtonが呼び出される前にフォームが送信されないようにします。

(3)フォームはindex.phpファイルに単純にリダイレクトするPHPファイルに送信します

このアプローチには2つの利点があります。

(1)Chromeで動作します。

(2)Firefoxでは、ユーザーはログインに成功した場合にのみパスワードを保存するように求められます(個人的には、間違っていた場合にpwdを保存するよう求められるのは面倒です)。

関連するコードは次のとおりです(簡略化)。

INDEX.PHP

<html>
<head>
<title>Chrome: Remember Password</title>

<!-- dependencies -->
<script type="text/javascript" src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
<script type="text/javascript" src="http://underscorejs.org/underscore.js"></script>
<script type="text/javascript" src="http://backbonejs.org/backbone.js"></script>

<!-- app code -->
<script type="text/javascript" src="mycode.js"></script>
<script>

    $(function(){

        createForm();

    });

</script>

</head>
<body>

    <div id='header'>
        <h1>Welcome to my page!</h1>
    </div>

    <div id='content'>
        <div id='form'>
        </div>
    </div>

</body> 
</html>

MYCODE.JS

function createForm() {

    VLoginForm = Backbone.View.extend({

        allowDefaultSubmit : true,

        // UI events from the HTML created by this view
        events : {
            "click button[name=login]" : "onClickLogin",
            "submit form" : "onFormSubmit"
        },

        initialize : function() {
            this.verified = false;
            // this would be in a template
            this.html = "<form method='post' action='dummy.php'><p><label for='email'>Email</label><input type='text' name='email'></p><p><label for='password'>Password<input type='password' name='password'></p><button name='login'>Login</button></form>";        
        },
        render : function() {
            this.$el.html(this.html);
            return this;
        },
        onClickLogin : function(event) {

            // verify the data with an AJAX call (not included)

            if ( verifiedWithAJAX ) {
                this.verified = true;
                this.$("form").submit();
            }           
            else {
                // not verified, output a message
            }

            // we may have been called manually, so double check
            // that we have an event to stop.
            if ( event ) {
                event.preventDefault();
                event.stopPropagation();
            }

        },
        onFormSubmit : function(event) {
            if ( !this.verified ) {             
                event.preventDefault();
                event.stopPropagation();
                this.onClickLogin();
            }
            else if ( this.allowDefaultSubmit ) {
                // submits the form as per default
            }
            else {
                // do your own thing...
                event.preventDefault();
                event.stopPropagation();
            }
        }
    });

    var form = new VLoginForm();
    $("#form").html(form.render().$el);

}

DUMMY.PHP

<?php
    header("Location: index.php");
?>

編集: mkurzによる答えは有望に見えます。おそらくバグは修正されています。

8
EleventyOne

Chrome 46で始まるiframeはもう不要です!

対応するすべてのChrome問題が修正されました: 12

フォームを削除(または非表示)するか、アクションURLを変更することで、Push状態またはajaxリクエストの後に元のログインフォームがもう「存在しない」ことを確認してください(テストしませんでしたが、動作するはずです)。また、同じページ内の他のすべてのフォームが別のアクションURLを指していることを確認してください。そうでない場合、それらもログインフォームと見なされます。

この例を確認してください。

<!doctype html>
<title>dynamic</title>
<button onclick="addGeneratedForms()">addGeneratedForms</button>
<script>
function addGeneratedForms(){
  var div = document.createElement('div');
  div.innerHTML = '<form class="login" method="post" action="login">\
    <input type="text" name="username">\
    <input type="password" name="password">\
    <button type="submit">login</button> stay on this page but update the url with pushState\
  </form>';
  document.body.appendChild(div);
}
document.body.addEventListener('submit', function ajax(e){
  e.preventDefault();
  setTimeout(function(){
      e.target.parentNode.removeChild(e.target); // Or alternatively just hide the form: e.target.style.display = 'none';

      history.replaceState({success:true}, 'title', "/success.html");

      // This is working too!!! (uncomment the history.xxxState(..) line above) (it works when the http response is a redirect or a 200 status)
      //var request = new XMLHttpRequest();
      //request.open('POST', '/success.html', true); // use a real url you have instead of '/success.html'
      //request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
      //request.send();
  }, 1);
}, false);
</script>

興味がある場合は、 this repo にさらに例があります。次のノードで実行できます:node server.js。このコミットからのコメントも参照してください: https://github.com/mkurz/ajax-login/commit/c0d9503c1d2a6a3a052f337b8cad2259033b1a58

ヘルプが必要な場合はお知らせください。

23
mkurz

Chrome(v39)を取得して、リロードせずにパスワード保存プロンプトを表示できます。srcがしない空白ページであるiframeにフォームを送信するだけですContent-Type: text/htmlヘッダーがあります(ヘッダーを削除するか、text/plainを使用すると両方とも機能するようです)。

これがバグなのか、意図した動作なのかわからない。前者の場合は、静かにしてください:-)

1
ejain

これで、experimentalAPIを使用できます:Credential Management API.

公式例

コードスニペットを使用した別の回答

0
shameleo

私はChrome 32.0を使用しています。両方の方法はここでうまく機能しますが、なぜあなたの目的が異なるのか理解できません...

とは言うものの、私は多くの場合、数百ミリ秒の間setTimeout()を使用しなければなりませんでした。 focus()は期待どおりに機能します。それもおそらくあなたのためのトリックを行います。あなたの場合:

setTimeout(display_login_form(),500)
0
aanders77