HTML 양식이 편집되었는지 감지하는 일반적인 방법


96

탭 HTML 양식이 있습니다. 한 탭에서 다른 탭으로 이동하면 데이터가 변경되지 않아도 현재 탭의 데이터가 DB에 유지됩니다.

양식이 편집 된 경우에만 지속성 호출을 만들고 싶습니다. 양식에는 모든 종류의 컨트롤이 포함될 수 있습니다. 일부 텍스트를 입력하여 양식을 더럽히는 것은 아니지만 달력 컨트롤에서 날짜를 선택하는 것도 적합합니다.

이를 달성하는 한 가지 방법은 기본적으로 양식을 읽기 전용 모드로 표시하고 '편집'버튼을 갖는 것이며 사용자가 편집 버튼을 클릭하면 DB에 대한 호출이 이루어집니다 (데이터 수정 여부에 관계없이 다시 한 번) . 이것은 현재 존재하는 것보다 더 나은 개선입니다).

컨트롤 값이 수정되었는지 확인하는 일반 자바 스크립트 함수를 작성하는 방법을 알고 싶습니다.


흥미로운 솔루션은 SitePoint에 크레이그 버클러에 의해 게시되었습니다. 특히이 솔루션은 jQuery에 의존하지 않으며 브라우저 간 호환이 가능합니다.
MagicAndi

답변:


161

순수한 자바 스크립트에서 이것은 쉬운 작업이 아니지만 jQuery를 사용하면 매우 쉽게 할 수 있습니다.

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

그런 다음 저장하기 전에 변경되었는지 확인할 수 있습니다.

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

위의 예에서 양식에는 "myform"과 동일한 ID가 있습니다.

여러 형태로 필요한 경우 쉽게 플러그인으로 전환 할 수 있습니다.

$.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의 빠른 검색에서 MD5 및 SHA1 해시 알고리즘의 Javascript 구현을 찾았습니다. 원하는 경우 모든 양식 입력을 연결하고 해시 한 다음 해당 값을 메모리에 저장할 수 있습니다. 사용자가 완료되면. 모든 값을 연결하고 다시 해시하십시오. 2 개의 해시를 비교하십시오. 동일하면 사용자가 양식 필드를 변경하지 않은 것입니다. 서로 다르면 무언가 편집 된 것이므로 지속성 코드를 호출해야합니다.


2
이것이 제가이 질문에 기대했던 것입니다. 도서관이 있습니까?
Hamedz

모든 필드를 연결했지만 해시 하지 않은 경우 동일한 결과 가 나오지 않습니까?
ashleedawg

예,하지만 데이터 자체가 해시보다 클 수 있습니다.
JRG

27

질문이 올바른지 확실하지 않지만 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를 사용하지 않고).

제 경우에는 검사를 트리거 한 요소이므로 항상 변경 될 것이기 때문에 하나의 특정 양식 요소가 계산되지 않기를 원했습니다. 예외적 인 요소의 이름은 '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());
    }



4

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

항상 내 양식에 대해 true를 반환합니다.
Rich Stone 19

양식을 볼 수 있도록 JSBin 또는 Gist에 양식을 게시 할 수 있습니까?
AnthumChris

내 잘못은 귀하의 예제와 같이 JS가 아닌 jquery로 양식을 선택했습니다. 직렬화 과부하가없는 멋지고 깨끗한 js 솔루션입니다. 감사합니다!
Rich Stone

2

다음은 FormData()API를 사용하여 생성, 업데이트 및 삭제 된 양식 항목을 감지 하는 네이티브 JavaScript의 polyfill 메서드 데모입니다 . 다음을 사용하여 변경된 사항이 있는지 확인하고 다음을 사용하여 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.