Каков наилучший способ отслеживать изменения в форме с помощью JavaScript? - PullRequest
45 голосов
/ 11 октября 2008

Я бы хотел отслеживать изменения во входных данных в форме через JavaScript. Мое намерение (но не ограничено)

  • включить кнопку «Сохранить» только тогда, когда что-то изменилось
  • предупреждение, если пользователь хочет закрыть страницу и что-то не сохраняется

Идеи

Ответы [ 15 ]

32 голосов
/ 11 октября 2008

Переберите все входные элементы и установите обработчик onchange для каждого. Когда это срабатывает, установите флаг, который позволит вам знать, что форма изменилась. Базовую версию этого было бы очень легко настроить, но она не была бы достаточно умной, чтобы распознать, если кто-то изменил ввод с «a» на «b», а затем снова на «a». Если бы было важно разобраться в этом деле, это все равно было бы возможно, но потребовало бы немного больше работы.

Вот базовый пример в jQuery:

$("#myForm")
    .on("input", function() {
        // do whatever you need to do when something's changed.
        // perhaps set up an onExit function on the window
        $('#saveButton').show();
    })
;
17 голосов
/ 11 октября 2008

Элементы текстовой формы в JS предоставляют свойство .value и свойство .defaultValue, поэтому вы можете легко реализовать что-то вроде:

function formChanged(form) {
  for (var i = 0; i < form.elements.length; i++) {
      if(form.elements[i].value != form.elements[i].defaultValue) return(true);
  }
  return(false);
}

Для флажков и переключателей посмотрите, есть ли element.checked != element.defaultChecked, а для элементов HTML <select /> вам нужно перебрать массив select.options и проверить для каждого параметра, selected == defaultSelected.

Возможно, вы захотите использовать фреймворк, такой как jQuery , чтобы присоединить обработчики к событию onchange каждого отдельного элемента формы. Эти обработчики могут вызывать ваш код formChanged() и изменять свойство enabled вашей кнопки «сохранить», и / или прикреплять / отсоединять обработчик события для события beforeunload тела документа.

7 голосов
/ 09 июля 2013

Попробуйте

function isModifiedForm(form){
  var __clone = $(form).clone();
  __clone[0].reset();
  return $(form).serialize() == $(__clone).serialize();
}

Надеюсь, это поможет))

4 голосов
/ 22 октября 2013

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

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

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

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

    // Enable & Disable Submit Button
    var formToggleSubmit = function(){
        formB = $(formSelector).serialize();
        $(formSelector+' [type="submit"]').attr( "disabled", formA == formB);
    };

    formToggleSubmit();
    $(formSelector).change(formToggleSubmit);
    $(formSelector).keyup(formToggleSubmit);
}



// Call function on DOM Ready:

$(function(){
    formUnloadPrompt('form');
});
2 голосов
/ 11 октября 2008

Я ответил на вопрос, подобный этому, на Ars Technica, но вопрос был сформулирован так, что изменения необходимо было обнаружить, даже если пользователь не размывает текстовое поле (в этом случае событие изменения никогда не срабатывает). Я придумал всеобъемлющий сценарий, который:

  1. включает кнопки отправки и сброса при изменении значений поля
  2. отключает кнопки отправки и сброса при сбросе формы
  3. прерывает выход из страницы, если данные формы изменились и не были отправлены
  4. поддерживает IE 6+, Firefox 2+, Safari 3+ (и, вероятно, Opera, но я не тестировал)

Этот скрипт зависит от Prototype, но его можно легко адаптировать к другой библиотеке или автономно.

