Validering af HTML-formularer

I et HTML input-element kan man skrive hvad som helst. Hvis der kræves en e-mail-adresse eller en dato, så kan ikke alle tekster accepteres. Det sparer tid, for både bruger og server, hvis teksten afvises inden formularen sendes til serveren.

Form-validering i browseren sparer denne tid på den mest hensigtsmæssige måde hvis den køres lige før formularen afsendes.

Denne side viser Javascript-kode der kan bruges til at validere formularer med, og hvordan den bruges.

Hvad validering på klienten ikke kan

Validering på klienten er ikke sikker på at blive udført.

Formålet med validering på klienten (browseren) er ikke at undvære validering på serveren. Det er kun at spare tid ved hurtigt at afvise ugyldigt input, som ellers ville blive afvist af servern.

Validering på klienten er afhængigt af Javascript. Hvis brugeren har slået Javascript fra, eller bruger en browser uden Javascript, så bliver formen sent uvalideret. Hvis det er et problem for serveren, så kan skruppelløse individer udnytte serverens svagheder ved bevidst at slå validering fra, og så sende data der udnytter problemet. Derfor skal potentielt farlige data tjekkes af serveren.

Hvad er god validering

God validering blander sig så lidt som muligt, men giver så meget information som muligt. Den blokkerer ikke for gyldige data.

For at blande sig så lidt som muligt, så kalder man først valideringen når formularen er færdigudfyldt, altså lige før den afsendes. Hvis der ingen fejl er, så afsendes formularen uden videre. Hvis nogle felter er udfyldt forkert, så gives en samlet advarsel, og afsendelsen stoppes.

Hvordan gør man så?

At kalde validerings-funktionen

Den simpleste del af validering er at kalde den på det rigtige tidspunkt. Et form-tag har attributten onsubmit som udføres inden formularen afsendes. Hvis man returnerer værdien false, så bliver formularen ikke afsendt.

Hvis vi laver en funktion der validerer formularen, og lader den returnere false hvis der er fejl, så kan vi blot skrive følgende:

<form action="..." onsubmit="return valideringsData.validate(this);">

Ved at sende this (der refererer til form-elementet selv) med som argument, så har funktionen nem adgang til formularen. Man slipper for at skrive document.forms['mitFormNavn'] for at få fat i den.

Variablen valideringsData peger på de data som funktionen validerer formularen i forhold til. I god objektorienteret skik er valideringsfunktionen en metode i dette objekt.

Selve funktionen

Her er en generel valideringsfunktion. I stedet for at skulle ind og ændre i funktionen hver gang man tilføjer eller fjerner input-elementer, så gemmer jeg information om formularen i et objekt.

<script type="text/javascript">
/* (C) 2003 Lasse R. Nielsen - lrn@infimum.dk 
 * The code may be used freely as long as this copyright notice is retained
 */
 
/* Class of validation data, see example for usage */ 
function FormValidator(dataArray) {
   this.dataArray = dataArray;
}
/* Class representing a running test */ 
FormValidator.prototype.Test = function(){
  this.errors = new Array();  // error messages
  this.elemOk = new Object(); // has an element had an error
  this.firstError = null;     // first element with an error
};
FormValidator.prototype.Test.prototype.reportError = 
  function (elem,message) {
    this.errors[this.errors.length] = message;
    this.firstError = this.firstError || elem;  
  };
FormValidator.prototype.Test.prototype.markError = 
  function markError(elem,ok) {
    if (elem.length && !elem.options) { // collection, e.g., radiobuttons
      for(var i=0; i < elem.length; i++) {
        markError(elem[i],ok);
      }  
      return;
    }
    if (!ok) {
      elem.style.border = "2px solid red";
    } else {
      elem.style.border = "";
    }
  };
FormValidator.prototype.Test.prototype.result = function(){
  if (this.errors.length > 0) {
    alert(this.errors.join("\n"));    
    if (this.firstError && this.firstError.focus) {
      this.firstError.focus();
    }
    return false;
  }
  return true;
};
 

/* the validation function itself */
FormValidator.prototype.validate = function validate(form) {
  var test = new this.Test();
  var data = this.dataArray;
  for (var i=0; i < data.length; i++) {   
    // data[i] == ["elemName",function isOk(){},"Default Error Message!"]
    // find element
    var name = data[i][0];
    var elem = form.elements[name];
    if (!elem) { // no form control with that name! Panic!
      continue;
    }
  
    // test element with provided function
    var ok = data[i][1](elem);
    // report error
    if (!ok) {
      test.reportError(elem,data[i][2]);
    }
    if (typeof ok == "string") { // Special error message
      test.reportError(elem,ok);
      ok = false;
    }
    
    // mark element if it has errors
    if (typeof test.elemOk[name] == "undefined") {
      test.elemOk[name] = ok;
    } else {
      ok = test.elemOk[name] = test.elemOk[elem.name] && ok;
    }
    test.markError(elem,ok);
  }
  return test.result();
}
</script>

