web-dev-qa-db-fra.com

Moyen générique de détecter si le formulaire HTML est modifié

J'ai un formulaire HTML à onglets. Lors de la navigation d'un onglet à l'autre, les données de l'onglet actuel sont conservées (sur la base de données) même si les données ne sont pas modifiées.

Je voudrais faire l'appel de persistance que si le formulaire est modifié. Le formulaire peut contenir tout type de contrôle. Il n'est pas nécessaire de salir le formulaire en tapant du texte, mais le choix d'une date dans un contrôle de calendrier serait également admissible.

Une façon d'y parvenir serait d'afficher le formulaire en mode lecture seule par défaut et d'avoir un bouton "Modifier". Si l'utilisateur clique sur le bouton Modifier, l'appel de la base de données est alors effectué (encore une fois, que les données soient modifiées ou non). C'est une meilleure amélioration de ce qui existe actuellement).

J'aimerais savoir comment écrire une fonction JavaScript générique qui permettrait de vérifier si l'une des valeurs de contrôle a été modifiée?

69
Sathya

En javascript pur, cela ne serait pas une tâche facile, mais jQuery le rend très facile à faire:

$("#myform :input").change(function() {
   $("#myform").data("changed",true);
});

Ensuite, avant de sauvegarder, vous pouvez vérifier si cela a été changé:

if ($("#myform").data("changed")) {
   // submit the form
}

Dans l'exemple ci-dessus, le formulaire a un identifiant égal à "myform".

Si vous en avez besoin sous de nombreuses formes, vous pouvez facilement le transformer en plugin:

$.fn.extend({
 trackChanges: function() {
   $(":input",this).change(function() {
      $(this.form).data("changed", true);
   });
 }
 ,
 isChanged: function() { 
   return this.data("changed"); 
 }
});

Ensuite, vous pouvez simplement dire:

$("#myform").trackChanges();

et vérifiez si un formulaire a changé:

if ($("#myform").isChanged()) {
   // ...
}
128

Dans le cas où JQuery est hors de question. Une recherche rapide sur Google a permis de trouver des implémentations Javascript des algorithmes de hachage MD5 et SHA1. Si vous le souhaitez, vous pouvez concaténer toutes les entrées de formulaire et les hacher, puis stocker cette valeur en mémoire. Lorsque l'utilisateur a terminé. Concaténer toutes les valeurs et hachage à nouveau. Comparez les 2 hashes. S'ils sont identiques, l'utilisateur n'a modifié aucun champ de formulaire. S'ils sont différents, quelque chose a été modifié et vous devez appeler votre code de persistance.

32
Matthew Vines

Je ne sais pas si j'ai bien compris votre question, mais qu'en est-il de addEventListener? Si vous ne vous souciez pas trop du support IE8, cela devrait aller. Le code suivant fonctionne pour moi:

var form = document.getElementById("myForm");

form.addEventListener("input", function () {
    console.log("Form has changed!");
});
9
mecograph

Voici comment je l'ai fait (sans utiliser jQuery).

Dans mon cas, je voulais qu'un élément de formulaire en particulier ne soit pas compté, car c'était l'élément qui avait déclenché le contrôle et qui aurait donc toujours changé. L'élément exceptionnel s'appelle 'reporting_period' et est codé en dur dans la fonction 'hasFormChanged ()'.

Pour tester, appelez la fonction "changeReportingPeriod ()", que vous voudrez probablement nommer autrement.

IMPORTANT: vous devez appeler setInitialValues ​​() lorsque les valeurs ont été définies sur leurs valeurs d'origine (généralement au chargement de la page, mais pas dans mon cas).

