23
  1. テキストエリアに何かを入力/変更する
  2. フォームを送信する前に、ページを離れます(たとえば、ブラウザの戻るボタンをクリックして)
  3. 編集ページに戻ります(たとえば、進むボタンをクリックして)

期待される結果:テキストエリアに入力されたコンテンツはまだそこにあるはずです

実結果:

  • HTTPSの場合:すべての変更がなくなりました(悪いです!)
  • HTTPの場合:変更はまだあります(良いです!)

HTTPSを使用しているときにこれが発生するのはなぜですか?どうすればこれを防ぐことができますか?ブラウザまたはWebサイトに責任がありますか?

4

2 に答える 2

90

次の解決策を検討できます。

属性(autocompleteHTML5)

フォームで「送信」された以前のユーザー入力に基づいautocompleteた値でフィールドに入力するようにブラウザに指示するため、これは無関係のようです。しかし、私のテストではそれを見ました。送信せずにフォームに記入した後。進む(履歴)ボタンを押して、もう一度押すと、設定するとフォームフィールドが自動入力され、に設定するとすべてクリアされました。autocomplete="on""off"

それで; (HTML5ユーザーを対象とする場合)この属性を使用して、フォームデータを「キャッシュ」できます。(Operaを除くすべての主要なブラウザで動作します)。

<form action="/update" method="post" autocomplete="on">
    Email:    <input type="text" id="email" /><br />
    Username: <input type="text" id="uname" /><br />
    Password: <input type="password" id="pwd" autocomplete="off"/><br />
    <input type="submit" />
</form> 

残りのフォームコントロールがオンの場合、特定のフィールド(この場合はパスワード)のオートコンプリート機能をオフに設定できることに注意してください。

MSDN備考:

  • オートコンプリート属性がない場合、要素に親フォームがない場合、またはフォームのオートコンプリートが「オン」に設定されている場合、フィールドはデフォルトで「オン」状態になります。
  • オートコンプリート機能によって提供される情報は、オブジェクトモデルに公開されず、ユーザーがテキストフィールドの値として提案の1つを選択するまで、Webページに表示されません。

未送信のフォームデータをローカルに保存します。

入力データは、ページリダイレクトの直前、またはすべてのフォームコントロールのフォーカスアウトイベントでローカルに保存できます。

クッキー

この場合、古き良きCookieが便利ですが、欠点を考慮する必要があります。

  1. プログラムで値を暗号化できますが、クライアント側で作業するため、Cookieはこれに対して本当に安全ではありません。Http-OnlyマークさSecureれたCookieは、Cookieが「送信」(安全)でJavascript(httpのみ)からアクセスできない場合にSSLを適用するために使用されるため、ここでは役に立ちません。
  2. ブラウザにはCookieのサイズ制限があります。MSDNから:「ほとんどのブラウザは最大4096バイトのCookieをサポートしています。この小さな制限のため、Cookieは少量のデータを保存するために最適に使用されます」。したがって、このサイズを監視しない場合(Cookieを書き込むとき、および/またはmaxlength属性を介してコントロールの値を制限することによって)。それは問題になる可能性があります。(この場合、値のトリミングは最悪です)。
  3. ブラウザには、ドメインごとに設定できるCookieの数にも制限があります。それで; フォームデータをCookieに保存する場合。フォームフィールドの値ごとにCookieを設定する代わりに; それらを1つまたはいくつかのCookieにマージする必要があります。サイトがこの制限を超えないようにします。

それでも、明るい面は、すべてのブラウザでサポートされていることです。Cookieを介して機密性の高い長すぎるデータを「キャッシュ」する予定がない場合は、次のソリューションを使用できます。そうでない場合; 次の提案を行う方がよいでしょう:localStorage

// Below is just a demonstration and is not tested thoroughly for 
// production-ready web applications by any means.  
// But it should give you an idea.

/** 
 * Caches the user-input data from the targeted form, stores it in the cookies 
 * and fetches back to the form when requested or needed. 
 */