$(document).observe('dom:loaded', function(e) {
    var browser = {
        trident: !!document.all && !window.opera,
        webkit: (!(!!document.all && !window.opera) && !document.doctype) ||
            (!!window.devicePixelRatio && !!window.getMatchedCSSRules)
    };

    // Select form elements that won't bubble up delegated events (eg. onchange)
    var inputs = $('form_id').select('select, input[type="radio"], input[type="checkbox"]');

    $('form_id').observe('submit', function(e) {
        // Don't bother submitting if form not modified
        if(!$('form_id').hasClassName('modified')) {
            e.stop();
            return false;
        }
        $('form_id').addClassName('saving');
    });

    var change = function(e) {
        // Paste event fires before content has been pasted
        if(e && e.type && e.type == 'paste') {
            arguments.callee.defer();
            return false;
        }

        // Check if event actually results in changed data
        if(!e || e.type != 'change') {
            var modified = false;
            $('form_id').getElements().each(function(element) {
                if(element.tagName.match(/^textarea$/i)) {
                    if($F(element) != element.defaultValue) {
                        modified = true;
                    }
                    return;
                } else if(element.tagName.match(/^input$/i)) {
                    if(element.type.match(/^(text|hidden)$/i) && $F(element) != element.defaultValue) {
                        modified = true;
                    } else if(element.type.match(/^(checkbox|radio)$/i) && element.checked != element.defaultChecked) {
                        modified = true;
                    }
                }
            });
            if(!modified) {
                return false;
            }
        }

        // Mark form as modified
        $('form_id').addClassName('modified');

        // Enable submit/reset buttons
        $('reset_button_id').removeAttribute('disabled');
        $('submit_button_id').removeAttribute('disabled');

        // Remove event handlers as they're no longer needed
        if(browser.trident) {
            $('form_id').stopObserving('keyup', change);
            $('form_id').stopObserving('paste', change);
        } else {
            $('form_id').stopObserving('input', change);
        }
        if(browser.webkit) {
            $$('#form_id textarea').invoke('stopObserving', 'keyup', change);
            $$('#form_id textarea').invoke('stopObserving', 'paste', change);
        }
        inputs.invoke('stopObserving', 'change', arguments.callee);
    };

    $('form_id').observe('reset', function(e) {
        // Unset form modified, restart modified check...
        $('reset_button_id').writeAttribute('disabled', true);
        $('submit_button_id').writeAttribute('disabled', true);
        $('form_id').removeClassName('modified');
        startObservers();
    });

    var startObservers = (function(e) {
        if(browser.trident) {
            $('form_id').observe('keyup', change);
            $('form_id').observe('paste', change);
        } else {
            $('form_id').observe('input', change);
        }
        // Webkit apparently doesn't fire oninput in textareas
        if(browser.webkit) {
            $$('#form_id textarea').invoke('observe', 'keyup', change);
            $$('#form_id textarea').invoke('observe', 'paste', change);
        }
        inputs.invoke('observe', 'change', change);
        return arguments.callee;
    })();

    window.onbeforeunload = function(e) {
        if($('form_id').hasClassName('modified') && !$('form_id').hasClassName('saving')) {
            return 'You have unsaved content, would you really like to leave the page? All your changes will be lost.';
        }
    };

});
2 голосов
/ 11 октября 2008

Если вы используете каркас веб-приложения (rails, ASP.NET, Cake, symfony), должны быть пакеты для проверки ajax,

http://webtecker.com/2008/03/17/list-of-ajax-form-validators/

и некоторая оболочка onbeforeunload () для предупреждения пользователей о том, что они собираются закрыть форму:

http://pragmatig.wordpress.com/2008/03/03/protecting-userdata-from-beeing-lost-with-jquery/ Обнаружение несохраненных изменений

1 голос
/ 25 февраля 2013

Я использовал dirtyforms.js. У меня хорошо работает.

http://mal.co.nz/code/jquery-dirty-forms/

1 голос
/ 11 октября 2008

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

// this example uses the prototype library
// also, it's not very efficient, I just threw it together
var valuesAtLoad = [];
var valuesOnCheck = [];
var isDirty = false;
var names = [];
Event.observe(window, 'load', function() {
    $$('.field').each(function(i) {
        valuesAtLoad.push($F(i));
    });
});

var checkValues = function() {
    var changes = [];
    valuesOnCheck = [];
    $$('.field').each(function(i) {
        valuesOnCheck.push($F(i));
    });

    for(var i = 0; i <= valuesOnCheck.length - 1; i++ ) {
        var source = valuesOnCheck[i];
        var compare = valuesAtLoad[i];
        if( source !== compare ) {
            changes.push($$('.field')[i]);
        }
    }

    return changes.length > 0 ? changes : [];
};

setInterval(function() { names = checkValues().pluck('id'); isDirty = names.length > 0; }, 100);

// notify the user when they exit
Event.observe(window, 'beforeunload', function(e) {
     e.returnValue = isDirty ? "you have changed the following fields: \r\n" + names + "\r\n these changes will be lost if you exit. Are you sure you want to continue?" : true;
});
1 голос
/ 11 октября 2008

Чтобы предупредить пользователя перед закрытием, используйте unbeforeunload:

window.onbeforeunload = function() {
   return "You are about to lose your form data.";
};
0 голосов
/ 29 апреля 2015

Я провел несколько кросс-браузерных тестов.

На Chrome и Safari это приятно:

<form onchange="validate()">
...
</form>

Для Firefox + Chrome / Safari я иду с этим:

<form onkeydown="validate()">
    ...
    <input type="checkbox" onchange="validate()">
</form>

Для таких элементов, как флажки или радиокнопки, требуется собственный прослушиватель событий onchange.

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