49

我想通过javascript跟踪表单中输入的变化。我的意图是(但不限于)

  • 仅在某些内容发生更改时启用“保存”按钮
  • 如果用户想要关闭页面并且某些内容未保存,则发出警报

想法?

4

15 回答 15

31

循环遍历所有输入元素,并onchange在每个元素上放置一个处理程序。当它触发时,设置一个标志,让您知道表单已更改。它的基本版本很容易设置,但如果有人将输入从“a”更改为“b”然后又变回“a”,它就不够聪明。如果抓住那个案例很重要,那么它仍然是可能的,但需要更多的工作。

这是 jQuery 中的一个基本示例:

$("#myForm")
    .on("input", function() {
        // do whatever you need to do when something's changed.
        // perhaps set up an onExit function on the window
        $('#saveButton').show();
    })
;
于 2008-10-11T14:17:47.177 回答
18

JS 中的文本表单元素暴露了一个.value属性和一个.defaultValue属性,因此您可以轻松地实现如下内容:

function formChanged(form) {
  for (var i = 0; i < form.elements.length; i++) {
      if(form.elements[i].value != form.elements[i].defaultValue) return(true);
  }
  return(false);
}

对于复选框和单选按钮,请查看是否element.checked != element.defaultChecked,对于 HTML<select />元素,您需要遍历select.options数组并检查每个选项是否selected == defaultSelected.

您可能想查看使用jQuery之类的框架将处理程序附加到onchange每个单独的表单元素的事件。这些处理程序可以调用您的formChanged()代码并修改enabled“保存”按钮的属性,和/或附加/分离文档正文事件的事件处理程序beforeunload

于 2008-10-11T14:18:00.590 回答
7

这是一个用于检测表单更改的简单的 javascript 和 jquery 方法。它会禁用提交按钮,直到进行更改。它检测通过提交表单以外的方式离开页面的尝试。它解释了用户的“撤消”,它被封装在一个函数中以便于应用,并且它不会在提交时出错。只需调用该函数并传递表单的 ID。

此函数在页面加载时序列化表单一次,并在用户离开页面之前再次序列化。如果两个表单状态不同,则会显示提示。

试试看:http: //jsfiddle.net/skibulk/ev5rE/

function formUnloadPrompt(formSelector) {
    var formA = $(formSelector).serialize(), formB, formSubmit = false;

    // Detect Form Submit
    $(formSelector).submit( function(){
        formSubmit = true;
    });

    // Handle Form Unload    
    window.onbeforeunload = function(){
        if (formSubmit) return;
        formB = $(formSelector).serialize();
        if (formA != formB) return "Your changes have not been saved.";
    };

    // Enable & Disable Submit Button
    var formToggleSubmit = function(){
        formB = $(formSelector).serialize();
        $(formSelector+' [type="submit"]').attr( "disabled", formA == formB);
    };

    formToggleSubmit();
    $(formSelector).change(formToggleSubmit);
    $(formSelector).keyup(formToggleSubmit);
}



// Call function on DOM Ready:

$(function(){
    formUnloadPrompt('form');
});
于 2013-10-22T18:59:55.763 回答
6

尝试

function isModifiedForm(form){
  var __clone = $(form).clone();
  __clone[0].reset();
  return $(form).serialize() == $(__clone).serialize();
}

希望它有帮助))

于 2013-07-09T12:35:16.253 回答
2

如果您使用 Web 应用程序框架(rails、ASP.NET、Cake、symfony),则应该有用于 ajax 验证的包,

http://webtecer.com/2008/03/17/list-of-ajax-form-validators/

以及 onbeforeunload() 上的一些包装器以警告用户即将关闭表单:

http://pragmatig.wordpress.com/2008/03/03/protecting-userdata-from-beeing-lost-with-jquery/ 检测未保存的更改

于 2008-10-11T14:19:47.873 回答
2

我在 Ars Technica 上回答了这样的问题,但问题是这样设计的,即使用户没有模糊文本字段(在这种情况下,更改事件永远不会触发),也需要检测到更改。我想出了一个全面的脚本:

  1. 如果字段值更改,则启用提交和重置按钮
  2. 如果表单被重置,则禁用提交和重置按钮
  3. 如果表单数据已更改且未提交,则中断离开页面
  4. 支持 IE 6+、Firefox 2+、Safari 3+(大概是 Opera 但我没有测试)

