Виявлення незбережених змін


94

У мене є вимога реалізувати підказку "Незбережені зміни" у програмі ASP .Net. Якщо користувач змінює елементи керування веб-формою та намагається перейти до збереження перед збереженням, повинно з’явитися підказка, що попереджає їх про збереження змін, і надати їм можливість скасувати та залишитися на поточній сторінці. Підказка не повинна відображатися, якщо користувач не торкнувся жодного з елементів керування.

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


Мене цікавить те саме, тільки не для asp.net, але я думаю, що там є загальне рішення.
— Майкл Ніл

Рішення, яке я опублікував нижче, може бути використано у звичайному HTML / Javscript. Просто змініть "codebehind" на атрибути в самих тегах HTML.
— Wayne

Я знаю, що тут запізнююся на 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")відноситься до всіх елементів введення, тексту, виділення та елементів кнопок.


32
Оновлення, тому що я використовував це рішення, але є трохи простіший спосіб. Якщо ви використовуєте: $ (': input'). Change (function () {тоді це працює для всіх полів введення, вам не потрібно реплікувати для інших типів вводу. $ (": Input") вибирає весь вхід, textarea , виберіть елементи та кнопки
— CodeClimber

5
Я думаю, що це може мати проблеми у Firefox. Якщо користувач змінює форму, а потім натискає кнопку Оновити браузери, сторінка буде перезавантажена, і Firefox заповнить форму попередніми записами користувачів - не запускаючи подію зміни. Тепер форма буде брудною, але прапор не буде встановлено, якщо користувач не зробить іншого редагування.
— Оскар Берггрен

8
Просто підказка. Якщо форма забруднена, це все ще не означає, що вона містить дійсно змінені дані. Для кращого користування вам потрібно насправді порівняти старі дані з новими;)
— Slava Fomin II

Майте на увазі, що changeдля введення тексту спрацьовує один раз при виході / втраті фокусу, тоді як inputтригери для кожного натискання клавіші. (Я зазвичай використовую його разом changeз JQuery's 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 я не отримую жодного повідомлення. У мене є повідомлення за замовчуванням: "Ви впевнені, що хочете залишити сторінку? Деякі дані можуть бути втрачені" (Це більш-менш переклад повідомлення, яке відображає мені мій неанглійський браузер)
— Ромен Гідю

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. IT також шукає будь-які кнопки подання чи скидання, щоб скинути прапор із зазначенням змін.

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;

Привіт скидання коліна не спрацювало, коли я поставив прапорець, спочатку я встановив його і знову зняв прапорець, але попередження запускається, як я можу скинути налаштування, коли користувач зняв прапорець
— розробник

8

Дякую за відповіді всім. У підсумку я реалізував рішення за допомогою JQuery та Protect-Data плагіну . Це дозволяє мені автоматично застосовувати моніторинг до всіх елементів керування на сторінці.

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

  • Коли користувач вибере параметр скасування, функція doPostBack видасть помилку JavaScript. Мені довелося вручну поставити пробний виклик навколо .submit call у межах 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-класів тут - приєднати атрибут 'брудний' до кожної форми, оскільки ви не можете просто використовувати змінні для створення загального рішення-wize 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 ..
— Dimitry K

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

Я розширив пропозицію Slace вище, щоб включити більшість редагованих елементів, а також виключаючи певні елементи (тут стиль 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.