var formCache = (function () {
    var _form = null, 
        _formData = [],
        _strFormElements = "input[type='text'],"
                + "input[type='checkbox']," 
                + "input[type='radio']," 
                // + "input[type='password'],"  // leave password field out 
                + "input[type='hidden'],"
                // + "input[type='image'],"
                + "input[type='file'],"
                // more input types...
                + "input[type='email'],"
                + "input[type='tel'],"
                + "input[type='url'],"
                + "select,"
                + "textarea";

    function _warn() {
        console.log('formCache is not initialized.');
    }

    return {

        /**
         * Initializes the formCache with a target form (id). 
         * You can pass any container id for the formId parameter, formCache will 
         * still look for form elements inside the given container. If no form id 
         * is passed, it will target the first <form> element in the DOM. 
         */
        init: function (formId) {
            var f = (typeof formId === 'undefined' || formId === null || $.trim(formId) === '') 
                    ? $('form').first() 
                    : $('#' + formId);
            _form = f.length > 0 ? f : null;
            console.log(_form);
            return formCache; // make it chainable
        },

        /** 
         * Stores the form data in the cookies.
         */
        save: function () {
            if (_form === null) return _warn();

            _form
                .find(_strFormElements)
                .each(function() {
                    var f = $(this).attr('id') + ':' + formCache.getFieldValue($(this));
                    _formData.push(f);
                });
            docCookies.setItem('formData', _formData.join(), 31536e3); // 1 year expiration (persistent)
            console.log('Cached form data:', _formData);
            return formCache;
        },

        /** 
         * Fills out the form elements from the data previously stored in the cookies.
         */
        fetch: function () {
            if (_form === null) return _warn();

            if (!docCookies.hasItem('formData')) return;
            var fd = _formData.length < 1 ? docCookies.getItem('formData').split(',') : _formData;
            $.each(fd, function (i, item) {
                var s = item.split(':');
                var elem = $('#' + s[0]);
                formCache.setFieldValue(elem, s[1]);
            });
            return formCache;
        },

        /** 
         * Sets the value of the specified form field from previously stored data.
         */
        setFieldValue: function (elem, value) {
            if (_form === null) return _warn();

            if (elem.is('input:text') || elem.is('input:hidden') || elem.is('input:image') ||
                    elem.is('input:file') || elem.is('textarea')) {
                elem.val(value);
            } else if (elem.is('input:checkbox') || elem.is('input:radio')) {
                elem.prop('checked', value);
            } else if (elem.is('select')) {
                elem.prop('selectedIndex', value);
            }
            return formCache;
        },

        /**
         * Gets the previously stored value of the specified form field.
         */
        getFieldValue: function (elem) {
            if (_form === null) return _warn();

            if (elem.is('input:text') || elem.is('input:hidden') || elem.is('input:image') ||
                elem.is('input:file') || elem.is('textarea')) {
                    return elem.val();
                } else if (elem.is('input:checkbox') || elem.is('input:radio')) {
                    return elem.prop('checked');
                } else if (elem.is('select')) {
                    return elem.prop('selectedIndex');
                }
            else return null;
        },

        /**
         * Clears the cache and removes the previously stored form data from cookies.
         */
        clear: function () {
            _formData = [];
            docCookies.removeItem('formData');
            return formCache;
        },

        /**
         * Clears all the form fields. 
         * This is different from form.reset() which only re-sets the fields 
         * to their initial values.
         */
        clearForm: function () {
            _form
                .find(_strFormElements)
                .each(function() {
                    var elem = $(this);
                    if (elem.is('input:text') || elem.is('input:password') || elem.is('input:hidden') || 
                        elem.is('input:image') || elem.is('input:file') || elem.is('textarea')) {
                        elem.val('');
                    } else if (elem.is('input:checkbox') || elem.is('input:radio')) {
                        elem.prop('checked', false);
                    } else if (elem.is('select')) {
                        elem.prop('selectedIndex', -1);
                    }
                });
            return formCache;
        }
    };
})();

// Save form data right before we unload the form-page
$(window).on('beforeunload', function (event) {
    formCache.save();
    return false;
});

// Initialize and fetch form data (if exists) when we load the form-page back
$(document).on('ready', function (event) {
    formCache.init().fetch();
});