这个脚本依赖于原型,但可以很容易地适应另一个库或独立。

$(document).observe('dom:loaded', function(e) {
    var browser = {
        trident: !!document.all && !window.opera,
        webkit: (!(!!document.all && !window.opera) && !document.doctype) ||
            (!!window.devicePixelRatio && !!window.getMatchedCSSRules)
    };

    // Select form elements that won't bubble up delegated events (eg. onchange)
    var inputs = $('form_id').select('select, input[type="radio"], input[type="checkbox"]');

    $('form_id').observe('submit', function(e) {
        // Don't bother submitting if form not modified
        if(!$('form_id').hasClassName('modified')) {
            e.stop();
            return false;
        }
        $('form_id').addClassName('saving');
    });

    var change = function(e) {
        // Paste event fires before content has been pasted
        if(e && e.type && e.type == 'paste') {
            arguments.callee.defer();
            return false;
        }

        // Check if event actually results in changed data
        if(!e || e.type != 'change') {
            var modified = false;
            $('form_id').getElements().each(function(element) {
                if(element.tagName.match(/^textarea$/i)) {
                    if($F(element) != element.defaultValue) {
                        modified = true;
                    }
                    return;
                } else if(element.tagName.match(/^input$/i)) {
                    if(element.type.match(/^(text|hidden)$/i) && $F(element) != element.defaultValue) {
                        modified = true;
                    } else if(element.type.match(/^(checkbox|radio)$/i) && element.checked != element.defaultChecked) {
                        modified = true;
                    }
                }
            });
            if(!modified) {
                return false;
            }
        }

        // Mark form as modified
        $('form_id').addClassName('modified');

        // Enable submit/reset buttons
        $('reset_button_id').removeAttribute('disabled');
        $('submit_button_id').removeAttribute('disabled');

        // Remove event handlers as they're no longer needed
        if(browser.trident) {
            $('form_id').stopObserving('keyup', change);
            $('form_id').stopObserving('paste', change);
        } else {
            $('form_id').stopObserving('input', change);
        }
        if(browser.webkit) {
            $$('#form_id textarea').invoke('stopObserving', 'keyup', change);
            $$('#form_id textarea').invoke('stopObserving', 'paste', change);
        }
        inputs.invoke('stopObserving', 'change', arguments.callee);
    };

    $('form_id').observe('reset', function(e) {
        // Unset form modified, restart modified check...
        $('reset_button_id').writeAttribute('disabled', true);
        $('submit_button_id').writeAttribute('disabled', true);
        $('form_id').removeClassName('modified');
        startObservers();
    });

    var startObservers = (function(e) {
        if(browser.trident) {
            $('form_id').observe('keyup', change);
            $('form_id').observe('paste', change);
        } else {
            $('form_id').observe('input', change);
        }
        // Webkit apparently doesn't fire oninput in textareas
        if(browser.webkit) {
            $$('#form_id textarea').invoke('observe', 'keyup', change);
            $$('#form_id textarea').invoke('observe', 'paste', change);
        }
        inputs.invoke('observe', 'change', change);
        return arguments.callee;
    })();

    window.onbeforeunload = function(e) {
        if($('form_id').hasClassName('modified') && !$('form_id').hasClassName('saving')) {
            return 'You have unsaved content, would you really like to leave the page? All your changes will be lost.';
        }
    };

});
于 2008-10-11T17:27:37.730 回答
1

要在关闭前提醒用户,请使用 unbeforeunload:

window.onbeforeunload = function() {
   return "You are about to lose your form data.";
};
于 2008-10-11T14:22:47.727 回答
1

当页面加载时,我会将每个字段值存储在一个变量中,然后在用户卸载页面时比较这些值。如果检测到任何差异,您将知道要保存什么,并且更好的是,能够明确告诉用户如果他们退出哪些数据将不会被保存。

// this example uses the prototype library
// also, it's not very efficient, I just threw it together
var valuesAtLoad = [];
var valuesOnCheck = [];
var isDirty = false;
var names = [];
Event.observe(window, 'load', function() {
    $$('.field').each(function(i) {
        valuesAtLoad.push($F(i));
    });
});

var checkValues = function() {
    var changes = [];
    valuesOnCheck = [];
    $$('.field').each(function(i) {
        valuesOnCheck.push($F(i));
    });

    for(var i = 0; i <= valuesOnCheck.length - 1; i++ ) {
        var source = valuesOnCheck[i];
        var compare = valuesAtLoad[i];
        if( source !== compare ) {
            changes.push($$('.field')[i]);
        }
    }

    return changes.length > 0 ? changes : [];
};