Her er et eksempel på brugen af validerings-objektet:

<form action="..." onsubmit="return minValidering.validate(this);"> <label>Fornavn: <input type="text" name="name" /></label> <label>Alder: <input type="text" name="age" /></label> <label>Fødselsdag: <input type="text" name="birthday" /></label> <input type="submit" value="Submit" /> </form> <script type="text/javascript">
 var minValidering = new FormValidator(
  [["name",nonEmpty,"Fornavn skal udfyldes"],
   ["name",noSpace,"Fornavn må ikke indeholde mellemrum"],
   ["age",nonEmpty,"Alder skal udfyldes"],
   ["age",onlyDigits,"Alder skal være et tal"],
   ["birthday",isDate,"Fødselsdag skal være en dato i formatet YYYY-MM-DD"]
  ]);
 
 function nonEmpty(elem) {
    return elem.value != "";
 }

 function noSpace(elem) {
    return elem.value.indexOf(" ")==-1;
 }

 function onlyDigits(elem) {
    return /^\d*$/.test(elem.value);
 }

 function isDate(elem) { 
    if (!/^\d{4}-\d{2}-\d{2}$/.test(elem.value)) { return false; }
    var parts = elem.value.split("-");
    var day = new Date(+parts[0],parts[1]-1,+parts[2]);
    if (day.getDate() != +parts[2] || day.getMonth() != parts[1]-1) {
      return "Fødselsdatoen findes ikke. Formatet er YYYY-MM-DD.";
    }
    return true;
 }
</script>

For hver validering man vil lave, skal man tilføje et element til arrayet. En element er selv er array på formen:

["elementNavn",testfunktion,"Fejlbesked"]

Valideringsfunktionen tager elementet med det navn og kalder funktionen med det element som argument. Funktionen skal enten returnere true hvis elementets indhold godkendes, eller false hvis det ikke gør. Hvis indholdet ikke godkendes , så kan funktionen kan også vælge at returnere en streng med en speciel fejlbesked. Ellers bruges "Fejlbesked".

Man kan teste flere ting ved det samme element. Her tests både at fornavnet ikke er tomt og at det ikke indeholder mellemrum. De to fejl udelukker hinanden, men man kan også have to fejl der begge kan optræde samtidigt. Begge fejlbeskedder bliver så vist.

Testfunktioner

Til hver test skal man have en testfunktion. I de fleste tilfælde kan man nøjes med nogle få standard-funktioner. Her er nogle eksempler:

<script type="text/javascript">
 /* først en hjælpe-funktion */

 // funktion til at lave funktioner der tester med et regulært udtryk
 function makeRegExpTest(re) {
   return function(elem) {
     return re.test(elem.value);
   }
 }

 /* Testfunktionerne */

 // ikke tomt element, til text, textarea og password 
 function nonEmpty(elem) {
    return elem.value != "";
 }

 // ingen mellemrum 
 function noSpace(elem) {
    return elem.value.indexOf(" ")==-1;
 }

 // kun cifre 
 var onlyDigits = makeRegExpTest(/^\d*$/);

 // telefonnummer: cifre, mellemrum og bindestreger, mindst et. 
 var isPhoneNumber = makeRegExpTest(/^[- \d]+$/);

 // E-mail-adresse: noget@noget.noget 
 var isEMailAddress = makeRegExpTest(/.+@.+\..+/);

 // Gyldig dato i formatet YYYY-MM-DD 
 // udelukker fx 30. februar
 function isDate(elem) { 
    if (!/^\d{4}-\d{2}-\d{2}$/.test(elem.value)) { 
      return false; // format forkert
    }
    var parts = elem.value.split("-");
    var day = new Date(+parts[0],parts[1]-1,+parts[2]);
    if (day.getDate() != +parts[2]  || 
        day.getMonth() != parts[1]-1) {
      return "Datoen findes ikke. Formatet er YYYY-MM-DD.";
    }
    return true;
 }

 // Mindst en checkbox eller radiobutton valgt
 function isOneChecked(elem) {
   if (!elem.length) { // ikke checkbox group
     return elem.checked;
   }
   var numChecked = 0;
   for (var i = 0; i < elem.length; i++) {
     numChecked += elem[i].checked;
   }
   return (numChecked > 0);
 }

 // mindst en select-option valgt, til select-elementer med multiple eller size 2+
 function isSelected(elem) {
   return (elem.selectedIndex != -1);
 }
</script>

Lasse Reichstein Nielsen

Last modified: Tue Jan 13 01:19:06 Romance Standard Time 2004