NOTE: Je ne prétends pas qu'il s'agit d'une solution élégante, en fait, je ne crois pas aux solutions JavaScript élégantes. Mon accent personnel en JavaScript concerne la lisibilité, pas l'élégance structurelle (comme si cela était possible en JavaScript). Je ne me soucie pas du tout de la taille des fichiers lors de l'écriture de JavaScript car c'est à cela que gzip est destiné, et essayer d'écrire du code JavaScript plus compact entraîne invariablement des problèmes de maintenance intolérables. Je ne m'excuse pas, n'exprime aucun remords et refuse d'en débattre. C'est JavaScript. Désolé, je devais le préciser afin de me convaincre que je devrais me donner la peine de poster. Soyez heureux! :)


    var initial_values = new Array();

    // Gets all form elements from the entire document.
    function getAllFormElements() {
        // Return variable.
        var all_form_elements = Array();

        // The form.
        var form_activity_report = document.getElementById('form_activity_report');

        // Different types of form elements.
        var inputs = form_activity_report.getElementsByTagName('input');
        var textareas = form_activity_report.getElementsByTagName('textarea');
        var selects = form_activity_report.getElementsByTagName('select');

        // We do it this way because we want to return an Array, not a NodeList.
        var i;
        for (i = 0; i < inputs.length; i++) {
            all_form_elements.Push(inputs[i]);
        }
        for (i = 0; i < textareas.length; i++) {
            all_form_elements.Push(textareas[i]);
        }
        for (i = 0; i < selects.length; i++) {
            all_form_elements.Push(selects[i]);
        }

        return all_form_elements;
    }

    // Sets the initial values of every form element.
    function setInitialFormValues() {
        var inputs = getAllFormElements();
        for (var i = 0; i < inputs.length; i++) {
            initial_values.Push(inputs[i].value);
        }
    }

    function hasFormChanged() {
        var has_changed = false;
        var elements = getAllFormElements();

        for (var i = 0; i < elements.length; i++) {
            if (elements[i].id != 'reporting_period' && elements[i].value != initial_values[i]) {
                has_changed = true;
                break;
            }
        }

        return has_changed;
    }

    function changeReportingPeriod() {
        alert(hasFormChanged());
    }


4
Teekin

Les modifications de formulaire peuvent être facilement détectées en JavaScript natif sans jQuery:

function initChangeDetection(form) {
  Array.from(form).forEach(el => el.dataset.origValue = el.value);
}
function formHasChanges(form) {
  return Array.from(form).some(el => 'origValue' in el.dataset && el.dataset.origValue !== el.value);
}


initChangeDetection() peut être appelé en toute sécurité plusieurs fois au cours du cycle de vie de votre page: Voir Test sur JSBin


Pour les anciens navigateurs qui ne prennent pas en charge les nouvelles fonctions de flèche/tableau:

function initChangeDetection(form) {
  for (var i=0; i<form.length; i++) {
    var el = form[i];
    el.dataset.origValue = el.value;
  }
}
function formHasChanges(form) {
  for (var i=0; i<form.length; i++) {
    var el = form[i];
    if ('origValue' in el.dataset && el.dataset.origValue !== el.value) {
      return true;
    }
  }
  return false;
}
3
AnthumChris

Une autre façon d'y parvenir est de sérialiser le formulaire:

$(function() {
    var $form = $('form');
    var initialState = $form.serialize();
    
    $form.submit(function (e) {
      if (initialState === $form.serialize()) {
        console.log('Form is unchanged!');
      } else {
        console.log('Form has changed!');
      }
      e.preventDefault();
    });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form>
Field 1: <input type="text" name="field_1" value="My value 1"> <br>
Field 2: <input type="text" name="field_2" value="My value 2"> <br>
Check: <input type="checkbox" name="field_3" value="1"><br>
<input type="submit">
</form>

3
nikoskip

Voici une démonstration de méthode polyfill en JavaScript natif qui utilise l’API FormData() pour détecter les entrées de formulaire créées, mises à jour et supprimées. Vous pouvez vérifier si quelque chose a été modifié avec HTMLFormElement#isChanged et obtenir un objet contenant les différences d'un formulaire de réinitialisation à l'aide de HTMLFormElement#changes (en supposant qu'ils ne soient pas masqués par un nom d'entrée):

Object.defineProperties(HTMLFormElement.prototype, {
  isChanged: {
    configurable: true,
    get: function isChanged () {
      'use strict'

      var thisData = new FormData(this)
      var that = this.cloneNode(true)

      // avoid masking: https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/reset
      HTMLFormElement.prototype.reset.call(that)

      var thatData = new FormData(that)

      const theseKeys = Array.from(thisData.keys())
      const thoseKeys = Array.from(thatData.keys())

      if (theseKeys.length !== thoseKeys.length) {
        return true
      }

      const allKeys = new Set(theseKeys.concat(thoseKeys))

      function unequal (value, index) {
        return value !== this[index]
      }

      for (const key of theseKeys) {
        const theseValues = thisData.getAll(key)
        const thoseValues = thatData.getAll(key)

        if (theseValues.length !== thoseValues.length) {
          return true
        }

        if (theseValues.some(unequal, thoseValues)) {
          return true
        }
      }

      return false
    }
  },
  changes: {
    configurable: true,
    get: function changes () {
      'use strict'

      var thisData = new FormData(this)
      var that = this.cloneNode(true)

      // avoid masking: https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/reset
      HTMLFormElement.prototype.reset.call(that)

      var thatData = new FormData(that)

      const theseKeys = Array.from(thisData.keys())
      const thoseKeys = Array.from(thatData.keys())

      const created = new FormData()
      const deleted = new FormData()
      const updated = new FormData()

      const allKeys = new Set(theseKeys.concat(thoseKeys))

      function unequal (value, index) {
        return value !== this[index]
      }

      for (const key of allKeys) {
        const theseValues = thisData.getAll(key)
        const thoseValues = thatData.getAll(key)

        const createdValues = theseValues.slice(thoseValues.length)
        const deletedValues = thoseValues.slice(theseValues.length)

        const minLength = Math.min(theseValues.length, thoseValues.length)

        const updatedValues = theseValues.slice(0, minLength).filter(unequal, thoseValues)

        function append (value) {
          this.append(key, value)
        }

        createdValues.forEach(append, created)
        deletedValues.forEach(append, deleted)
        updatedValues.forEach(append, updated)
      }

      return {
        created: Array.from(created),
        deleted: Array.from(deleted),
        updated: Array.from(updated)
      }
    }
  }
})

document.querySelector('[value="Check"]').addEventListener('click', function () {
  if (this.form.isChanged) {
    console.log(this.form.changes)
  } else {
    console.log('unchanged')
  }
})
<form>
  <div>
    <label for="name">Text Input:</label>
    <input type="text" name="name" id="name" value="" tabindex="1" />
  </div>

  <div>
    <h4>Radio Button Choice</h4>

    <label for="radio-choice-1">Choice 1</label>
    <input type="radio" name="radio-choice-1" id="radio-choice-1" tabindex="2" value="choice-1" />

    <label for="radio-choice-2">Choice 2</label>
    <input type="radio" name="radio-choice-2" id="radio-choice-2" tabindex="3" value="choice-2" />
  </div>

  <div>
    <label for="select-choice">Select Dropdown Choice:</label>
    <select name="select-choice" id="select-choice">
      <option value="Choice 1">Choice 1</option>
      <option value="Choice 2">Choice 2</option>
      <option value="Choice 3">Choice 3</option>
    </select>
  </div>

  <div>
    <label for="textarea">Textarea:</label>
    <textarea cols="40" rows="8" name="textarea" id="textarea"></textarea>
  </div>

  <div>
    <label for="checkbox">Checkbox:</label>
    <input type="checkbox" name="checkbox" id="checkbox" />
  </div>

  <div>
    <input type="button" value="Check" />
  </div>
</form>

0
Patrick Roberts