Generic way to detect if html form is edited

我有一个选项卡式的 html 表单。在从一个选项卡导航到另一个选项卡时,即使数据没有更改,当前选项卡的数据也会保持(在数据库上)。

我希望只有在表单被编辑时才进行持久性调用。窗体可以包含任何类型的控件。弄脏表单不需要键入某些文本,但在日历控件中选择日期也符合条件。

实现这一点的一种方法是在默认情况下以只读模式显示表单,并有一个“编辑”按钮,如果用户点击编辑按钮,然后调用数据库(再一次,不管是否修改数据。这是对现有技术的一个更好的改进)。

我想知道如何编写一个通用的 javascript 函数,将检查是否有任何控件值已被修改?

113030 次浏览

在纯 javascript 中,这不是一个简单的任务,但是 jQuery 让它变得非常容易:

$("#myform :input").change(function() {
$("#myform").data("changed",true);
});

Then before saving, you can check if it was changed:

if ($("#myform").data("changed")) {
// submit the form
}

在上面的例子中,表单的 id 等于“ myform”。

如果你需要多种形式的插件,你可以很容易地把它变成一个插件:

$.fn.extend({
trackChanges: function() {
$(":input",this).change(function() {
$(this.form).data("changed", true);
});
}
,
isChanged: function() {
return this.data("changed");
}
});

然后你可以简单地说:

$("#myform").trackChanges();

并检查表格是否已更改:

if ($("#myform").isChanged()) {
// ...
}

In case JQuery is out of the question. A quick search on Google found Javascript implementations of MD5 and SHA1 hash algorithms. If you wanted, you could concatenate all form inputs and hash them, then store that value in memory. When the user is done. Concatenate all the values and hash again. Compare the 2 hashes. If they are the same, the user did not change any form fields. If they are different, something has been edited, and you need to call your persistence code.

我是这样做的(不使用 jQuery)。

在我的例子中,我希望不计算一个特定的表单元素,因为它是触发检查的元素,因此总是会发生变化。异常元素名为“ report _ period”,并在函数“ hasFormChanged ()”中进行硬编码。

To test, make an element call the function "changeReportingPeriod()", which you'll probably want to name something else.

重要提示: 当值被设置为其原始值时,必须调用 setInitialValue ()(通常在页面加载时调用,但在本例中不调用)。

注意: 我并不认为这是一个优雅的解决方案,事实上我并不相信优雅的 JavaScript 解决方案。我个人对 JavaScript 的强调是可读性,而不是结构优雅(就好像在 JavaScript 中可以做到这一点一样)。在编写 JavaScript 时,我根本不关心文件大小,因为这就是 gzip 的用途,并且尝试编写更紧凑的 JavaScript 代码总是会导致维护方面无法忍受的问题。我没有道歉,没有悔恨,也拒绝辩论。是 JavaScript。对不起,我必须把话说清楚才能让自己相信我应该发帖。开心点!:)


var initial_values = new Array();


// Gets all form elements from the entire document.
function getAllFormElements() {
// Return variable.
var all_form_elements = Array();


// The form.
var form_activity_report = document.getElementById('form_activity_report');


// Different types of form elements.
var inputs = form_activity_report.getElementsByTagName('input');
var textareas = form_activity_report.getElementsByTagName('textarea');
var selects = form_activity_report.getElementsByTagName('select');


// We do it this way because we want to return an Array, not a NodeList.
var i;
for (i = 0; i < inputs.length; i++) {
all_form_elements.push(inputs[i]);
}
for (i = 0; i < textareas.length; i++) {
all_form_elements.push(textareas[i]);
}
for (i = 0; i < selects.length; i++) {
all_form_elements.push(selects[i]);
}


return all_form_elements;
}


// Sets the initial values of every form element.
function setInitialFormValues() {
var inputs = getAllFormElements();
for (var i = 0; i < inputs.length; i++) {
initial_values.push(inputs[i].value);
}
}


function hasFormChanged() {
var has_changed = false;
var elements = getAllFormElements();


for (var i = 0; i < elements.length; i++) {
if (elements[i].id != 'reporting_period' && elements[i].value != initial_values[i]) {
has_changed = true;
break;
}
}


return has_changed;
}


function changeReportingPeriod() {
alert(hasFormChanged());
}




我不确定我是否正确回答了你的问题,但是 addEventListener 怎么样呢?如果你不太关心 IE8的支持,这应该是好的。以下代码正在为我工作:

var form = document.getElementById("myForm");


form.addEventListener("input", function () {
console.log("Form has changed!");
});

实现这一点的另一种方法是序列化表单:

$(function() {
var $form = $('form');
var initialState = $form.serialize();
    

$form.submit(function (e) {
if (initialState === $form.serialize()) {
console.log('Form is unchanged!');
} else {
console.log('Form has changed!');
}
e.preventDefault();
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form>
Field 1: <input type="text" name="field_1" value="My value 1"> <br>
Field 2: <input type="text" name="field_2" value="My value 2"> <br>
Check: <input type="checkbox" name="field_3" value="1"><br>
<input type="submit">
</form>

下面是本地 JavaScript 中的一个 polyfill 方法演示,它使用 FormData()API 来检测创建、更新和删除的表单条目。您可以使用 HTMLFormElement#isChanged检查是否有任何更改,并使用 HTMLFormElement#changes获得一个包含与重置表单不同之处的对象(假设它们没有被输入名称掩盖) :

Object.defineProperties(HTMLFormElement.prototype, {
isChanged: {
configurable: true,
get: function isChanged () {
'use strict'


var thisData = new FormData(this)
var that = this.cloneNode(true)


// avoid masking: https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/reset
HTMLFormElement.prototype.reset.call(that)


var thatData = new FormData(that)


const theseKeys = Array.from(thisData.keys())
const thoseKeys = Array.from(thatData.keys())


if (theseKeys.length !== thoseKeys.length) {
return true
}


const allKeys = new Set(theseKeys.concat(thoseKeys))


function unequal (value, index) {
return value !== this[index]
}


for (const key of theseKeys) {
const theseValues = thisData.getAll(key)
const thoseValues = thatData.getAll(key)


if (theseValues.length !== thoseValues.length) {
return true
}


if (theseValues.some(unequal, thoseValues)) {
return true
}
}


return false
}
},
changes: {
configurable: true,
get: function changes () {
'use strict'


var thisData = new FormData(this)
var that = this.cloneNode(true)


// avoid masking: https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/reset
HTMLFormElement.prototype.reset.call(that)


var thatData = new FormData(that)


const theseKeys = Array.from(thisData.keys())
const thoseKeys = Array.from(thatData.keys())


const created = new FormData()
const deleted = new FormData()
const updated = new FormData()


const allKeys = new Set(theseKeys.concat(thoseKeys))


function unequal (value, index) {
return value !== this[index]
}


for (const key of allKeys) {
const theseValues = thisData.getAll(key)
const thoseValues = thatData.getAll(key)


const createdValues = theseValues.slice(thoseValues.length)
const deletedValues = thoseValues.slice(theseValues.length)


const minLength = Math.min(theseValues.length, thoseValues.length)


const updatedValues = theseValues.slice(0, minLength).filter(unequal, thoseValues)


function append (value) {
this.append(key, value)
}


createdValues.forEach(append, created)
deletedValues.forEach(append, deleted)
updatedValues.forEach(append, updated)
}


return {
created: Array.from(created),
deleted: Array.from(deleted),
updated: Array.from(updated)
}
}
}
})


document.querySelector('[value="Check"]').addEventListener('click', function () {
if (this.form.isChanged) {
console.log(this.form.changes)
} else {
console.log('unchanged')
}
})
<form>
<div>
<label for="name">Text Input:</label>
<input type="text" name="name" id="name" value="" tabindex="1" />
</div>


<div>
<h4>Radio Button Choice</h4>


<label for="radio-choice-1">Choice 1</label>
<input type="radio" name="radio-choice-1" id="radio-choice-1" tabindex="2" value="choice-1" />


<label for="radio-choice-2">Choice 2</label>
<input type="radio" name="radio-choice-2" id="radio-choice-2" tabindex="3" value="choice-2" />
</div>


<div>
<label for="select-choice">Select Dropdown Choice:</label>
<select name="select-choice" id="select-choice">
<option value="Choice 1">Choice 1</option>
<option value="Choice 2">Choice 2</option>
<option value="Choice 3">Choice 3</option>
</select>
</div>


<div>
<label for="textarea">Textarea:</label>
<textarea cols="40" rows="8" name="textarea" id="textarea"></textarea>
</div>


<div>
<label for="checkbox">Checkbox:</label>
<input type="checkbox" name="checkbox" id="checkbox" />
</div>


<div>
<input type="button" value="Check" />
</div>
</form>

在没有 jQuery 的原生 JavaScript 中,表单更改很容易被检测到:

function initChangeDetection(form) {
Array.from(form).forEach(el => el.dataset.origValue = el.value);
}
function formHasChanges(form) {
return Array.from(form).some(el => 'origValue' in el.dataset && el.dataset.origValue !== el.value);
}


可以在页面的整个生命周期中安全地多次调用 initChangeDetection(): 请参见 JSBin 测试


对于不支持新的箭头/数组函数的老浏览器:

function initChangeDetection(form) {
for (var i=0; i<form.length; i++) {
var el = form[i];
el.dataset.origValue = el.value;
}
}
function formHasChanges(form) {
for (var i=0; i<form.length; i++) {
var el = form[i];
if ('origValue' in el.dataset && el.dataset.origValue !== el.value) {
return true;
}
}
return false;
}

我真的很喜欢上面 Teekin 的贡献,并且已经实施了。

However, I have expanded it to allow for checkboxes too using code like this:

// Gets all form elements from the entire document.
function getAllFormElements() {
// Return variable.
var all_form_elements = Array();


// The form.
var Form = document.getElementById('frmCompDetls');


// Different types of form elements.
var inputs = Form.getElementsByTagName('input');
var textareas = Form.getElementsByTagName('textarea');
var selects = Form.getElementsByTagName('select');
var checkboxes = Form.getElementsByTagName('CheckBox');


// We do it this way because we want to return an Array, not a NodeList.
var i;
for (i = 0; i < inputs.length; i++) {
all_form_elements.push(inputs[i]);
}
for (i = 0; i < textareas.length; i++) {
all_form_elements.push(textareas[i]);
}
for (i = 0; i < selects.length; i++) {
all_form_elements.push(selects[i]);
}
for (i = 0; i < checkboxes.length; i++) {
all_form_elements.push(checkboxes[i]);
}
return all_form_elements;
}


// Sets the initial values of every form element.
function setInitialFormValues() {
var inputs = getAllFormElements();
for (var i = 0; i < inputs.length; i++) {
if(inputs[i].type != "checkbox"){
initial_values.push(inputs[i].value);
}
else
{
initial_values.push(inputs[i].checked);
}
}
    

}


function hasFormChanged() {
var has_changed = false;
var elements = getAllFormElements();
var diffstring = ""
for (var i = 0; i < elements.length; i++) {
if (elements[i].type != "checkbox"){
if (elements[i].value != initial_values[i]) {
has_changed = true;
//diffstring = diffstring + elements[i].value+" Was "+initial_values[i]+"\n";
break;
}
}
else
{
if (elements[i].checked != initial_values[i]) {
has_changed = true;
//diffstring = diffstring + elements[i].value+" Was "+initial_values[i]+"\n";
break;
}
}
}
//alert(diffstring);
return has_changed;
}


取舍字符串只是一个调试工具