web-dev-qa-db-fra.com

Quel est le meilleur moyen de suivre les modifications apportées à un formulaire via JavaScript?

Je voudrais suivre les modifications des entrées dans un formulaire via JavaScript. Mon intention est (mais sans s'y limiter) de

  • activez le bouton "enregistrer" uniquement lorsque quelque chose a changé
  • alerte si l'utilisateur veut fermer la page et que quelque chose n'est pas enregistré

Des idées?

43
Ricardo Acras

Parcourez tous les éléments en entrée et placez un gestionnaire onchange sur chacun d'eux. Lorsque cela se déclenche, définissez un indicateur vous permettant de savoir que le formulaire a changé. Une version de base de ce serait très facile à configurer, mais ne serait pas assez intelligente pour reconnaître si quelqu'un changeait une entrée de "a" en "b" puis revenait à "a". Si cela était important, alors ce serait toujours possible, mais cela demanderait un peu plus de travail.

Voici un exemple de base dans 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();
    })
;
30
nickf

Les éléments de formulaire texte dans JS exposent une propriété .value et une propriété .defaultValue, de sorte que vous pouvez facilement implémenter quelque chose comme:

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

Pour les cases à cocher et les boutons radio, voyez si element.checked != element.defaultChecked, et pour les éléments HTML <select />, vous devrez parcourir le tableau select.options et vérifier pour chaque option si selected == defaultSelected.

Vous voudrez peut-être utiliser un cadre tel que jQuery pour attacher des gestionnaires à l'événement onchange de chaque élément de formulaire. Ces gestionnaires peuvent appeler votre code formChanged() et modifier la propriété enabled de votre bouton "Enregistrer", et/ou attacher/détacher un gestionnaire d'événement pour l'événement beforeunload du corps du document.

17
Dylan Beattie

Essayer

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

J'espère que ça aide))

7
Nikita Makarov

Voici une méthode simple à utiliser pour détecter les modifications de formulaire javascript & jquery. Il désactive le bouton d'envoi jusqu'à ce que des modifications soient apportées. Il détecte les tentatives de sortie de la page autrement que par l'envoi du formulaire. Il rend compte des "annulations" de l'utilisateur, il est encapsulé dans une fonction pour faciliter l'application et ne provoque pas de ratés lors de l'envoi. Appelez simplement la fonction et transmettez l'ID de votre formulaire.

Cette fonction sérialise le formulaire une fois lorsque la page est chargée et à nouveau avant que l'utilisateur ne quitte la page. Si les deux états de formulaire sont différents, l'invite s'affiche.

Essayez-le: 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');
});
4
skibulk

