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


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.