Différer le chargement des scripts Contact Form 7 et Recaptcha

Dynamic loading, un paysage de mi-montagne et le logo CF7 sur le repo WordPress Plugins

Contact Form 7 est un plugin WordPress qui gère des formulaires de contact multiples, la personnalisation des contenus des formulaires et des e-mails avec des balises simples. Le formulaire supporte l’envoi par Ajax, le ReCaptcha V3 de Google, l’anti Spam Akismet de WordPress.

Chargement des scripts

Par défaut, le plugin charge les scripts nécessaires sur toutes les pages du blog. L’idée est de charger ces fichiers seulement lorsque l’on en a besoin, c’est-à-dire sur les pages qui utilise le shortcode [contact-form-7]. Les scripts en question sont :

  • /contact-form-7/includes/js/scripts.js, 14.3kB, chargé en 153ms,
  • https://www.google.com/recaptcha/api.js?render=SITE_KEY&ver=3.0, 1.1kB, chargé en 152ms,
  • https://www.gstatic.com/recaptcha/releases/SITE_KEY/recaptcha__fr.js, 137kB, chargé en 938ms,
  • /contact-form-7/modules/recaptcha/script.js, 1.4kB, chargé en 152ms,
  • Et bizarre, https://www.gstatic.com/recaptcha/releases/SITE_KEY/recaptcha__fr.js est chargé une deuxième fois en 641ms. Sans doute l’appel à api.js qui fait cela.
Extrait de la console sur le chargement des scripts, en particulier recaptcha_fr.js qui charge deux fois
Chargement du premier recaptcha_fr.js, timeline de chargement sur 941.35ms

Performances

Le serveur n’est pas très rapide, la connexion non plus. La taille des fichiers chargés n’est pas démente, mais si l’on additionne tous les temps de chargement, cela fait pas mal : 153 + 152 + 938 + 152 + 641 = 2036 ms, soit 2 secondes, sur n’importe quelle page du site. Si l’on peut réduire ce temps, voir l’annuler, cela aura un grand impact sur la performance, donc le référencement et pour l’expérience utilisateur.

Sans compter le chargement des styles /contact-form-7/includes/css/styles.css?ver=5.2.2, 2.0kB chargé en 726ms, il serait bien de ne le charger que lorsque l’on en a besoin.

Chargement sélectif

Si l’on dispose d’une page de contact dédiée à l’envoi de mails par les visiteurs, on peut choisir de charger les scripts Contact Form 7 et Recaptcha seulement sur cette page.

On utilise le hook wp_enqueue_scripts avec une priorité supérieure à la priorité par défaut de 10 (prenons 12) pour enlever de la file de chargement les scripts et le style en question. On suppose que le slug de la page contact est contact. Si vous avez appelé cette page Contact, il y a de grandes chances que l’identifiant défini par défaut par WordPress soit celui-là.

Si le script Recaptcha n’est pas chargé, au cas où l’on n’a pas renseigné les clés publique et privée de Google Recaptcha dans l’intégration de Contact Form 7, le fait de le retirer de la file n’aura aucun effet et ne produira aucune erreur.

Ne pas charger les scripts au début

Nous allons mettre en place une technique avancée afin de ne charger les fichiers scripts de Contact Form 7 et reCAPTCHA de Google seulement lorsque l’on en a besoin.

Technique

J’ai mis sur le site des Clionautes un lien de Contact dans le footer. Lorsque l’utilisateur clique sur ce lien, une fenêtre modale s’ouvre, l’utilisateur remplit le formulaire et le mail s’envoie en Ajax. La technique envisagée consiste à ne charger les scripts nécessaires que lorsque l’utilisateur clique sur ce lien.

Lien Contact dans le footer

Le lien est défini en php et a la forme suivante, sachant que le shortcode de contact est [contact-form-7 id="802" title="Footer Contact Form"] :

Le shortcode se trouve dans le footer, son id est clio-contact-form, n’est pas visible style="display: none", et est bien présent sur toutes les pages.

Retirer les scripts et définition des paramètres

