Bien préparer un développement javascript avec jQuery

David Koss
WebProdDesign
Published in
6 min readApr 9, 2010

On me pose souvent des questions sur certaines bonnes-pratiques javascript assez récurrentes en partant de l’utilisation d’une librairie telle que jQuery. C’est pourquoi je profite d’une de ces questions posée récemment (merci Gilles) pour faire un article sur le sujet.

Donc voici les différentes étapes assez systématiques par lesquelles je passe quand je dois entamer un développement javascript dans une page.

1. Charger jQuery

Je passe rapidement sur cette étape, vous pouvez directement l’appeler depuis le serveur de jquery.com ou le copier dans le répertoire local du site.

<script type="text/javascript"
src="http://code.jquery.com/jquery-latest.pack.js"></script>

2. Créer un fichier javascript et le charger dans la page

On ne le redira jamais assez : ne mettez pas de balises <script> dans vos pages ! Préférez un fichier à part du genre monScript.js cela vous permettra de bien séparer les choses. Ici je pars du principe que vous avez un dossier js à la racine du site dans lequel vous mettez ce genre de fichier.

<script type="text/javascript" src="/js/monScript.js"></script>

3. Préparer le fichier javascript

A la longue, mes fichiers javascripts finissent par avoir toujours la même structure… En l’occurence, je commence toujours par écrire les quelques lignes suivantes

(function($){$(document).ready(function(){
// Méthodes exécutées au chargement du DOM
});
})(jQuery);a. Englober tout le code dans une fonction anonymeCe morceau de code peut déjà sembler complexe à certains, mais une fois décortiqué il se comprend très bien. Dans un premier temps, on crée ce que l'on appelle une fonction anonyme, c'est à dire une fonction qui n'a pas de nom :function(){ /* contenu de monScript.js */ }Le but va être de "ranger" tout le code du fichier à l'intérieur de cette fonction. L'avantage est énorme, car grâce au principe de champ d'application des variables et des objets en javascript, tout ce que nous allons faire dans cette fonction ne pourra pas venir perturber d'autres scripts éventuels chargés dans la même page. Par exemple, fini les fonctions avec des noms à rallonge pour être sûr qu'aucune autre n'aura le même nom.Le problème à ce stade, c'est qu'une fonction (anonyme ou pas) ne s'exécute pas d'elle-même, pour l'exécuter, il faut qu'elle soit appelée. Ainsi, dans le code suivant, la première ligne sert à déclarer la fonction, la deuxième à l'appeler :var toto = function() { alert("toto") };
// => Pour l'instant rien ne se passe...
toto();
// => Là ma page affiche l'alerte, car la fonction toto est exécutée

La subtilité avec une fonction anonyme c'est justement qu'elle n'a pas de nom ! Mais ce n'est en fait pas un problème, car en javascript vous pouvez considérer que n'importe quel bout de code entre parenthèse est un objet en soit (ou une variable pour faire simple). Du coup, on peut à la fois déclarer une fonction anonyme et l'appeler en mettant la fonction entre parenthèse, puis en juxtaposant des parenthèses vides "()" justes après, comme on le ferait avec le nom d'une fonction classique déclarée auparavant. Ce qui nous donne :
(function(){ /* contenu de monScript.js */ })()Pour bien comprendre, il faut réaliser que les lignes de codes suivantes sont totalement équivalentes :function tata() { alert("tata") }; tata();
// => Affiche "tata"
var titi = function() { alert("titi") }; titi();
// => Affiche "titi"
(function() { alert("toto") })();
// => Affiche "toto"
b. Garantir l'équivalence jQuery == $Au final on profite également du fait de passer par une fonction pour lui passer en paramètre l'objet jQuery. L'intérêt est de profiter du fait que lorsque on déclare une fonction, on déclare au passage le nom des variables internes qui vont correspondre au paramètres passés à la fonction. Par exemple dans le code suivant, je passe la variable globale monMessage à la fonction showMessage à l'intérieur de laquelle je manipule cette fois la variable "m" qui à l'avantage d'être beaucoup plus courte.function showMessage(m) {
alert(m);
};
var monMessage = "Coucou !";
showMessage(monMessage);
// => Affiche "Coucou !"

Par défaut, jQuery propose déjà une variable "$" qui est l'équivalent de la variable globale jQuery. Malheureusement on ne peut pas toujours s'y fier, car certaines librairies telles que Prototype ou Mootools peuvent venir surcharger elles aussi la variable $. Il est donc plus sûr de redéfinir cette variable nous-même.
Cette astuce a aussi l'avantage inverse, à savoir permettre d'utiliser jQuery en mode "no conflict". C'est à dire en lui demandant explicitement de ne pas utiliser la variable $ pour qu'elle soit disponible pour d'autres librairies (et donc éviter les conflits). Car après tout jQuery n'a pas le monopole du dollar ! ;-)4. Répartir tout le code dans des fonctions simples avec des noms explicitesIl est souvent tentant de commencer un développement (surtout les plus petits) en écrivant le code sans essayer de le structurer. Mais avec le temps on se rend compte que faire du code propre dès le départ est toujours payant.Pour cela j'essaie d'appliquer 2 grands principes :
  • Je crée des fonctions différentes pour chaque unitée fonctionnelle. Notamment il arrive souvent qu’un développement consiste en deux parties : l’opération en elle même exécutée par le navigateur et le branchement de cette opération sur un évènement (par exemple, le clic sur un lien ou un bouton). Dans ce cas, je sépare ces deux parties en deux fonctions distinctes. L’intérêt principal est bien entendu de pouvoir rappeler la première fonction dans différents cas (on factorise le code). On gagne aussi en clarté et en maintenabilité
  • Je nomme mes fonctions de manière explicite en respectant une nomenclature précise. Par exemple une fonction qui est appelée directement au chargement commence par “init”, si elle consiste à brancher une fonction sur un évènement elle précise “event” ainsi que l’élément sur lequel elle s’applique, etc.
