Cách chung để phát hiện xem biểu mẫu html có được chỉnh sửa hay không


94

Tôi có một biểu mẫu html theo thẻ. Khi điều hướng từ tab này sang tab khác, dữ liệu của tab hiện tại vẫn tồn tại (trên DB) ngay cả khi không có thay đổi nào đối với dữ liệu.

Tôi chỉ muốn thực hiện cuộc gọi kiên trì nếu biểu mẫu được chỉnh sửa. Biểu mẫu có thể chứa bất kỳ loại điều khiển nào. Làm bẩn biểu mẫu không cần phải nhập một số văn bản mà chọn một ngày trong điều khiển lịch cũng sẽ đủ điều kiện.

Một cách để đạt được điều này là hiển thị biểu mẫu ở chế độ chỉ đọc theo mặc định và có nút 'Chỉnh sửa' và nếu người dùng nhấp vào nút chỉnh sửa thì lệnh gọi đến DB được thực hiện (một lần nữa, bất kể dữ liệu có được sửa đổi hay không . Đây là một cải tiến tốt hơn cho những gì hiện đang tồn tại).

Tôi muốn biết cách viết một hàm javascript chung để kiểm tra xem có bất kỳ giá trị điều khiển nào đã được sửa đổi không?


Một giải pháp thú vị đã được Craig Buckler đăng trên SitePoint. Đặc biệt quan tâm, giải pháp không dựa trên jQuery và tương thích với nhiều trình duyệt.
MagicAndi

Câu trả lời:


158

Trong javascript thuần túy, đây không phải là một nhiệm vụ dễ dàng, nhưng jQuery làm cho nó rất dễ thực hiện:

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

Sau đó, trước khi lưu, bạn có thể kiểm tra xem nó đã được thay đổi hay chưa:

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

Trong ví dụ trên, biểu mẫu có id bằng "myform".

Nếu bạn cần cái này ở nhiều dạng, bạn có thể dễ dàng biến nó thành một plugin:

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

Sau đó, bạn có thể chỉ cần nói:

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

và kiểm tra xem biểu mẫu đã thay đổi chưa:

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

13
Điều này là tốt đẹp và đơn giản. Tuy nhiên, nếu người dùng thay đổi đầu vào biểu mẫu và sau đó hoàn nguyên thay đổi (ví dụ: bằng cách nhấp vào hộp kiểm hai lần), thì biểu mẫu sẽ được coi là đã sửa đổi. Điều đó có được chấp nhận hay không tất nhiên phụ thuộc vào bối cảnh. Để biết một giải pháp thay thế, hãy xem stackoverflow.com/questions/10311663/…
jlh

