Обнаружение несохраненных изменений


95

У меня есть требование реализовать запрос «Несохраненные изменения» в приложении ASP .Net. Если пользователь изменяет элементы управления в веб-форме и пытается уйти перед сохранением, должно появиться приглашение, предупреждающее его о том, что у них есть несохраненные изменения, и дающее ему возможность отменить и остаться на текущей странице. Приглашение не должно отображаться, если пользователь не коснулся ни одного из элементов управления.

В идеале я хотел бы реализовать это в JavaScript, но прежде чем я перейду к развертыванию собственного кода, есть ли какие-либо существующие фреймворки или рекомендуемые шаблоны проектирования для достижения этого? В идеале я бы хотел что-то, что можно было бы повторно использовать на нескольких страницах с минимальными изменениями.


Меня интересует то же самое, только не для asp.net, но я думаю, что есть общее решение.
Майкл Нил,

Решение, которое я опубликовал ниже, можно использовать в простом HTML / Javscript. Просто измените «скрытый код» на атрибуты в самих тегах HTML.
Уэйн

Я знаю, что опаздываю на 5 лет, но думаю, что могу улучшить предыдущие решения ... Я просто исправил и обновил свой ответ ниже.
skibulk

Ответы:


96

Используя jQuery:

var _isDirty = false;
$("input[type='text']").change(function(){
  _isDirty = true;
});
// replicate for other input types and selects

При необходимости комбинируйте с onunload/ onbeforeunloadметодами.

Из комментариев следующее относится ко всем полям ввода без дублирования кода:

$(':input').change(function () {

Использование $(":input")относится ко всем элементам input, textarea, select и button.


32
Проголосуйте за, потому что я использовал это решение, но есть более простой способ. Если вы используете: $ (': input'). Change (function () {тогда это работает для всех полей ввода, вам не нужно реплицировать для других типов ввода. $ (": Input") выбирает весь ввод, textarea , выберите элементы и кнопки
CodeClimber

5
Думаю, в Firefox могут быть проблемы. Если пользователь изменяет форму, а затем нажимает кнопку «Обновить» в браузере, страница будет перезагружена, и Firefox заполнит форму предыдущими записями пользователя - без запуска события изменения. Теперь форма будет грязной, но флаг не будет установлен, если пользователь не внесет другое изменение.
Оскар Берггрен

8
Просто намек. Если форма грязная, это еще не означает, что она действительно содержит измененные данные. Для удобства использования нужно сравнивать старые данные с новыми;)
Слава Фомин II

Имейте в виду, что changeдля ввода текста срабатывает один раз при выходе / потере фокуса, тогда как inputтриггеры для каждого нажатия клавиши. (Обычно я использую его вместе changeс JQuery one(), чтобы предотвратить множественные события.)
SpazzMarticus

46

Одна часть головоломки:

/**
 * Determines if a form is dirty by comparing the current value of each element
 * with its default value.
 *
 * @param {Form} form the form to be checked.
 * @return {Boolean} <code>true</code> if the form is dirty, <code>false</code>
 *                   otherwise.
 */
function formIsDirty(form) {
  for (var i = 0; i < form.elements.length; i++) {
    var element = form.elements[i];
    var type = element.type;
    if (type == "checkbox" || type == "radio") {
      if (element.checked != element.defaultChecked) {
        return true;
      }
    }
    else if (type == "hidden" || type == "password" ||
             type == "text" || type == "textarea") {
      if (element.value != element.defaultValue) {
        return true;
      }
    }
    else if (type == "select-one" || type == "select-multiple") {
      for (var j = 0; j < element.options.length; j++) {
        if (element.options[j].selected !=
            element.options[j].defaultSelected) {
          return true;
        }
      }
    }
  }
  return false;
}

И еще :

window.onbeforeunload = function(e) {
  e = e || window.event;  
  if (formIsDirty(document.forms["someForm"])) {
    // For IE and Firefox
    if (e) {
      e.returnValue = "You have unsaved changes.";
    }
    // For Safari
    return "You have unsaved changes.";
  }
};

Завершите все, и что вы получите?

var confirmExitIfModified = (function() {
  function formIsDirty(form) {
    // ...as above
  }

  return function(form, message) {
    window.onbeforeunload = function(e) {
      e = e || window.event;
      if (formIsDirty(document.forms[form])) {
        // For IE and Firefox
        if (e) {
          e.returnValue = message;
        }
        // For Safari
        return message;
      }
    };
  };
})();

confirmExitIfModified("someForm", "You have unsaved changes.");

Вы, вероятно, также захотите изменить регистрацию beforeunloadобработчика событий, чтобы использовать LIBRARY_OF_CHOICEрегистрацию событий.


В Firefox 9.0.1 я не получаю никаких сообщений. У меня есть сообщение по умолчанию: «Вы уверены, что хотите покинуть страницу? Некоторые данные могут быть потеряны» (это более или менее перевод сообщения, которое отображается в моем неанглийском браузере)
Romain Guidoux

1
Этот сценарий потрясающий, но для меня он также отображал предупреждающее сообщение при отправке моей формы. Я исправил это, отменив привязку события в onClick ( onclick="window.onbeforeunload=null")
Joost

1
Мне нравится подход сравнения свойств defaultValue и т. Д., А не установка грязного бита. Это означает, что если кто-то изменит поле, а затем вернет его обратно, форма не будет считаться грязной.
thelem

Спасибо, это здорово. Подскажите, пожалуйста, как зарегистрироваться на jquery? Если у меня нет этого скрипта на той же странице html, он не срабатывает. Если он у меня внутри внешнего файла .js, он не работает.
Шива Нару

17

На странице .aspx вам нужна функция Javascript, чтобы определить, является ли информация формы "грязной".

<script language="javascript">
    var isDirty = false;

    function setDirty() {
        isDirty = true;
    }

    function checkSave() {
        var sSave;
        if (isDirty == true) {
            sSave = window.confirm("You have some changes that have not been saved. Click OK to save now or CANCEL to continue without saving.");
            if (sSave == true) {
                document.getElementById('__EVENTTARGET').value = 'btnSubmit';
                document.getElementById('__EVENTARGUMENT').value = 'Click';  
                window.document.formName.submit();
            } else {
                 return true;
            }
        }
    }
</script>
<body class="StandardBody" onunload="checkSave()">

а в выделенном коде добавьте триггеры в поля ввода, а также сбросьте кнопки отправки / отмены ....

btnSubmit.Attributes.Add("onclick", "isDirty = 0;");
btnCancel.Attributes.Add("onclick", "isDirty = 0;");
txtName.Attributes.Add("onchange", "setDirty();");
txtAddress.Attributes.Add("onchange", "setDirty();");
//etc..

9

Далее используется функция браузера onbeforeunload и jquery для захвата любого события onchange. ИТ-специалисты также ищут любые кнопки отправки или сброса, чтобы сбросить флаг, указывающий, что изменения произошли.

dataChanged = 0;     // global variable flags unsaved changes      

function bindForChange(){    
     $('input,checkbox,textarea,radio,select').bind('change',function(event) { dataChanged = 1})
     $(':reset,:submit').bind('click',function(event) { dataChanged = 0 })
}


function askConfirm(){  
    if (dataChanged){ 
        return "You have some unsaved changes.  Press OK to continue without saving." 
    }
}

window.onbeforeunload = askConfirm;
window.onload = bindForChange;

Привет, сброс Colin не работал, когда я поставил флажок, сначала я проверил его и снова снял флажок, но появляется предупреждение, как я могу выполнить сброс, когда пользователь снял флажок
Разработчик

8

Всем спасибо за ответы. В итоге я реализовал решение с использованием JQuery и подключаемого модуля Protect-Data . Это позволяет мне автоматически применять мониторинг ко всем элементам управления на странице.

Однако есть несколько предостережений, особенно при работе с приложением ASP .Net:

  • Когда пользователь выбирает опцию отмены, функция doPostBack выдает ошибку JavaScript. Мне пришлось вручную поставить try-catch вокруг вызова .submit в doPostBack, чтобы подавить его.

  • На некоторых страницах пользователь может выполнить действие, которое выполняет обратную передачу на ту же страницу, но не сохраняет. Это приводит к сбросу любой логики JavaScript, поэтому он думает, что ничего не изменилось после обратной передачи, когда что-то могло измениться. Мне пришлось реализовать скрытое текстовое поле, которое отправляется обратно со страницей и используется для хранения простого логического значения, указывающего, являются ли данные грязными. Это сохраняется во всех обратных передачах.

  • Вы можете захотеть, чтобы некоторые обратные передачи на странице не вызывали диалоговое окно, например, кнопка «Сохранить». В этом случае вы можете использовать JQuery для добавления функции OnClick, которая устанавливает для window.onbeforeunload значение null.

Надеюсь, это будет полезно для всех, кому нужно реализовать что-то подобное.


7

Общее решение Поддержка нескольких форм на данной странице ( просто скопируйте и вставьте в свой проект )

$(document).ready(function() {
    $('form :input').change(function() {
        $(this).closest('form').addClass('form-dirty');
    });

    $(window).bind('beforeunload', function() {
        if($('form:not(.ignore-changes).form-dirty').length > 0) {
            return 'You have unsaved changes, are you sure you want to discard them?';
        }
    });

    $('form').bind('submit',function() {
        $(this).closest('form').removeClass('form-dirty');
        return true;
    });
});

Примечание. Это решение объединено с решениями других компаний для создания общего интегрированного решения.

Особенности:

  • Просто скопируйте и вставьте в свое приложение.
  • Поддерживает несколько форм.
  • Вы можете стилизовать или сделать грязные формы действий, так как они имеют класс "грязная форма".
  • Вы можете исключить некоторые формы, добавив класс ignore-changes.

Это работает хорошо, но я заменяю добавленные классы _isDirtyпеременной, как описано Аароном Пауэллом. Я не видел необходимости добавлять и удалять классы из HTML вместо переменных в javascript.
Мартин

1
Идея использования классов html здесь заключается в том, чтобы прикрепить атрибут «dirty» к каждой форме, поскольку вы не можете просто использовать переменные для создания общего решения js / html для приложения.
MhdSyrwan

1
Другими словами, использование переменной будет работать только для одной формы, иначе вам понадобится массив грязных флагов, что значительно усложнит решение.
MhdSyrwan

1
Спасибо, что объяснили это. Мне нужна была только одна страница формы.
Мартин

6

Следующее решение работает для прототипа (протестировано в FF, IE 6 и Safari). Он использует общий наблюдатель формы (который запускает форму: изменяется при изменении любых полей формы), который вы также можете использовать для других вещей.

/* use this function to announce changes from your own scripts/event handlers.
 * Example: onClick="makeDirty($(this).up('form'));"
 */
function makeDirty(form) {
    form.fire("form:changed");
}

function handleChange(form, event) {
    makeDirty(form);
}

/* generic form observer, ensure that form:changed is being fired whenever
 * a field is being changed in that particular for
 */
function setupFormChangeObserver(form) {
    var handler = handleChange.curry(form);

    form.getElements().each(function (element) {
        element.observe("change", handler);
    });
}

/* installs a form protector to a form marked with class 'protectForm' */
function setupProtectForm() {
    var form = $$("form.protectForm").first();

    /* abort if no form */
    if (!form) return;

    setupFormChangeObserver(form);

    var dirty = false;
    form.observe("form:changed", function(event) {
        dirty = true;
    });

    /* submitting the form makes the form clean again */
    form.observe("submit", function(event) {
        dirty = false;
    });

    /* unfortunatly a propper event handler doesn't appear to work with IE and Safari */
    window.onbeforeunload = function(event) {
        if (dirty) {
            return "There are unsaved changes, they will be lost if you leave now.";
        }
    };
}

document.observe("dom:loaded", setupProtectForm);

6

Вот простое решение javascript / jquery. Он учитывает «отмены» пользователем, он инкапсулирован в функцию для простоты применения и не дает сбоев при отправке. Просто вызовите функцию и передайте идентификатор вашей формы.

Эта функция сериализует форму один раз при загрузке страницы и еще раз перед тем, как пользователь покинет страницу. Если два состояния формы различаются, отображается подсказка.

Попробуйте: http://jsfiddle.net/skibulk/Ydt7Y/

function formUnloadPrompt(formSelector) {
    var formA = $(formSelector).serialize(), formB, formSubmit = false;

    // Detect Form Submit
    $(formSelector).submit( function(){
        formSubmit = true;
    });

    // Handle Form Unload    
    window.onbeforeunload = function(){
        if (formSubmit) return;
        formB = $(formSelector).serialize();
        if (formA != formB) return "Your changes have not been saved.";
    };
}

$(function(){
    formUnloadPrompt('form');
});

2
Я попробовал ваш подход. Он работает в Chrome и Firefox, но не может работать в Safari на Ipad Mini ..
Дмитрий К,

4

Недавно я внес свой вклад в плагин jQuery с открытым исходным кодом под названием dirtyForms .

Плагин разработан для работы с динамически добавляемым HTML, поддерживает несколько форм, может поддерживать практически любую диалоговую структуру, возвращается к браузеру перед диалоговым окном выгрузки, имеет подключаемую вспомогательную структуру для поддержки получения грязного статуса из пользовательских редакторов (плагин tinyMCE включен) , работает в iFrames, а грязный статус можно установить или сбросить по желанию.

https://github.com/snikch/jquery.dirtyforms


4

Обнаружить изменения формы с помощью jQuery очень просто:

var formInitVal = $('#formId').serialize(); // detect form init value after form is displayed

// check for form changes
if ($('#formId').serialize() != formInitVal) {
    // show confirmation alert
}

2

Я расширил предложение Слэйса, приведенное выше, включив в него большинство редактируемых элементов, а также исключив определенные элементы (здесь используется стиль CSS, называемый «srSearch») из установки флага грязных данных.

<script type="text/javascript">
        var _isDirty = false;
        $(document).ready(function () {            

            // Set exclude CSS class on radio-button list elements
            $('table.srSearch input:radio').addClass("srSearch");

            $("input[type='text'],input[type='radio'],select,textarea").not(".srSearch").change(function () {
                _isDirty = true;
            });
        });

        $(window).bind('beforeunload', function () {
            if (_isDirty) {
                return 'You have unsaved changes.';
            }
        });        


2
      var unsaved = false;
    $(":input").change(function () {         
        unsaved = true;
    });

    function unloadPage() {         
        if (unsaved) {             
          alert("You have unsaved changes on this page. Do you want to leave this page and discard your changes or stay on this page?");
        }
    } 

window.onbeforeunload = unloadPage;


1

Именно это и есть плагин Fleegix.js fleegix.form.diff(http://js.fleegix.org/plugins/form/diff ) был создан для. Сериализуйте начальное состояние формы при загрузке, используя fleegix.form.toObject(http://js.fleegix.org/ref#fleegix.form.toObject ) и сохраните его в переменной, затем сравните с текущим состоянием, используя fleegix.form.diffпри выгрузке. Проще простого.


0

Один метод , использующий массивы для хранения переменных, чтобы можно было отслеживать изменения.

Вот очень простой метод обнаружения изменений , но остальное не так элегантно.

Еще один довольно простой и небольшой метод из блога Farfetched Blog :

<body onLoad="lookForChanges()" onBeforeUnload="return warnOfUnsavedChanges()">
<form>
<select name=a multiple>
 <option value=1>1
 <option value=2>2
 <option value=3>3
</select>
<input name=b value=123>
<input type=submit>
</form>

<script>
var changed = 0;
function recordChange() {
 changed = 1;
}
function recordChangeIfChangeKey(myevent) {
 if (myevent.which && !myevent.ctrlKey && !myevent.ctrlKey)
  recordChange(myevent);
}
function ignoreChange() {
 changed = 0;
}
function lookForChanges() {
 var origfunc;
 for (i = 0; i < document.forms.length; i++) {
  for (j = 0; j < document.forms[i].elements.length; j++) {
   var formField=document.forms[i].elements[j];
   var formFieldType=formField.type.toLowerCase();
   if (formFieldType == 'checkbox' || formFieldType == 'radio') {
    addHandler(formField, 'click', recordChange);
   } else if (formFieldType == 'text' || formFieldType == 'textarea') {
    if (formField.attachEvent) {
     addHandler(formField, 'keypress', recordChange);
    } else {
     addHandler(formField, 'keypress', recordChangeIfChangeKey);
    }
   } else if (formFieldType == 'select-multiple' || formFieldType == 'select-one') {
    addHandler(formField, 'change', recordChange);
   }
  }
  addHandler(document.forms[i], 'submit', ignoreChange);
 }
}
function warnOfUnsavedChanges() {
 if (changed) {
  if ("event" in window) //ie
   event.returnValue = 'You have unsaved changes on this page, which will be discarded if you leave now. Click "Cancel" in order to save them first.';
  else //netscape
   return false;
 }
}
function addHandler(target, eventName, handler) {
 if (target.attachEvent) {
  target.attachEvent('on'+eventName, handler);
 } else {
  target.addEventListener(eventName, handler, false);
 }
}
</script>

0

В IE document.ready не будет работать должным образом, он обновит значения ввода.

поэтому нам нужно привязать событие загрузки внутри функции document.ready, которая также будет обрабатываться для браузера IE.

ниже приведен код, который следует поместить в функцию document.ready.

 $(document).ready(function () {
   $(window).bind("load", function () { 
    $("input, select").change(function () {});
   });
});
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.