setInterval(function() { names = checkValues().pluck('id'); isDirty = names.length > 0; }, 100);

// notify the user when they exit
Event.observe(window, 'beforeunload', function(e) {
     e.returnValue = isDirty ? "you have changed the following fields: \r\n" + names + "\r\n these changes will be lost if you exit. Are you sure you want to continue?" : true;
});
于 2008-10-11T18:02:12.543 回答
1

我用过dirtyforms.js。对我来说效果很好。

http://mal.co.nz/code/jquery-dirty-forms/

于 2013-02-25T19:58:55.567 回答
0

将事件处理程序附加到每个表单输入/选择/文本区域的 onchange 事件。设置一个变量来告诉您是否应该启用“保存”按钮。创建一个 onunload 处理程序来检查脏表单,并在提交表单时重置变量:

window.onunload = checkUnsavedPage;
var isDirty = false;
var formElements = //获取所有表单元素的引用
for(var i = 0; len = formElements.length; i++) {
    //给每个元素添加onchange事件调用formChanged()
}

函数formChanged(事件){
    isDirty = 假;
    document.getElementById("savebtn").disabled = "";
}

函数 checkUnsavedPage() {
    如果(脏){
        var isSure = confirm("你确定?");  
        如果(!isSure){
            event.preventDefault();
        }
    }
}
于 2008-10-11T14:21:53.983 回答
0

这是Dylan Beattie 建议的完整实现:

用于“未保存数据”保护的客户端/JS 框架?

您不需要存储初始值来确定表单是否已更改,除非您在客户端动态填充它(尽管即使那样,您仍然可以default在表单元素上设置属性)。

于 2008-10-11T18:28:44.720 回答
0

您还可以查看我在jQuery track changes in forms plugin 中构建的这个 jQuery 插件

在此处查看演示并此处下载 JS

于 2008-12-31T06:04:54.430 回答
0

如果您愿意使用 jQuery,请参阅我对类似问题的回答: Disable submit button unless original form data has changed

于 2013-05-16T18:34:35.463 回答
0

我遇到了同样的挑战,我正在考虑一个通用的解决方案。下面的代码并不完美,它来自最初的研发。以下是我使用的步骤:

1)将以下JS移动到另一个文件(比如changeFramework.js)

2)通过导入将其包含在您的项目中

3)在你的html页面中,无论哪个控件需要监控,添加类“monitorChange”

4) 全局变量“hasChanged”会告诉您,您正在处理的页面是否有任何变化。

<script type="text/javascript" id="MonitorChangeFramework">
// MONITOR CHANGE FRAMEWORK
// ALL ELEMENTS WITH CLASS ".monitorChange" WILL BE REGISTERED FOR CHANGE
// ON CHANGE IT WILL RAISE A FLAG
var hasChanged;

function MonitorChange() {
    hasChanged = false;
    $(".monitorChange").change(function () {
        hasChanged = true;
    });
}

以下是我使用此框架的控件:

<textarea class="monitorChange" rows="5" cols="10" id="testArea"></textarea></br>
        <div id="divDrinks">
            <input type="checkbox" class="chb monitorChange" value="Tea" />Tea </br>
            <input type="checkbox" class="chb monitorChange" value="Milk" checked='checked' />Milk</br>
            <input type="checkbox" class="chb monitorChange" value="Coffee" />Coffee </br>
        </div>
        <select id="comboCar" class="monitorChange">
            <option value="volvo">Volvo</option>
            <option value="saab">Saab</option>
            <option value="mercedes">Mercedes</option>
            <option value="audi">Audi</option>
        </select>
        <button id="testButton">
            test</button><a onclick="NavigateTo()">next >>> </a>

我相信这个框架可以有很大的改进。欢迎评论/更改/反馈。:)

于 2013-11-22T04:39:19.237 回答
0

我做了一些跨浏览器测试。

在 Chrome 和 Safari 上,这很好:

<form onchange="validate()">
...
</form>

对于 Firefox + Chrome/Safari,我会这样做:

<form onkeydown="validate()">
    ...
    <input type="checkbox" onchange="validate()">
</form>

复选框或单选按钮等项目需要自己的 onchange 事件侦听器。

于 2015-04-29T14:06:17.973 回答