On enlève dequeue les scripts Contact Form 7 et Recaptcha de la file d’attente. On redéfinit les variables wpcf7 (contact-form-7/includes/controller.php ligne 42) et wpcf7_recaptcha (contact-form-7\modules\recaptcha\recaptcha.php ligne 19) de la version 5.2.2 de Contact Form 7. On ajoute les clés scriptUrl, styleUrl et recaptchaUrl aux tableaux des variables, pour retrouver le chemin des scripts en javascript. Puisque nous les enlevons de la file d’attente au départ, il faudra les charger dynamiquement en javascript. Le code des deux variables est tiré directement des deux fichiers du plugin Contact Form 7. Le fichier frontend.js est un script personnalisé, qui a le handle clio-frontend-js.

Le fichier javascript

Le fichier frontend.js est un script personnalisé, qui a le handle clio-frontend-js, utilisé pour quelques opérations sur le site public. Si vous n’utlisez aucun script personnalisé, vous créez un nouveau fichier javascript frontend.js, dans votre répertoire de fichiers js, que vous chargez sur le frontend avec le wp_enqueue_script. Il faut connaître bien évidemment son url : /url/to/js/frontend.js, que vous devez retrouver dans votre plugin ou thème enfant.

Chargement de frontend.js

Dans le constructeur de la classe MyPlugin_Frontend, on ajoute :

On définit le callback scripts, /url/to/js/frontend.js est le chemin pour trouver le script, VERSION est la version du script :

On ajoute la dépendance ['jquery'] si l’on en a besoin.

Structure de frontend.js

Ce fichier a la structure suivante, on définit les deux fonctions enqueueCf7() et enqueueRecaptcha() :

On définit deux fonctions, l’une enqueueCf7() pour le chargement du script de base Contact Form 7, l’autre enqueueRecaptcha() pour le chargement des scripts Recaptcha. On écrit des balises link et script en javascript que l’on insère dans le DOM à la fin du head ou du body, on ne recourt pas à jQuery pour cela. Il faut toutefois charger le script frontend.js dans le footer, pour que le DOM soit défini, cela se fait en ajoutant true dans wp_register_script.

Chargement différé

Dans mon cas, le lien de contact est dans le footer et ouvre une fenêtre modale pour écrire et envoyer le mail. Ce lien existe donc sur toutes les pages. On ne charge les fichiers javascript Contact Form 7 et Recaptcha seulement lorsque le lien est cliqué.

Listener sur le click et chargement différé

En reprenant les deux fonctions enqueueCf7() et enqueueRecaptcha(), le fichier frontend.js se complète ainsi :

Lorsque l’utilisateur clique sur le lien, les deux scripts Contact Form 7 et Recaptcha sont chargés dynamiquement.

Test du chargement différé

On lance alors une page du site et l’on clique sur le lien pour envoyer un email. Le style et le script Contact Form 7 se chargent, mais le script renvoie une erreur dans la console :

Console error message : Uncaught TypeError: wpcf7.initForm is not a function (scripts.js:33)

Cette erreur vient directement du fichier /contact-form-7/includes/js/scripts.js. Dans ce fichier, une fonction s’exécute dès le chargement en appelant des fonctions qui sont définies après. À la ligne 31, on lit :

Les deux fonctions initForm() et refill() de l’objet wpcf7 sont définies après dans le code. Et Javascript n’aime pas du tout cela, les fonctions n’étant pas définies avec l’instruction function. De toute façon, elles ne pourraient pas l’être car elles font partie de l’objet wpcf7. Pour bien faire, il faudrait repousser le code de la fonction qui s’auto-exécute après la définition des deux fonctions initForm() et refill(). Le mieux étant de déplacer ce code à la fin, car ces fonctions utilisent elles-mêmes d’autres fonctions.

Avant d’envoyer une requête dans le dépot de Contact Form 7 sur github.com, je copie le fichier /contact-form-7/includes/js/scripts.js, dans le répertoire js de mon plugin et je modifie ce fichier en déplaçant le bout de code ligne 14, la fonction js qui s’auto-exécute en utilisant wpcf7.initForm() et wpcf7.refill().

La version du plugin Contact Form 7 est la 5.2.2.

On déplace ce bout de code à la fin des définitions des fonctions de l’objet wpcf7 à la ligne 506, ou 480 si l’on a effacé la fonction auto-exécutable, avant la fermeture de la fonction englobante jQuery : } )( jQuery ); version 5.2.2.

Il ne reste plus qu’à changer la clé ScriptUrl dans la classe MyPlugin_Frontend. /url/to/js/frontend.js est le chemin pour trouver le script dans votre plugin.

Github issue

