如何在离开和返回HTTPS页面时保留更改的表单内容? (适用于HTTP)

时间:2013-01-20 00:56:12

标签: forms browser https back

  1. 输入/更改textarea中的内容
  2. 在提交表单之前,请离开页面(例如,点击浏览器的后退按钮)
  3. 返回编辑页面(例如,点击前进按钮)
  4. 预期结果:在textarea中输入的内容应该仍然存在

    实际结果:

    • HTTPS :所有更改都消失了(糟糕!)
    • HTTP :更改仍然存在(好!)

    为什么在使用HTTPS时会发生这种情况?我怎么能阻止这个?浏览器或网站是否负责?

2 个答案:

答案 0 :(得分:61)

您可以考虑以下解决方案:

autocomplete 属性(HTML5)

这似乎无关紧要,因为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备注:

  • 如果缺少自动填充属性,该字段将默认为&#39;如果元素没有父表单,或者表单有 自动填充设置为&#39; on&#39;。
  • 自动完成功能提供的信息未公开 对象模型,直到用户才对网页可见 选择其中一个建议作为文本字段的值。

在本地保存未提交的表单数据:

您可以在页面重定向之前本地存储输入数据,也可以在每个表单控件的焦点输出事件中存储输入数据:

缓存

在这种情况下,好的旧饼干可以派上用场,但你应该考虑下方:

  1. 即使您可以通过编程方式加密值;因为我们将在客户端工作,所以cookie并不是真正安全的。 Http-OnlySecure标记的Cookie不会对我们有所帮助,因为这些选项用于在Cookie被发送时强制执行SSL&#34; (安全),无法从Javascript(仅限http)访问。
  2. 浏览器的Cookie大小限制。来自 MSDN &#34;大多数浏览器都支持 最多4096字节的cookie。由于这个小限制,cookie 最好用于存储少量数据&#34;。所以,如果你不是 注意这个大小(当你写cookie和/或限制时) 控件的值通过maxlength属性);这可能是一个 问题。 (在这种情况下修剪值是最糟糕的事情)。
  3. 浏览器对可设置的Cookie数量也有限制 每个域名。所以;将表单数据存储在cookie中时;而不是为每个表单字段值设置cookie;你应该把它们合并成一个或几个cookie;为您的网站不要 超过此限制。
  4. 然而,好的一面是它们得到所有浏览器的支持,如果你不打算&#34;缓存&#34;通过Cookies敏感和过长的数据,然后您可以使用以下解决方案。如果不是这样的话;你应该更好地采用下一个建议: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();
    });
    

    这是 working demo on jsFiddle

    注意: &#34; cookies读者/作者&#34;来自developer.mozilla.org的脚本应包含在上面的代码中。你也可以使用雅虎的YUI 2: Cookie Utility,它有一个有用的setSub()方法,用于在单个cookie中设置子cookie,用于我之前提到的浏览器限制。

    localStorage的

    您还可以使用更为现代的技术,例如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
    }
    

    所以,就像在cookies示例中一样;

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

    的sessionStorage

    这种方式几乎一样。从W3C:sessionStorage对象等于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...";
    });
    

    在页面加载时从服务器检索以前保存的数据:

    提醒你;如果用户正在填写&#34;更新&#34;形式,例如;您始终可以从服务器获取以前保存的数据并自动填写表单(非敏感字段)。

    结论

    如果你真的需要这个并且值得一试;您应该考虑实现回退机制的跨浏览器解决方案;如:

    • 如果您支持HTML5功能;使用 HTML5 autocomplete 属性即可。 (您可以预先在HTML中嵌入该属性,或者 在测试浏览器支持时,通过Javascript / jQuery设置它。)
    • ELSE如果您支持Storage对象;跟着去吧 的 localStorage
    • ELSE IF [cookies当前会话商店] + [饼干大小你的 形式数据需求]&lt; 4096字节;然后使用 cookies
    • 如果你有一个服务器端的web-app发出静默的AJAX请求 在服务器上存储数据
    • ELSE不要这样做。

    注意: 对于HTML5功能检测,请查看this pagethis page,或者您可以使用{{3} }。

    HTTPS问题

    原因是,当使用 HTTPS 时,所有表单更改都消失了;它是 Modernizr 。表单主要用于用户输入,并且(可能)包含敏感数据。所以这种行为似乎是自然而且可取的我上面提供的解决方案与HTTP上的解决方案相同。所以这应该涵盖你所有的担忧。

    进一步阅读:

答案 1 :(得分: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 属性中添加来自流的数据可确保始终显示正确的值。即使是浏览器的 popstate 事件(后退和前进按钮点击)