web-dev-qa-db-fra.com

Retarder HTML5: pseudo-classe invalide jusqu'au premier événement

J'ai récemment découvert que le :invalid la pseudo-classe s'applique aux éléments de formulaire required dès le chargement de la page. Par exemple, si vous avez ce code:

<style>
input:invalid { background-color: pink; color: white; }
input:valid { background-color: white; color: black; }
</style>
…
<input name="foo" required />

Ensuite, votre page se chargera avec un élément d'entrée rose vide dessus. La validation intégrée à HTML5 est excellente, mais je ne pense pas que la plupart des utilisateurs s'attendent à ce que le formulaire soit validé avant d'avoir eu la possibilité de saisir des valeurs. Existe-t-il un moyen de retarder l'application de la pseudo-classe jusqu'au premier événement affectant cet élément (soumission de formulaire, flou, modification, tout ce qui est approprié)? Est-il possible de le faire sans JavaScript?

59
kojiro

http://www.alistapart.com/articles/forward-thinking-form-validation/

Puisque nous voulons seulement indiquer qu'un champ n'est pas valide une fois qu'il a le focus, nous utilisons la pseudo-classe focus pour déclencher le style invalide. (Naturellement, marquer tous les champs obligatoires comme invalides dès le départ serait un mauvais choix de conception.)

Suivant cette logique, votre code ressemblerait à quelque chose comme ça ...

<style>
    input:focus:required:invalid {background-color: pink; color: white;}
    input:required:valid {background-color: white; color: black; }
<style>

Créé un violon ici: http://jsfiddle.net/tbERP/

Comme vous le devinez, et comme vous le verrez au violon, cette technique ne montre le style de validation que lorsque l'élément a le focus. Dès que vous déplacez le focus, le style est supprimé, qu'il soit valide ou non. Pas idéal par tous les moyens.

35
Bart

Ce n'est pas possible en CSS pur, mais cela peut être fait avec JavaScript. Ceci est un exemple jQuery :

// use $.fn.one here to fire the event only once.
$(':required').one('blur keydown', function() {
  console.log('touched', this);
  $(this).addClass('touched');
});
/**
 * All required inputs initially are yellow.
 */
:required {
  background-color: lightyellow;
}

/**
 * If a required input has been touched and is valid, it should be white.
 */
.touched:required:valid {
  background-color: white;
}

/**
 * If a required input has been touched and is invalid, it should be pink.
 */
.touched:required:invalid {
  background-color: pink;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>
  <label>
    Name:
    <input type="text" required> *required
  </label>
</p>
<p>
  <label>Age:
    <input type="text">
  </label>
</p>
21
kzh

Ces réponses sont obsolètes. Vous pouvez maintenant le faire en recherchant une pseudo-classe d'espace réservé avec CSS.

input:not(:placeholder-shown):invalid {
    background-color: salmon;
}
form:invalid button {
    background-color: salmon;
    pointer-events: none;
}
<form>
    <input type="email" placeholder="[email protected]" required>
    <button type="submit">Submit</button>
</form>

Il commence par un arrière-plan normal et devient rose lorsque vous entrez votre adresse e-mail incomplète.

18
Carl

J'ai créé une petite cale pour gérer cela dans ma base de code. Je commence juste avec mon élément <form/> Ayant la propriété novalidate avec un attribut data-validate-on="blur". Cela guette le premier événement de ce type. De cette façon, vous pouvez toujours utiliser les sélecteurs css natifs :invalid Pour le style du formulaire.

$(function () {
    $('[data-validate-on]').each(function () {
        var $form = $(this);
        var event_name = $form.data('validate-on');

        $form.one(event_name, ':input', function (event) {
            $form.removeAttr('novalidate');
        });
    });
});
3
aghouseh

Mozilla s'occupe de cela avec leur propre : - moz-ui-invalid pseudoclasse qui ne s'applique qu'aux formulaires après leur interaction. MDN déconseille de l'utiliser en raison d'un manque de support. Cependant, vous pouvez le modifier pour Firefox.

Il y a une spécification de niveau 4 pour une spécification : user-invalid à l'horizon qui offrira un comportement similaire.

Désolé si cela n'aide pas, mais j'espère que cela offre un certain contexte.

3
Aaron Benjamin

Lorsque vous utilisez la validation de formulaire HTML5, essayez d'utiliser le navigateur pour détecter les soumissions/champs non valides, plutôt que de réinventer la roue.

Écoutez l'événement invalid pour ajouter une classe "invalide" à votre formulaire. Avec la classe 'invalide' ajoutée, vous pouvez aller en ville avec le style de votre formulaire en utilisant CSS3 :pseudo sélecteurs.

Par exemple:

// where myformid is the ID of your form
var myForm = document.forms.myformid;

var checkCustomValidity = function(field, msg) {
    if('setCustomValidity' in field) {
        field.setCustomValidity(msg);
    } else {
        field.validationMessage = msg;
    }
};

var validateForm = function() {

    // here, we're testing the field with an ID of 'name'
    checkCustomValidity(myForm.name, '');

    if(myForm.name.value.length < 4) {
        checkCustomValidity(
            // alerts fields error message response
            myForm.name, 'Please enter a valid Full Name, here.'
        );
    }
};

/* here, we are handling your question above, by adding an invalid
   class to the form if it returns invalid.  Below, you'll notice
   our attached listener for a form state of invalid */
var styleInvalidForm = function() {
    myForm.className = myForm.className += ' invalid';
}

myForm.addEventListener('input', validateForm, false);
myForm.addEventListener('keyup', validateForm, false);
myForm.addEventListener('invalid', styleInvalidForm, true);

Maintenant, stylisez simplement votre formulaire comme bon vous semble en fonction de la classe "invalide" que nous avons jointe.

Par exemple:

form.invalid input:invalid,
form.invalid textarea:invalid {
    background: rgba(255, 0, 0, .05);
    border-color: #ff6d6d;
    -webkit-box-shadow: 0 0 6px rgba(255, 0, 0, .35);
    box-shadow: 0 0 6px rgba(255, 0, 0, .35);
}
2
Jonathan Calvin

Il existe un événement html5 invalid qui se déclenche sur les éléments du formulaire avant que l'événement submit ne se produise pour chaque élément qui ne passe pas checkValidity. Vous pouvez utiliser cet événement pour appliquer une classe par exemple au formulaire environnant et afficher: les styles non valides uniquement après que cet événement se soit produit.

 $("form input, form select, form textarea").on("invalid", function() {
     $(this).closest('form').addClass('invalid');
 });

Votre CSS ressemblerait alors à ceci:

:invalid { box-shadow: none; }
.invalid input:invalid,
.invalid textarea:invalid,
.invalid select:invalid { border: 1px solid #A90909 !important; background-color: #EEC2C2; }

La première ligne supprime le style par défaut, de sorte que les éléments du formulaire semblent neutres lors du chargement de la page. Dès que l'événement invalide se déclenche (lorsqu'un utilisateur essaie de soumettre le formulaire), les éléments sont visiblement rendus invalides.

2
Liquinaut

Ceci est une version VanillaJS (pas de jQuery) de kzh 's answer

{
  let f = function() {
    this.classList.add('touched')
  }
  document
    .querySelectorAll('input')
    .forEach((e) => {
      e.addEventListener('blur', f, false)
      e.addEventListener('keydown', f, false)
    })
}
/**
 * All required inputs initially are yellow.
 */
:required {
  background-color: lightyellow;
}

/**
 * If a required input has been touched and is valid, it should be white.
 */
.touched:required:valid {
  background-color: white;
}

/**
 * If a required input has been touched and is invalid, it should be pink.
 */
.touched:required:invalid {
  background-color: pink;
}
<p><label>
  Name:
  <input type="text" required> *required
</label></p>
<p><label>Age:
  <input type="text">
</label></p>
2
yunzen

Vous pouvez faire en sorte que seuls les éléments qui ont une certaine classe sur eux et soient requis, sont roses. Ajoutez un gestionnaire d'événements à chaque élément requis qui ajoute cette classe lorsque vous quittez l'élément.

Quelque chose comme:

<style>
  input.touched:invalid { background-color: pink; color: white; }
  input.touched:valid { background-color: white; color: black; }
</style>
<script>
  document.addEventListener('DOMContentLoaded', function() {
    var required = document.querySelectorAll('input:required');
    for (var i = 0; i < required.length; ++i) {
      (function(elem) {
        function removeClass(name) {
          if (elem.classList) elem.classList.remove(name);
          else
            elem.className = elem.className.replace(
              RegExp('(^|\\s)\\s*' + name + '(?:\\s+|$)'),
              function (match, leading) {return leading;}
          );
        }

        function addClass(name) {
          removeClass(name);
          if (elem.classList) elem.classList.add(name);
          else elem.className += ' ' + name;
        }

        // If you require a class, and you use JS to add it, you end up
        // not showing pink at all if JS is disabled.
        // One workaround is to have the class on all your elements anyway,
        // and remove it when you set up proper validation.
        // The main problem with that is that without JS, you see what you're
        // already seeing, and stuff looks hideous.
        // Unfortunately, you kinda have to pick one or the other.


        // Let non-blank elements stay "touched", if they are already,
        // so other stuff can make the element :invalid if need be
        if (elem.value == '') addClass('touched');

        elem.addEventListener('blur', function() {
          addClass('touched');
        });

        // Oh, and when the form submits, they need to know about everything
        if (elem.form) {
          elem.form.addEventListener('submit', function() {
            addClass('touched');
          });
        };
      })(required[i]);
    }
  });
</script>

Et bien sûr, cela ne fonctionnera pas tel quel dans IE8 ou inférieur, car (a) DOMContentLoaded est relativement nouveau et n'était pas standard à la sortie d'IE8, (b) IE8 utilise attachEvent plutôt que la norme DOM addEventListener, et (c) IE8 ne se souciera pas de :required de toute façon, car il ne prend pas techniquement en charge HTML 5.

1
cHao

Un bon moyen est d'abstraire :invalid, :valid avec une classe CSS puis du JavaScript pour vérifier si le champ de saisie était focalisé ou non.

CSS:

input.dirty:invalid{ color: red; }
input.dirty:valid{ color: green; }

JS:

// Function to add class to target element
function makeDirty(e){
  e.target.classList.toggle('dirty');
}

// get form inputs
var inputs = document.forms[0].elements;

// bind events to all inputs
for(let input of inputs){
  input.addEventListener('invalid', makeDirty);
  input.addEventListener('blur', makeDirty);
  input.addEventListener('valid', makeDirty);
}

DÉMO

1
Rust

Je ne peux pas commenter, mais pour aller avec la réponse très utile de @ Carl concernant l'utilisation de: not (: placeholder-shown). Comme un autre commentaire l'a mentionné, cela montrera toujours l'état invalide si vous n'avez AUCUN espace réservé (comme certains modèles de formulaires l'exigent).

Pour résoudre ce problème, ajoutez simplement un espace réservé vide comme

<input type="text" name="username" placeholder=" " required>

Ensuite, votre CSS, quelque chose comme

:not(:placeholder-shown):invalid{ background-color: #ff000038; }

A travaillé pour moi!

0
Marcus Frasier

À la suite de agousehidée , vous pouvez avoir un peu de javascript pour savoir quand le bouton d'envoi a été ciblé, et faire apparaître la validation à ce moment-là.

Le javascript ajoutera une classe (par exemple. submit-focussed) dans le champ du formulaire lorsque le bouton d'envoi est focalisé ou cliqué, ce qui permet ensuite au CSS de styliser les entrées non valides.

Cela suit la meilleure pratique consistant à afficher les commentaires de validation une fois que l'utilisateur a terminé de remplir les champs, car selon la recherche, il n'y a aucun avantage supplémentaire à les afficher pendant le processus.

document
  .querySelector('input[type=submit]')
  .onfocus = function() {
    this
      .closest('form')
      .classList
      .add('submit-focussed');
  };
form.submit-focussed input:invalid {
  border: thin solid red;
}
<form>
  <label>Email <input type="email" required="" /></label>
  <input type="submit" />
</form>

alternative à jQuery

(function($) {
  $('input[type=submit]').on('focus', function() {
    $(this)
      .parent('form')
      .addClass('submit-focussed');
  });
})(jQuery); /* WordPress compatible */
0
robrecord

Voici ma méthode pour éviter que le style par défaut de toute entrée non focalisée soit invalide, il vous suffit d'ajouter une simple commande js onFocus pour laisser la page Web identifier focused et unfocused entrées, donc toutes les entrées n'apparaîtront pas dans le style invalide à la première place.

<style>
input.focused:required:invalid { background-color: pink; color: white; }
input:valid { background-color: white; color: black; }
</style>
…
<input name="foo" class="notfocused" onFocus="document.activeElement.className='focused';" required />

Essayez-le ci-dessous:

input.focused:required:invalid {
  background-color: pink;
  color: white;
}

input:required:valid {
  background-color: darkseagreen;
  color: black;
}
<label>At least 1 charater:</label><br />
<input type="text" name="foo" class="notfocused" onFocus="document.activeElement.className='focused';" required />
0
timo