J'ai répondu à une question comme celle-ci sur Ars Technica, mais la question était formulée de telle sorte que les modifications devaient être détectées même si l'utilisateur ne rend pas flou un champ de texte (auquel cas l'événement de modification ne se déclenche jamais). Je suis venu avec un script complet qui:

  1. active les boutons d'envoi et de réinitialisation si les valeurs de champ changent
  2. désactive les boutons d'envoi et de réinitialisation si le formulaire est réinitialisé
  3. interrompt en quittant la page si les données du formulaire ont changé et n'ont pas été soumises
  4. prend en charge IE 6+, Firefox 2+, Safari 3+ (et probablement Opera mais je n'ai pas testé)

Ce script dépend de Prototype mais peut être facilement adapté à une autre bibliothèque ou à une version autonome.

$(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
eyelidlessness

Si vous utilisez un framework d’applications Web (Rails, ASP.NET, Cake, symfony), il devrait y avoir des packages pour la validation ajax, 

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

et un wrapper sur onbeforeunload () pour avertir les utilisateurs sur le point de fermer le formulaire:

http://pragmatig.wordpress.com/2008/03/03/protecting-userdata-from-beeing-lost-with-jquery/Détection des modifications non enregistrées

2
Gene T

J'ai utilisé dirtyforms.js. Fonctionne bien pour moi.

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

1
satyrFrost

Pour alerter l'utilisateur avant la fermeture, utilisez unbeforeunload:

window.onbeforeunload = function() {
   return "You are about to lose your form data.";
};

Je voudrais stocker chaque valeur de champs dans une variable lors du chargement de la page, puis comparer ces valeurs lorsque l'utilisateur décharge la page. Si des différences sont détectées, vous saurez ce qu'il faut sauvegarder et, mieux encore, serez en mesure d'indiquer spécifiquement à l'utilisateur quelles données ne seront pas sauvegardées si elles sortent.

// 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
Jared

J'ai fait des tests multi-navigateurs.

Sur Chrome et Safari c'est Nice:

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

Pour Firefox + Chrome/Safari, je vais avec ceci:

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

Les éléments tels que les cases à cocher ou les boutons radio ont besoin d'un propre écouteur d'événement onchange.

0
Bijan

Attachez un gestionnaire d'événement à chaque événement onchange de formulaire/entrée/sélection/textarea. Définir une variable pour vous dire si vous devez activer le bouton "enregistrer". Créez un gestionnaire onunload qui recherche également un formulaire modifié et, lorsque le formulaire est soumis, réinitialisez la variable:

 window.onunload = checkUnsavedPage; 
 var isDirty = false; 
 var formElements = // Obtenir une référence à tous les éléments de formulaire 
 pour (var i = 0; len = formElements.length; i ++) {
 // Ajoute un événement onchange à chaque élément pour appeler formChanged () 
} 
.____ function formChanged (event) {
 isDirty = false; 
 document.getElementById ("savebtn"). disabled = ""; 
} 

 function checkUnsavedPage () {
 si (isDirty) {
 var isSure = confirm ("vous sur?"); 
 si (! isSure) {
 event.preventDefault (); 
 } 
 } 
} 
0
Eric Wendelin

Voici une mise en œuvre complète de la suggestion de Dylan Beattie :

Cadre Client/JS pour la protection des "données non sauvegardées"?

Vous ne devriez pas avoir besoin de stocker les valeurs initiales pour déterminer si le formulaire a changé, à moins que vous ne le remplissiez de manière dynamique côté client (même si, néanmoins, vous pouvez toujours configurer les propriétés default sur les éléments de formulaire).

0
Jonny Buchanan

J'ai eu le même défi et je pensais à une solution commune. Le code ci-dessous n'est pas parfait, il provient de la première recherche et développement. Voici les étapes que j'ai utilisées:

1) Déplacez le fichier JS suivant vers un autre fichier (par exemple, changeFramework.js)

2) Incluez-le dans votre projet en l'important

3) Dans votre page html, quelle que soit la commande à surveiller, ajoutez la classe "monitorChange"

4) La variable globale 'hasChanged' indiquera s'il y a un changement dans la page sur laquelle vous travaillez.

<script type="text/javascript" id="MonitorChangeFramework">
// MONITOR CHANGE FRAMEWORK
// ALL ELEMENTS WITH CLASS ".monitorChange" WILL BE REGISTERED FOR CHANGE
// ON CHANGE IT WILL RAISE A FLAG
var hasChanged;

function MonitorChange() {
    hasChanged = false;
    $(".monitorChange").change(function () {
        hasChanged = true;
    });
}

Voici les contrôles où j'ai utilisé ce cadre:

<textarea class="monitorChange" rows="5" cols="10" id="testArea"></textarea></br>
        <div id="divDrinks">
            <input type="checkbox" class="chb monitorChange" value="Tea" />Tea </br>
            <input type="checkbox" class="chb monitorChange" value="Milk" checked='checked' />Milk</br>
            <input type="checkbox" class="chb monitorChange" value="Coffee" />Coffee </br>
        </div>
        <select id="comboCar" class="monitorChange">
            <option value="volvo">Volvo</option>
            <option value="saab">Saab</option>
            <option value="mercedes">Mercedes</option>
            <option value="audi">Audi</option>
        </select>
        <button id="testButton">
            test</button><a onclick="NavigateTo()">next >>> </a>

Je crois qu'il peut y avoir une énorme amélioration dans ce cadre. Commentaires/changements/commentaires sont les bienvenus. :)

0
Savaratkar

Vous pouvez également consulter ce plugin jQuery que j’ai construit à jQuery suit les modifications apportées au formulaire plugin

Voir la démo ici et télécharger le fichier JS ici

0
brokenshard