1
cho đầu vào sống cần một số thay đổi trackChanges: function () { $(document).on('change', $(this).find(':input'), function (e) { var el = $(e.target); $(el).closest('form').data("changed", true); });
Dimmduh

36

Trong trường hợp JQuery thì khỏi nói. Một tìm kiếm nhanh trên Google đã tìm thấy các triển khai Javascript của các thuật toán băm MD5 và SHA1. Nếu muốn, bạn có thể nối tất cả các đầu vào biểu mẫu và băm chúng, sau đó lưu trữ giá trị đó trong bộ nhớ. Khi người dùng hoàn thành. Nối tất cả các giá trị và băm lại. So sánh 2 hàm băm. Nếu chúng giống nhau, người dùng không thay đổi bất kỳ trường biểu mẫu nào. Nếu chúng khác nhau, một cái gì đó đã được chỉnh sửa và bạn cần gọi mã tồn tại của mình.


2
Đây là những gì tôi mong đợi cho câu hỏi này, Có thư viện nào không?
Hamedz

Nó sẽ không có cùng kết quả nếu bạn nối tất cả các trường nhưng không băm nó?
ashleedawg

Có, nhưng bản thân dữ liệu có thể lớn hơn hàm băm.
JRG

26

Tôi không chắc liệu tôi có trả lời đúng câu hỏi của bạn hay không, nhưng addEventListener thì sao? Nếu bạn không quan tâm quá nhiều đến hỗ trợ của IE8, điều này sẽ ổn. Mã sau đang làm việc cho tôi:

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

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

hoàn hảo cho nhu cầu của tôi
Blessing

8

Một cách khác để đạt được điều này là tuần tự hóa biểu mẫu:

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


Tôi thích cách bạn đang nghĩ, đặc biệt nếu bạn tách jQuery ra và thực hiện với JavaScript vani. Tôi nhận thấy câu hỏi giả định rằng jQuery đã có sẵn, nhưng không có lý do gì để không có một giải pháp thậm chí có thể áp dụng rộng rãi hơn, tương tự như điều này với các lưu ý trong các nhận xét.
ruffin

4

Đây là cách tôi đã làm điều đó (không sử dụng jQuery).

Trong trường hợp của tôi, tôi muốn một phần tử biểu mẫu cụ thể không được tính, vì nó là phần tử đã kích hoạt kiểm tra và do đó sẽ luôn thay đổi. Phần tử đặc biệt được đặt tên là 'report_period' và được mã hóa cứng trong hàm 'hasFormChanged ()'.

Để kiểm tra, hãy thực hiện một phần tử gọi hàm "changeReportingPeriod ()", có thể bạn sẽ muốn đặt tên khác.

QUAN TRỌNG: Bạn phải gọi setInitialValues ​​() khi các giá trị đã được đặt thành giá trị ban đầu của chúng (thường là khi tải trang, nhưng không phải trong trường hợp của tôi).

LƯU Ý: Tôi không khẳng định rằng đây là một giải pháp thanh lịch, trên thực tế, tôi không tin vào các giải pháp JavaScript thanh lịch. Sự nhấn mạnh của cá nhân tôi trong JavaScript là tính dễ đọc, không phải tính sang trọng về cấu trúc (như thể điều đó có thể xảy ra trong JavaScript). Tôi không quan tâm đến kích thước tệp khi viết JavaScript vì đó là mục đích của gzip và việc cố gắng viết mã JavaScript nhỏ gọn hơn luôn dẫn đến các vấn đề không thể chấp nhận được khi bảo trì. Tôi không đưa ra lời xin lỗi, không bày tỏ sự hối hận và từ chối tranh luận về nó. Đó là JavaScript. Xin lỗi, tôi phải nói rõ điều này để thuyết phục bản thân rằng tôi nên đăng bài. Hãy hạnh phúc! :)


    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());
    }



3

Các thay đổi biểu mẫu có thể dễ dàng được phát hiện trong JavaScript gốc mà không cần jQuery:

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()có thể được gọi nhiều lần một cách an toàn trong suốt vòng đời trang của bạn: Xem Kiểm tra trên JSBin


Đối với các trình duyệt cũ hơn không hỗ trợ các hàm mũi tên / mảng mới hơn:

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

luôn luôn trả về true cho hình thức của tôi
Giàu Đá

Bạn có thể đăng biểu mẫu của mình trên JSBin hoặc Gist để chúng tôi có thể xem biểu mẫu của bạn không?
AnthumChris

lỗi của tôi, tôi đã chọn biểu mẫu với jquery chứ không phải với JS như trong ví dụ của bạn. Nó là một giải pháp js đẹp và sạch sẽ, không có quá tải tuần tự hóa. Cảm ơn bạn!
Rich Stone

2

Đây là bản trình diễn phương pháp polyfill trong JavaScript gốc sử dụng FormData()API để phát hiện các mục nhập biểu mẫu đã tạo, cập nhật và đã xóa. Bạn có thể kiểm tra xem có điều gì bị thay đổi bằng cách sử dụng HTMLFormElement#isChangedhay không và lấy một đối tượng chứa các điểm khác biệt từ biểu mẫu đặt lại bằng cách sử dụng HTMLFormElement#changes(giả sử chúng không bị che bởi tên đầu vào):

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>

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.