J’ai donc ouvert une issue sur github : Dynamic loading of includes/js/scripts.js #151

Github issue #151 message à propos du problème dans /includes/js/scripts.js après le chargement dynamique.

I wanted to test the way to dynamically load the Contact Form 7 and Recaptcha scripts to improve performance, because I have a link and a hidden shortcode in the footer therefore present on all the pages. The dynamic loading is done fine, but I have a javascript error in includes/js/scripts.js sent from line 33: wpcf7.initForm is not a function.
This is because wpcf7.initform and other functions are defined after and javacript doesn’t like that. I solved the problem, by copying the script into my plugin and moving the self-executing function block $(function () {...}); from line 14 to the end of the jQuery block on line 506. There are then no more errors, the functions have been defined and loaded.
With this change, we avoid this kind of inconvenience and can load the scripts dynamically by triggering the DOMContentLoaded event again on the document.

Bon, pas de réponses, juste cette issue a été considérée comme invalid, sans aucune autre forme de procès, et l’issue a disparue. J’aurai bien aimé une explication plus poussée. Ce javascript est bien mal fait, puis javascript est assez clair, les fonctions doivent être définies avant de les utiliser. Je garde mon texte, que je soumettrais à nouveau.

The github issue closed and invalid without explanation

Test après les modifications

Test sans Recaptcha

Les fichiers https://domain.tld/wp-content/plugins/my-plugin/assets/js/scripts.js, https://domain.tld/wp-content/plugins/contact-form-7/includes/css/styles.css sont bien chargés dynamiquement lorsque l’on clique sur le lien du footer. Il n’y a plus d’erreur dans la console.

Réponse Contact Form 7 après l'envoi du mail par Ajax : Merci pour votre message, il a été envoyé.

Test avec Recaptcha

On entre les bonnes clés publique et privée dans les options Intégration pour utiliser Recaptcha v3. Le fichier /contact-form-7/modules/recaptcha/script.js est chargé en 106ms. On teste à nouveau, il se produit une erreur, la soumission est qualifiée de spam.

Echec de l'envoi avec Contact Form 7. La soumission est qualifiée de SPAM.

Donc, il y a un souci. En regardant le fichier /contact-form-7/modules/recaptcha/script.js, on remarque qu’il déclenche des actions sur l’événement DOMContentLoaded. Comme le document a déjà été chargé, l’événement ne se produit pas. Il va donc falloir le déclencher une nouvelle fois après le chargement des scripts.

On va donc envoyer cet événement lorsque le script /contact-form-7/modules/recaptcha/script.js aura été chargé.

Déclenchement de DOMContentLoaded

On ajoute le déclenchement de DOMContentLoaded sur document à la fin de la fonction enqueueRecaptcha() en écoutant la fin du chargement du script qui envoie l’événement load dans frontend.js :

Et l’on teste de nouveau :

Succès de l'envoi avec Contact Form 7. Le Recaptcha est activé et chargé dynamiquement.

Et cela fonctionne enfin, le mail est envoyé et reçu par le destinataire :

Copie de l'email reçu par le destinataire, envoyé par Contact Form 7, Recaptcha chargé dynamiquement.

Gain de performance

Sans ce chargement dynamique, sur toutes les pages, on a vu que rien que le chargement des scripts relatifs à Contact Form 7 prenait à peu près de 2 secondes, bien que je pense que certaines choses puissent se faire en parallèle.

Les mesures de la perfomances sur la page d’accueil qui est certainement la plus lourde semblent montrer que l’on gagne sensiblement 1 seconde. Le cache navigateur est vidé à chaque fois, le mode est incognito, non connecté. Les mesures se font sur le site de développement, hebergé par un serveur très lent :

  • Sans chargement dynamique : 6.41 secondes (ce qui est déjà énorme)
  • Avec chargement dynamique : 5.48 secondes

Le temps de chargement est très variable, il faudrait faire les tests en parallèles, avec une connexion identique qui ne varie pas non plus, mais il semble que l’on gagne assez bien en performance.

Et hop, cela fait pas mal de modifications, mais cela fonctionne nickel. Ne reste plus qu’à surveiller les mises à jour du script /contact-form-7/includes/js/scripts.js, pour ne pas le copier dans notre plugin et le modifier. Il faudra vérifier les nouvelles versions de Contact Form 7 et modifier notre classe MyPlugin_Frontend en conséquence.

Soumettre un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables.