【问题标题】:Generic way to detect if html form is edited检测html表单是否被编辑的通用方法
【发布时间】:2009-06-06 13:28:13
【问题描述】:

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

我想仅在编辑表单时进行持久性调用。表单可以包含任何类型的控件。不必通过键入一些文本来弄脏表单,但在日历控件中选择日期也可以。

实现此目的的一种方法是默认以只读模式显示表单并具有“编辑”按钮,如果用户单击编辑按钮,则调用 DB(再次,无论是否数据已被修改。这是对当前现有数据的更好改进)。

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

【问题讨论】:

  • 一个interesting solution 已由 Craig Buckler 在 SitePoint 上发布。特别有趣的是,该解决方案不依赖 jQuery,并且是跨浏览器兼容的。

标签: javascript jquery html


【解决方案1】:

在纯 javascript 中,这不是一件容易的事,但 jQuery 让它变得非常容易:

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

然后在保存之前,可以检查一下是否改了:

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()) {
   // ...
}

【讨论】:

  • 这很好很简单。但是,如果用户更改表单输入然后恢复更改(例如通过单击复选框两次),则表单将被视为已修改。这是否可以接受当然取决于上下文。如需替代方案,请参阅stackoverflow.com/questions/10311663/…
  • 对于实时输入需要一些更改trackChanges: function () { $(document).on('change', $(this).find(':input'), function (e) { var el = $(e.target); $(el).closest('form').data("changed", true); });
【解决方案2】:

万一 JQuery 是不可能的。在 Google 上快速搜索发现 MD5 和 SHA1 哈希算法的 Javascript 实现。如果需要,您可以连接所有表单输入并对它们进行哈希处理,然后将该值存储在内存中。当用户完成时。连接所有值并再次散列。比较 2 个哈希值。如果它们相同,则用户没有更改任何表单字段。如果它们不同,则说明某些内容已被编辑,您需要调用持久性代码。

【讨论】:

  • 这是我对这个问题的期望,有没有图书馆?
  • 如果你连接所有字段但没有散列它会不会有相同的结果?
  • 是的,但数据本身可能比哈希值大。
【解决方案3】:

我不确定我的问题是否正确,但是 addEventListener 呢?如果您不太关心 IE8 支持,这应该没问题。以下代码对我有用:

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

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

【讨论】:

    【解决方案4】:

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

    $(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>

    【讨论】:

    • 我喜欢你的想法,特别是如果你去掉 jQuery 并使用 vanilla JavaScript 来做。我意识到假设 jQuery 可用的问题,但没有理由不提供更广泛适用的解决方案,similar to this 并在 cmets 中有警告。
    • 请记住serialize() 不会包含disabled 元素。在许多情况下可能没问题,但在其他情况下可能会成为问题。
    【解决方案5】:

    无需 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() 在页面的整个生命周期中可以安全地多次调用:See Test on 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;
    }
    

    【讨论】:

    • 我的表单总是返回 true
    • 您能否将您的表单发布到 JSBin 或 Gist 上,以便我们查看您的表单?
    • 我的错,我选择了带有 jquery 的表单,而不是像您的示例中那样使用 JS 的表单。这是一个不错且干净的 js 解决方案,没有序列化过载。谢谢!
    【解决方案6】:

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

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

    要进行测试,请让一个元素调用函数“changeReportingPeriod()”,您可能希望将其命名为其他名称。

    重要提示:当值已设置为其原始值时(通常在页面加载时,但在我的情况下不是),您必须调用 setInitialValues()。

    注意:我并不声称这是一个优雅的解决方案,事实上我不相信优雅的 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());
        }
    
    
    

    【讨论】:

      【解决方案7】:

      这是一个原生 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>

      【讨论】:

        【解决方案8】:

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

        但是,我已经扩展它以允许复选框也使用这样的代码:

        // 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;
        }
        
        

        diffstring 只是一个调试工具

        【讨论】:

          猜你喜欢
          • 2018-09-17
          • 1970-01-01
          • 2012-05-08
          • 2023-03-06
          • 1970-01-01
          • 1970-01-01
          • 2022-12-20
          • 2010-12-22
          • 2014-04-09
          相关资源
          最近更新 更多