4 astuces pour optimiser vos formulaires Gravity Forms
Mis à jour le :
Gravity Forms (lien affilié) est une excellente extension de création de formulaires pour WordPress. Elle dispose de nombreux hooks afin de la personnaliser dans ses moindres détails.
Depuis la première version de cet article, deux de ces astuces sont devenues inutiles : Gravity Forms les applique désormais lui-même. Les deux autres restent utiles si votre thème s’appuie sur Bootstrap, aujourd’hui en version 5. Le réglage Utiliser le code HTML5 a lui aussi disparu des réglages de l’extension avec la version 2.8.
Les astuces présentées ci-dessous peuvent être injectées dans le fichier functions.php de votre thème ou dans une extension de type mu-plugin.
Astuce n°1 – Charger les scripts dans le pied de page
Depuis Gravity Forms 2.5, les scripts d’initialisation des formulaires sont placés par défaut dans le pied de page, sauf pour un formulaire chargé en Ajax (documentation du filtre gform_init_scripts_footer). L’extension attend en plus le chargement de la page et de ses propres fichiers avant d’exécuter ce code, avec sa fonction gform.initializeOnLoaded().
Le filtre gform_init_scripts_footer et l’enveloppe DOMContentLoaded que je proposais ici avec les filtres gform_cdata_open et gform_cdata_close n’ont donc plus lieu d’être : retirez-les de votre thème ou de votre mu-plugin. Pour retarder le reste du JavaScript de vos pages, voyez plutôt mon guide de configuration de WP Rocket.
Astuce n°2 – Utiliser un button pour le bouton d’envoi
Depuis Gravity Forms 3.0, les boutons Envoyer, Suivant et Précédent sont des éléments <button> et non plus des <input> : c’est désormais le comportement par défaut de l’extension.
Le code que je proposais pour faire cette conversion est devenu dangereux : il cherche une balise <input> qui n’existe plus et provoque une erreur fatale (« Call to a member function getAttribute() on null ») qui bloque l’affichage de la page. Je l’ai vérifié avec Gravity Forms 3.1.2 et PHP 8.4. Si vous l’aviez repris, supprimez-le avant de mettre à jour l’extension.
Astuce n°3 – Ajouter les classes CSS de Bootstrap 5
Si votre thème sur mesure repose sur Bootstrap, vos formulaires peuvent en reprendre les styles. Les hooks de Gravity Forms le permettent sans toucher aux fichiers de l’extension.
Commencez par couper le CSS de Gravity Forms : depuis la version 2.8, le réglage Sortie CSS par défaut a disparu de Formulaires > Réglages et laisse place au filtre gform_disable_css. Le code ci-dessous l’active, puis ajoute les classes btn btn-primary text-uppercase aux boutons Envoyer et Suivant, et btn btn-outline-secondary au bouton Précédent.
/**
* Stop Gravity Forms from loading its own CSS: the theme and Bootstrap style the forms.
*/
add_filter( 'gform_disable_css', '__return_true' );
/**
* Add CSS classes to the first button (or input) found in a button markup.
*/
function wpc_gf_add_button_classes( string $button, array $classes ): string {
$processor = new WP_HTML_Tag_Processor( $button );
while ( $processor->next_tag() ) {
if ( in_array( $processor->get_tag(), array( 'BUTTON', 'INPUT' ), true ) ) {
foreach ( $classes as $class ) {
$processor->add_class( $class );
}
break;
}
}
return $processor->get_updated_html();
}
/**
* Submit and next buttons: primary Bootstrap button.
*/
function wpc_gf_primary_button( $button, $form ) {
return wpc_gf_add_button_classes( (string) $button, array( 'btn', 'btn-primary', 'text-uppercase' ) );
}
add_filter( 'gform_submit_button', 'wpc_gf_primary_button', 10, 2 );
add_filter( 'gform_next_button', 'wpc_gf_primary_button', 10, 2 );
/**
* Previous button: secondary Bootstrap button.
*/
function wpc_gf_secondary_button( $button, $form ) {
return wpc_gf_add_button_classes( (string) $button, array( 'btn', 'btn-outline-secondary' ) );
}
add_filter( 'gform_previous_button', 'wpc_gf_secondary_button', 10, 2 );
Pour les champs, le filtre gform_field_content ajoute form-control aux zones de saisie, form-select aux listes déroulantes, form-check-input et form-check-label aux cases à cocher et boutons radio, ainsi qu’une marge entre les champs.
/**
* Field containers: Bootstrap bottom margin between fields.
*/
function wpc_gf_bootstrap_field_container( $classes, $field, $form ) {
return trim( $classes . ' mb-3' );
}
add_filter( 'gform_field_css_class', 'wpc_gf_bootstrap_field_container', 10, 3 );
/**
* Fields: Bootstrap 5 form classes, error state and hidden labels.
*/
function wpc_gf_bootstrap_fields( $content, $field, $value, $entry_id, $form_id ) {
// Leave the form editor and the entry screens untouched.
if ( is_admin() && ! wp_doing_ajax() ) {
return $content;
}
$processor = new WP_HTML_Tag_Processor( (string) $content );
while ( $processor->next_tag() ) {
$tag = $processor->get_tag();
$type = strtolower( (string) $processor->get_attribute( 'type' ) );
if ( 'INPUT' === $tag && in_array( $type, array( 'checkbox', 'radio' ), true ) ) {
$processor->add_class( 'form-check-input' );
} elseif ( 'INPUT' === $tag && ! in_array( $type, array( 'hidden', 'submit', 'button', 'image' ), true ) ) {
$processor->add_class( 'form-control' );
} elseif ( 'TEXTAREA' === $tag ) {
$processor->add_class( 'form-control' );
} elseif ( 'SELECT' === $tag ) {
$processor->add_class( 'form-select' );
}
// Field label (label or legend): Bootstrap label, visually hidden when the field hides it.
if ( $processor->has_class( 'gfield_label' ) ) {
$processor->add_class( 'form-label' );
if ( 'LEGEND' === $tag ) {
$processor->add_class( 'fs-6' );
}
if ( 'hidden_label' === $field->labelPlacement ) {
$processor->add_class( 'visually-hidden' );
}
} elseif ( 'LABEL' === $tag && $processor->has_class( 'gform-field-label--type-inline' ) ) {
$processor->add_class( 'form-check-label' );
}
// Required indicator next to the label.
if ( $processor->has_class( 'gfield_required' ) && ! $processor->has_class( 'gfield_required_text' ) ) {
$processor->add_class( 'ms-1' );
$processor->add_class( 'text-danger' );
}
// Hidden sub-labels stay readable by screen readers.
if ( $processor->has_class( 'screen-reader-text' ) ) {
$processor->add_class( 'visually-hidden' );
}
// Error state of the field and its message.
if ( 'true' === $processor->get_attribute( 'aria-invalid' ) ) {
$processor->add_class( 'is-invalid' );
}
if ( $processor->has_class( 'gfield_validation_message' ) ) {
$processor->add_class( 'invalid-feedback' );
$processor->add_class( 'd-block' );
}
}
return $processor->get_updated_html();
}
add_filter( 'gform_field_content', 'wpc_gf_bootstrap_fields', 10, 5 );
Une fois le CSS de Gravity Forms coupé, un libellé que vous avez masqué dans l’éditeur de formulaires redeviendrait visible : le code lui ajoute la classe visually-hidden de Bootstrap, qui le cache à l’écran tout en le laissant lisible par les lecteurs d’écran. Voyez aussi comment masquer les labels des champs de Gravity Forms.
Astuce n°4 – Afficher les messages d’erreurs comme des alertes Bootstrap 5
La gestion des erreurs dans un formulaire est un élément fondamental dans l’expérience utilisateur. Les messages doivent être clairs et accessibles.
Depuis Gravity Forms 2.5, le message affiché en haut du formulaire est placé dans un bloc gform_validation_errors, que le filtre gform_form_validation_errors_markup permet de modifier. Le code lui ajoute les classes alert alert-danger et garde le texte de Gravity Forms, déjà traduit, ainsi que la liste des erreurs quand elle est affichée. Dans chaque champ, la zone en erreur reçoit is-invalid et son message invalid-feedback (voir le code de l’astuce n°3).
/**
* Error summary at the top of the form: Bootstrap danger alert.
*/
function wpc_gf_bootstrap_validation( $markup, $form ) {
$processor = new WP_HTML_Tag_Processor( (string) $markup );
while ( $processor->next_tag() ) {
if ( $processor->has_class( 'gform_validation_errors' ) ) {
$processor->add_class( 'alert' );
$processor->add_class( 'alert-danger' );
} elseif ( $processor->has_class( 'gform_submission_error' ) ) {
$processor->add_class( 'h6' );
} elseif ( 'OL' === $processor->get_tag() ) {
$processor->add_class( 'mb-0' );
} elseif ( $processor->has_class( 'gform_validation_error_link' ) ) {
$processor->add_class( 'alert-link' );
}
}
return $processor->get_updated_html();
}
add_filter( 'gform_form_validation_errors_markup', 'wpc_gf_bootstrap_validation', 10, 2 );
Un formulaire de démonstration envoyé à vide, avec Bootstrap 5 et le code de cet article
Code final
Voici le code des astuces n°3 et n°4 regroupé dans un fichier de type mu-plugin, à téléverser dans /wp-content/mu-plugins/ de votre installation WordPress. Il a été testé avec Gravity Forms 3.1.2, WordPress 7.1.2 et PHP 8.4 ; votre thème doit charger le CSS de Bootstrap 5.
<?php
/**
* Plugin Name: WPC Gravity Forms Bootstrap
* Description: Bootstrap 5 classes for Gravity Forms buttons, fields and error messages.
*/
defined( 'ABSPATH' ) || exit;
/**
* Stop Gravity Forms from loading its own CSS: the theme and Bootstrap style the forms.
*/
add_filter( 'gform_disable_css', '__return_true' );
/**
* Add CSS classes to the first button (or input) found in a button markup.
*/
function wpc_gf_add_button_classes( string $button, array $classes ): string {
$processor = new WP_HTML_Tag_Processor( $button );
while ( $processor->next_tag() ) {
if ( in_array( $processor->get_tag(), array( 'BUTTON', 'INPUT' ), true ) ) {
foreach ( $classes as $class ) {
$processor->add_class( $class );
}
break;
}
}
return $processor->get_updated_html();
}
/**
* Submit and next buttons: primary Bootstrap button.
*/
function wpc_gf_primary_button( $button, $form ) {
return wpc_gf_add_button_classes( (string) $button, array( 'btn', 'btn-primary', 'text-uppercase' ) );
}
add_filter( 'gform_submit_button', 'wpc_gf_primary_button', 10, 2 );
add_filter( 'gform_next_button', 'wpc_gf_primary_button', 10, 2 );
/**
* Previous button: secondary Bootstrap button.
*/
function wpc_gf_secondary_button( $button, $form ) {
return wpc_gf_add_button_classes( (string) $button, array( 'btn', 'btn-outline-secondary' ) );
}
add_filter( 'gform_previous_button', 'wpc_gf_secondary_button', 10, 2 );
/**
* Field containers: Bootstrap bottom margin between fields.
*/
function wpc_gf_bootstrap_field_container( $classes, $field, $form ) {
return trim( $classes . ' mb-3' );
}
add_filter( 'gform_field_css_class', 'wpc_gf_bootstrap_field_container', 10, 3 );
/**
* Fields: Bootstrap 5 form classes, error state and hidden labels.
*/
function wpc_gf_bootstrap_fields( $content, $field, $value, $entry_id, $form_id ) {
// Leave the form editor and the entry screens untouched.
if ( is_admin() && ! wp_doing_ajax() ) {
return $content;
}
$processor = new WP_HTML_Tag_Processor( (string) $content );
while ( $processor->next_tag() ) {
$tag = $processor->get_tag();
$type = strtolower( (string) $processor->get_attribute( 'type' ) );
if ( 'INPUT' === $tag && in_array( $type, array( 'checkbox', 'radio' ), true ) ) {
$processor->add_class( 'form-check-input' );
} elseif ( 'INPUT' === $tag && ! in_array( $type, array( 'hidden', 'submit', 'button', 'image' ), true ) ) {
$processor->add_class( 'form-control' );
} elseif ( 'TEXTAREA' === $tag ) {
$processor->add_class( 'form-control' );
} elseif ( 'SELECT' === $tag ) {
$processor->add_class( 'form-select' );
}
// Field label (label or legend): Bootstrap label, visually hidden when the field hides it.
if ( $processor->has_class( 'gfield_label' ) ) {
$processor->add_class( 'form-label' );
if ( 'LEGEND' === $tag ) {
$processor->add_class( 'fs-6' );
}
if ( 'hidden_label' === $field->labelPlacement ) {
$processor->add_class( 'visually-hidden' );
}
} elseif ( 'LABEL' === $tag && $processor->has_class( 'gform-field-label--type-inline' ) ) {
$processor->add_class( 'form-check-label' );
}
// Required indicator next to the label.
if ( $processor->has_class( 'gfield_required' ) && ! $processor->has_class( 'gfield_required_text' ) ) {
$processor->add_class( 'ms-1' );
$processor->add_class( 'text-danger' );
}
// Hidden sub-labels stay readable by screen readers.
if ( $processor->has_class( 'screen-reader-text' ) ) {
$processor->add_class( 'visually-hidden' );
}
// Error state of the field and its message.
if ( 'true' === $processor->get_attribute( 'aria-invalid' ) ) {
$processor->add_class( 'is-invalid' );
}
if ( $processor->has_class( 'gfield_validation_message' ) ) {
$processor->add_class( 'invalid-feedback' );
$processor->add_class( 'd-block' );
}
}
return $processor->get_updated_html();
}
add_filter( 'gform_field_content', 'wpc_gf_bootstrap_fields', 10, 5 );
/**
* Error summary at the top of the form: Bootstrap danger alert.
*/
function wpc_gf_bootstrap_validation( $markup, $form ) {
$processor = new WP_HTML_Tag_Processor( (string) $markup );
while ( $processor->next_tag() ) {
if ( $processor->has_class( 'gform_validation_errors' ) ) {
$processor->add_class( 'alert' );
$processor->add_class( 'alert-danger' );
} elseif ( $processor->has_class( 'gform_submission_error' ) ) {
$processor->add_class( 'h6' );
} elseif ( 'OL' === $processor->get_tag() ) {
$processor->add_class( 'mb-0' );
} elseif ( $processor->has_class( 'gform_validation_error_link' ) ) {
$processor->add_class( 'alert-link' );
}
}
return $processor->get_updated_html();
}
add_filter( 'gform_form_validation_errors_markup', 'wpc_gf_bootstrap_validation', 10, 2 );
Gravity Forms va bien au-delà du simple formulaire de contact. Je l’utilise aussi pour de la prise de rendez-vous, de la personnalisation de produits WooCommerce ou de la création de compte utilisateur, par exemple pour injecter une liste de produits WooCommerce dans un formulaire. Les possibilités sont nombreuses et la documentation technique de qualité.
L’outil de personnalisation ou customizer de WordPress met à disposition des hooks afin de le personnaliser. Un cas classique consiste à ajouter un panneau supplémentaire avec des champs comme l’ajout d’un logo.
4 réponses
Attention avec l’ajax cela peut avoir des conséquences sur la validation du formulaire
Sur un thème bien fait, en principe non. 😉
Je suis passé à côté de cet article! Aurélien, Merci pour ces astuces qui embellissent et optimisent grandement GF !
Le top!
Carrément ! Et si tu couples cela avec un thème sur-mesure et du SCSS tu peux même désactiver les CSS de l’extension. En place sur ce site ! 😉