En bonus, vous pouvez aussi décrire vos fonctions avec des commentaires, ça ne mange pas de pain... ;-)Tout cela nous donne un code qui peut ressembler à celui-ci :(function($){// Code exécuté au chargement du DOM
$(document).ready(function(){
initEventOnClickOnSaveButton();
});
/*
* Action lors du clic sur le bouton "sauvegarder"
*/
function initEventOnClickOnSaveButton() {
$("#saveButton").click(function(){
saveFormData($(this).closest("form"));
});
}
/*
* Sérialisation et sauvegarde des données d'un formulaire
* formElement via une requête POST en AJAX
*/
function saveFormData(formElement) {
var $form = $(form), data = $form.serialize();
$.post($form.attr("action"), data);
}
})(jQuery);5. Utiliser des champs cachés pour transmettre des données aux scriptsDans l'exemple précédant, je simule une fonction qui doit faire une requête AJAX (via la méthode post de jQuery). Pour déduire l'URL à interroger pour faire la requête, elle utilise la valeur de l'attribut action d'un formulaire. C'est une technique assez pratique et élégante dans la mesure où les formulaires contiennent par nature toutes les informations nécessaires pour envoyer une requête à un serveur, y compris l'URL à interroger. Le problème c'est qu'on a bien sûr pas toujours la possibilité de rencontrer ce cas de figure idéal. Bien souvent, quand on doit faire une requête AJAX, l'URL à interroger doit être générée par une méthode côté serveur (une méthode PHP par exemple).Pour répondre à cette problématique, un réflexe courant est de générer dynamiquement du code javascript dans la page, comme dans l'exemple suivant :<script type="text/javascript">// On suppose qu'il existe déjà une variable PHP $ajaxUrl...
var ajaxUrl = "<?php echo $ajaxUrl ?>";
// => On peut maintenant utiliser la variable globale ajaxUrl en javascript
</script>
Cette façon de faire a plusieurs inconvénients, dont certains vont à l'encontre de quelques unes des bonnes pratiques que nous avons vu juste avant :
  • Génération d’une balise <script> dans la page
  • Utilisation d’une variable globale qui peut entrer en conflit avec d’autres scripts
La méthode que j'utilise pour pallier à ces problèmes est de passer par des champs cachés. La mise en oeuvre est assez simple. Je commence par mettre le code suivant tout en bas de la page concernée (ou plutôt dans le footer global du site si possible, comme ça les données sont dispo partout) :<form id="dataForJS">
<input type="hidden" id="ajaxUrl" value="<?php echo $ajaxUrl ?>" />
</form>
Et voilà ! En javascript, il ne me reste plus qu'à aller chercher le champ caché par son identifiant et en récupérer la valeur. Ce qui, avec jQuery, est assez trivial :var ajaxUrl = $("#ajaxUrl").val();Vous pouvez même un peu plus automatiser les choses en récupérant directement toutes les URLs s'il y en a plusieurs. En ajouter une revient alors simplement à ajouter un champ caché :var urls = {};
$("#dataForJs input").each(function(){
var $input = $(this);
urls[$input.attr("id")] = $input.val();
});
alert(urls["ajaxUrl"]);
// => Affiche l'URL stockée dans le champs caché avec l'id "ajaxUrl"
En résumé...Voilà donc au final à quoi peut ressembler votre fichier JS prêt pour accueillir confortablement vos développements javascript avec jQuery :(function($){// Code exécuté au chargement du DOM
$(document).ready(function(){
initUrls();
initEventOnClickOnSaveButton();
});
/*
* Récupération des URLs sérialisées dans la page
*/
var urls = {};
function initUrls() {
$("#dataForJs input").each(function(){
var $input = $(this);
urls[$input.attr("id")] = $input.val();
});
}
/*
* Action lors du clic sur le bouton "sauvegarder"
*/
function initEventOnClickOnSaveButton() {
$("#saveButton").click(function(){
saveFormData($(this).closest("form"));
});
}
/*
* Sérialisation et sauvegarde des données d'un formulaire
* formElement via une requête POST en AJAX
*/
function saveFormData(formElement) {
var $form = $(form), data = $form.serialize();
$.post(urls["ajaxUrl"], data);
}
})(jQuery);

Et vous ? Avez-vous d'autres bonnes pratiques ou des conseils pour préparer vos développements frontend ? N'hésitez pas à les poster en commentaire, je compléterai ou corrigerai l'article au besoin...

--

--