วิธีทั่วไปในการตรวจสอบว่ามีการแก้ไขรูปแบบ html หรือไม่


96

ฉันมีแบบฟอร์ม html แบบแท็บ เมื่อนำทางจากแท็บหนึ่งไปยังอีกแท็บข้อมูลปัจจุบันของแท็บจะยังคงอยู่ (บนฐานข้อมูล) แม้ว่าจะไม่มีการเปลี่ยนแปลงข้อมูลก็ตาม

ฉันต้องการให้เรียกการคงอยู่ต่อเมื่อมีการแก้ไขแบบฟอร์มเท่านั้น แบบฟอร์มสามารถมีการควบคุมประเภทใดก็ได้ การทำให้แบบฟอร์มสกปรกไม่จำเป็นต้องพิมพ์ข้อความ แต่การเลือกวันที่ในตัวควบคุมปฏิทินก็มีคุณสมบัติเช่นกัน

วิธีหนึ่งในการบรรลุเป้าหมายนี้คือการแสดงแบบฟอร์มในโหมดอ่านอย่างเดียวตามค่าเริ่มต้นและมีปุ่ม 'แก้ไข' และหากผู้ใช้คลิกปุ่มแก้ไขการเรียกไปยังฐานข้อมูลจะเกิดขึ้น (อีกครั้งโดยไม่คำนึงว่าข้อมูลจะถูกแก้ไขหรือไม่ นี่คือการปรับปรุงให้ดีขึ้นกับสิ่งที่มีอยู่ในปัจจุบัน)

ฉันต้องการทราบวิธีการเขียนฟังก์ชัน javascript ทั่วไปที่จะตรวจสอบว่ามีการแก้ไขค่าการควบคุมใด ๆ หรือไม่?


โซลูชันที่น่าสนใจได้รับการโพสต์โดยเครกโล่ใน SitePoint สิ่งที่น่าสนใจเป็นพิเศษโซลูชันนี้ไม่ได้อาศัย jQuery และเข้ากันได้กับเบราว์เซอร์
MagicAndi

คำตอบ:


162

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

13
นี่เป็นสิ่งที่ดีและเรียบง่าย อย่างไรก็ตามหากผู้ใช้เปลี่ยนการป้อนแบบฟอร์มแล้วเปลี่ยนกลับการเปลี่ยนแปลง (ตัวอย่างเช่นโดยคลิกช่องทำเครื่องหมายสองครั้ง) แบบฟอร์มจะถือว่าแก้ไข ไม่ว่าจะเป็นที่ยอมรับหรือไม่ขึ้นอยู่กับบริบท สำหรับทางเลือกอื่นโปรดดูstackoverflow.com/questions/10311663/…
jlh

1
สำหรับอินพุตสดต้องการการเปลี่ยนแปลงบางอย่าง trackChanges: function () { $(document).on('change', $(this).find(':input'), function (e) { var el = $(e.target); $(el).closest('form').data("changed", true); });
Dimmduh

36

ในกรณีที่ JQuery หมดคำถาม การค้นหาอย่างรวดเร็วบน Google พบการใช้ Javascript ของอัลกอริทึมแฮช MD5 และ SHA1 หากคุณต้องการคุณสามารถเชื่อมต่ออินพุตแบบฟอร์มทั้งหมดและแฮชเข้าด้วยกันจากนั้นเก็บค่านั้นไว้ในหน่วยความจำ เมื่อผู้ใช้เสร็จสิ้น เชื่อมต่อค่าทั้งหมดและแฮชอีกครั้ง เปรียบเทียบแฮช 2 ตัว หากเหมือนกันแสดงว่าผู้ใช้ไม่ได้เปลี่ยนฟิลด์แบบฟอร์มใด ๆ หากแตกต่างกันแสดงว่ามีการแก้ไขบางอย่างและคุณต้องเรียกรหัสการคงอยู่ของคุณ


2
นี่คือสิ่งที่ฉันคาดหวังสำหรับคำถามนี้มีห้องสมุดไหม
Hamedz

จะไม่ได้ผลลัพธ์แบบเดียวกันหรือไม่ถ้าคุณเชื่อมต่อฟิลด์ทั้งหมด แต่ไม่ได้แฮช
ashleedawg

ใช่ แต่ข้อมูลอาจมีขนาดใหญ่กว่าแฮช
JRG

28

ฉันไม่แน่ใจว่าคำถามของคุณถูกต้องหรือเปล่า แต่ addEventListener ล่ะ หากคุณไม่สนใจเกี่ยวกับการสนับสนุน IE8 มากเกินไปสิ่งนี้ก็น่าจะใช้ได้ รหัสต่อไปนี้ใช้งานได้สำหรับฉัน:

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

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

สมบูรณ์แบบสำหรับความต้องการของฉัน
พร

8

อีกวิธีหนึ่งในการบรรลุสิ่งนี้คือการทำให้เป็นอนุกรมแบบฟอร์ม:

$(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 และใช้วานิลลา JavaScript ฉันรู้ว่าคำถามที่สันนิษฐานว่า jQuery มีให้ แต่ไม่มีเหตุผลที่จะไม่มีวิธีแก้ปัญหาที่ใช้งานได้กว้างขวางกว่านี้เช่นเดียวกับข้อควรระวังในความคิดเห็น
ruffin

โปรดทราบว่าserialize()จะไม่รวมdisabledองค์ประกอบต่างๆ อาจจะใช้ได้สำหรับหลาย ๆ กรณี แต่อาจเป็นปัญหาในกรณีอื่น ๆ
zed

4

นี่คือวิธีที่ฉันทำ (โดยไม่ใช้ jQuery)

ในกรณีของฉันฉันไม่ต้องการให้มีการนับองค์ประกอบรูปแบบใดองค์ประกอบหนึ่งเนื่องจากเป็นองค์ประกอบที่เรียกใช้การตรวจสอบและจะมีการเปลี่ยนแปลงอยู่เสมอ อิลิเมนต์พิเศษนี้มีชื่อว่า 'reports_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());
    }



4

การเปลี่ยนแปลงแบบฟอร์มสามารถตรวจพบได้อย่างง่ายดายใน JavaScript ดั้งเดิมโดยไม่ต้องใช้ 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()สามารถเรียกได้อย่างปลอดภัยหลายครั้งตลอดวงจรชีวิตของเพจของคุณ: ดูทดสอบบน 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;
}

คืนค่าจริงสำหรับแบบฟอร์มของฉันเสมอ
Rich Stone

คุณช่วยโพสต์แบบฟอร์มใน JSBin หรือ Gist เพื่อให้เราเห็นแบบฟอร์มของคุณได้ไหม
AnthumChris

ความผิดของฉันฉันเลือกแบบฟอร์มด้วย jquery ไม่ใช่ด้วย JS ตามตัวอย่างของคุณ เป็นโซลูชัน js ที่ดีและสะอาดโดยไม่มีการทำให้เป็นอนุกรมเกินพิกัด ขอขอบคุณ!
Rich Stone

2

นี่คือการสาธิตวิธี polyfill ใน JavaScript ดั้งเดิมที่ใช้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>

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.