これがjsFiddleの動作デモです。

注:developer.mozilla.orgの「cookiesreader / writer」スクリプトは、上記のコードに含まれている必要があります。YahooのYUI2 :Cookieユーティリティを使用することもできます。これは、前述のブラウザの制限に対して、単一のCookie内にサブCookieを設定するための便利なsetSub()メソッドを備えています。

ローカルストレージ

localStorage(HTML5)のような最新の手法を使用することもできます。より安全で高速です。IE 8以降を含むすべての主要なブラウザーは、この機能をサポートしています。(さらに、iOSとAndroidのサポート!)

if (typeof Storage !== 'undefined') { // We have local storage support
    localStorage.username = 'Onur'; // to save to local storage
    document.getElementById('uname').value = localStorage.username; // to fetch from local storage
}

したがって、Cookieの例のように。

$(window).on('beforeunload', function (event) {
    saveFormToLocalStorage();
    return false;
});

$(document).on('ready', function (event) {
    fillFormFromLocalStorage()
});

SessionStorage

これはほとんど同じように機能します。W3Cから:sessionStorageオブジェクトは、1つのセッションのデータのみを格納することを除いて、localStorageオブジェクトと同じです。

サイレントAJAX投稿を介してフォームデータをサーバー/DBに保存します。

あまり効率的な方法ではありませんが、他の人が実行できない場合にこれを使用することをお勧めします。イベントに投稿してbeforeunload、ユーザーにメッセージを表示することができます。

$(window).on('beforeunload', function (event) {
    //check if at least one field is filled out.
    //make the AJAX post if filled out.
    return "You are leaving the page without submitting the form...";
});

ページの読み込み時にサーバーから以前に保存したデータを取得する:

君に言っておく; たとえば、ユーザーが「更新」フォームに入力している場合。以前に保存したデータをサーバーからいつでも取得して、フォームに自動的に入力できます(機密性の低いフィールド)。

結論

あなたが本当にこれを必要とし、トラブルの価値があるなら; フォールバックメカニズムを実装するクロスブラウザソリューションを検討する必要があります。そのような:

  • HTML5機能をサポートしている場合。HTML5autocomplete 属性を使用します。(事前にHTMLに属性を埋め込むか、ブラウザーのサポートをテストするときにJavascript / jQueryを介して属性を設定できます。)
  • Storageそれ以外の場合、オブジェクトをサポートしている場合。と一緒に行く localStorage;
  • ELSEIF[現在のセッションが保存するCookie]+[フォームデータに必要なCookieサイズ]<4096バイト; 次に、を使用しますcookies
  • それ以外の場合、サーバー側のWebアプリがある場合は、サーバーにデータを保存するためのサイレントAJAXリクエストを 作成します。
  • それ以外の場合は行いません。

注: HTML5機能の検出については、このページまたはこのページを参照するか、 Modernizrを使用できます。

HTTPSの問題

その理由は、 HTTPSを使用すると、すべてのフォームの変更がなくなるためです。これは安全なプロトコルです。フォームは主にユーザー入力に使用され、(おそらく)機密データを含めることができます。したがって、この動作は自然で予想されるもののようです。上記で提供したソリューションは、HTTPの場合と同じように機能します。だから、それはあなたのすべての懸念をカバーするはずです。

参考文献:

于 2013-01-20T04:40:36.300 に答える
0

これは私のために働いたものです。

<select
  class="form-select custom-select page-number-select"
  (change)="onPageChange($event)"
  data-test="XXXX"
  [attr.aria-labelledby]="XXXX"
  [value]="pageNumber" <---- This fixed the problem
>
  <ng-container
    *ngFor="let pageNumber of totalPageCount"
  >
    <option value="{{ pageNumber }}" [attr.selected]="pageNumber == page ? '' : null" >
      {{ t('pageN', { pageNumber: pageNumber }) }}
    </option>
   </ng-container>
</select>

ストリームからのデータをvalue属性に追加すると、常に正しい値が表示されるようになります。ブラウザのポップステートイベント(戻るボタンと進むボタンのクリック)でも

于 2021-04-23T11:32:02.467 に答える