User Tools

Site Tools


normesprogrammation:normesjavascript

Normes de code - xhtml 1.0, css 2.1, javascript 1.6, prototype 1.6.0

Ce document est conçu pour être cohérent avec les normes utilisées en PERL (cf normesperl). Il s'applique à tout projet qui présente les caractéristiques suivantes:

  • Site web, ou programme avec interface utilisateur en html
  • Utilisation relativement importante de javascript, pour améliorer l'interactivité du site

Les normes décrites ici s'appliquent à l'utilisation de javascript version 1.5 (environ, cf. MSIE), avec prototype version 1.6, xhtml version 1.0 strict et CSS version 2

La norme xhtml

Le xhtml est une “reformulation” conforme à XML du langage html. En gros, on peut dire que les caractéristiques de xhtml par rapport à html sont les suivantes:

  • Une page xhtml doit commencer par une en-tête donnant accès à la DTD (voir ci-dessous).
  • Toute balise ouverte doit être fermée, certaines balises étant autofermantes. Dans le cas d'une balise autofermante, le caractère /> doit être précédé d'un espace (ce n'est pas à proprement parler dans la norme, mais c'est une recommandation importante par rapport aux navigateurs un peu anciens).
    <p>première ligne<br />secondeligne</p>
  • Toute valeur d'attribut doit figurer entre guillemets:
    <div id="ID_TITRE">...</div>
  • La DTD spécifie de manière très précise quelle balise peut être mise à l'intérieur de quelle autre.
  • Presque toutes les balises ou les attributs correspondant à la mise en forme sont supprimés et remplacés par des instruction CSS

Le xhtml se limite donc à exprimer la structure du document, et seulement la structure. La présentation est du ressort de la page de style, le comportement du code javascript.

Pour en savoir plus… Une documentation xhtml

en-tête xhtml

On écrira s

stématiquement en utf-8, l'en-tête obligatoire est la suivante:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
	<head>
		<title>titre</title>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
		<meta http-equiv="Content-Language" content="en" />
                ...
        </head>

Présentation du code

On s'arrangera pour écrire le code xhtml avec une indentation correcte, afin de faciliter sa lecture

La norme CSS

Le CSS est une norme permettant de définir la mise en pages. La norme CSS utilisée est la CSS 2.1. Les indications de style doivent se trouver dans un ou plusieurs fichiers css séparés, jamais dans la section head de la page, parfois (exceptionnellement) dans l'élément lui-même à travers une propriété style.

La présentation du css

Les fichiers de CSS doivent se présenter de la manière suivante:

  • Commentaires pour séparer les différentes sections (comme en C, c'est-à-dire /* … */)
  • Indentation comme ci-dessous:
    #ID_SOME_ID {
    background-color: red;
    }

Javascript

Le javascript

st un langage permettant d'implanter le comportement des différents éléments de la page web. Le code javascript doit se trouver dans un ou plusieurs fichiers js séparés, parfois (exceptionnellement et sur une seule ligne) dans la page web elle-même.

javascript discret

… en V.O. ça se dit Unobtrusive javascript (à vos souhaits).

Dans les formulaires

Ce style de programmation permet de séparer le code javascript (dédié au comportement) du code xhtml (dédié à la structure de la page). Lorsqu'on programme en “javascript discret”, on n'a plus d'attributs du style onclick=“xxxx” dans les balises html, ces attributs sont remplacés par des fonctions d'observation d'évènements écrites en javascript. Entre autres avantages, cela permet de simplifier le code perl (la complexité se retrouve dans le javascript, mais il s'agit essentiellement d'écrire proprement des objets javascript associés à des formulaires html, en particulier les constructeurs. cf. ci-dessous pour les détails). Les formulaires html sont le lieu où utiliser le javascript discret

... mais pas à la suite d'une requête ajax

Cependant, je n'ai pas réussi à implémenter ce style de programmation dans des pages mises à jour par ajax: les objets alloués lors de la création de la page étaient persistants lors des requêtes ajax successives… il a donc été nécessaire de revenir à la programmation traditionnelle, à base de onclick= dans les balises.

La bibliothèque prototype

La bibliothèque prototype prototype est une bibliothèque qui définit un grand nombre d'objets, de structures de données, de fonctions. Le fichier de la bibliothèque sera incorporé au projet. Il y a deux avantages à utiliser prototype:

  1. Le code est plus compact, plus rigoureux, plus clair à relire et plus simple à écrire.
  2. La portabilité entre les naviagateurs est meilleure

Portabilité

Les pages web doivent être conformes à la norme xhtml. D'autre part, on testera la page web au moins sur les navigateurs suivants:

  • firefox 2.x, versions unix et windows
  • Internet Explorer 7.x

Autres navigateurs avec lesquels il est souhaitable de tester la page:

  • opera (linux et windows)
  • konqueror (linux)
  • Internet Explorer 6 (windows)
  • Safari (macOS)

Un squelette

Avant de démarrer votre programme, récupérez ce squelette. Vous n'avez plus qu'à remplacer le code de ce squelette par votre code à vous.

Présentation du code

Dans quelle langue écrire ?

Le code sera écrit exclusivement en anglais (noms de variables, de subs, etc). Les commentaires et la documentation seront écrits de préférence en anglais, éventuellement en français.

Il est préférable d'écrire tout en anglais, afin de pouvoir aisément partager le code avec un collègue. Toutefois, mieux vaut une bonne documentation écrite en français qu'une mauvaises documentation écrite en anglais… d'autant plus qu'en cas de besoin, il est relativement simple de réécrire la documentation sans toucher au code, alors que traduire le code lui-même équivaut à refaire un développement.

Documentation générale du code

Il faut documenter le code, de préférence en utilisant le système (docpod ou doc de référence. Il est préférable de ne pas utiliser d'accents ou autres ç dans les lignes de documentation. FIXME Je ne suis pas sûr que le pod soit pertinent dans le cas de javascript, car les fichiers javascript restent des modules de bibliothèque, pas des commandes. Les directives pod pourront être relues par le programme perldoc, ou encore transformées en html par le programme pod2html.

Documentation en début de fichier

Les directives à insérer en tête de fichier sont les suivantes:

FIXME a faire

Documentation avant chaque sub

Un sub est une “boîte noire”; encore faut-il expliquer ce qui entre, ce qui sort, ce que fait le sub. doivent figurer:

/*
Title        : NewPosition
Usage        : o_obj.NewPosition(100,200,110,205)
Prerequisite : none
Procedure    : Changes to position/width/height of the div #ID_DYNAMIC_MARKER
Access       : private
Args         : The coord of the new position
Globals      : none
Return       : none
*/

remarques:

  • private s'applique uniquement lorsqu'il s'agit de méthodes de classes
  • On peut supprimer les attributs marqués none.
  • Ne pas oublier le mot-clé fonction ou procedure: une fonction renvoit quelque chose, une procédure ne renvoit rien. Il est très important de mettre en valeur cette information.

Il ne s'agit pas, ici, de mettre des tartines. Il doit être possible de dire en une ligne (2 ou 3 maxi) ce que fait la fonction. Sinon, cela veut dire que la fonction fait plein de choses compliquées, et dans ce cas il faut la remplacer par un ensemble de plusieurs fonctions élémentaires.

Indentation

Il faut indenter le code, c-à-d décaler chaque bloc de quelques espaces. Le nombre d'espace n'est pas déterminé; les valeurs suivantes sont acceptables:

  • 4 espaces par bloc
  • 8 espaces par bloc

On considère de manière générale que 4 espaces par bloc est une bonne solution, car cela produit du code lisible tout en évitant de décaler trop vers la droite: important lorsqu'il y a de nombreuses boucles imbriquées. Une autre question est de savoir si l'on met l'accolade ouvrante sur la même ligne que la condition dans le cas de boucles ou de structures if…: les deux formes sont acceptables; cela conduit à des codes du style:

if (condition) {
    ....
    ....
    ....
} else {
    ....
    ....
}

ou encore:

if (condition)
{
  ....
  ....
  ....
}
else
{
    ....
    ....
}

Variables et constantes

Constantes

Il n'existe pas de constantes en javascript standardisé: le mot-clé const est une extension Mozilla, et ne doit donc pas être utilisé (évidemment il marche partout sauf dans MSIE).

Déclaration de Variables globales

TOUTES LES VARIABLES GLOBALES DOIVENT ETRE DECLAREES, même si le langage ne l'impose pas.

Le code doit comporter le moins possible de variables globales. Cependant, à cause de l'interaction avec le html, il est impossible de s'en passer complètement. Afin de limiter l'usage des variables globales, on utilisera des objets (liste de paramètres ou de variables de configuration) : l'objet sera une variable globale, mais son état sera déterminé par ses données membres.

Le code suivant, à condition qu'il figure en-dehors de toute fonction ou de tout objet, correspond à la déclaration d'une variable globale…

var GLOBAL_1 = 0;

Le code suivant, même à l'intérieur d'une fonction, déclare lui aussi une variable globale: il ne faut pas l'utiliser

GLOBAL_2 = 0;

Noms de variables, de subs, de classes:

Plus la portée d'une variable est longue, plus le nom doit être informatif. Il est recommandé d'utiliser la politique de nommage suivante:

  • Les variables ou constantes locales sont écrites en lettres minuscules (sts).
  • Les variables ou constantes globales sont écrites en lettres majuscules, le nom doit être informatif (GLOBAL_STATUS, HOST).
  • Les noms de classes sont écrits avec la première lettre majuscule (Class), et il doit se terminer par le mot Class
  • Un nom de variable peut être séparé en deux parties de la manière suivante:
    • OtherClass pour une classe
    • lcl_hst pour une variable locale
    • GLOBAL_OBJECT pour une variable globale
  • Les noms de subs sont écrits en mettant en majuscule la première lettre de chaque mot (comme une classe, donc) (ChangeColor(“red”))

Règles de nommage:

D'autre part, les règles suivantes devront être adoptées pour nommer variables et subs:

  • Les noms de variables sont des noms communs (color)
  • Les noms de subs commencent par un verbe (mais éviter de commencer par Do, trop vague) (ChangeColor)
  • Les noms de variables autres que les scalaires commencent par un préfixe:
    • a_ pour un tableau (array, résultat de la fonction prototype $A())
    • h_ pour un tableau associatif (hash, résultat de la fonction prototype $H())
    • o_ pour un objet (résultat d'un new)
    • A_,H_,O_ pour l'équivalent en variables globales.

Noms d'Id xhtml

Les éléments xhtml peuvent avoir un attribut ID, c'est même un bon moyen pour effectuer une passerelle entre code html et code javascript ou css. Mais l'utilisation de ces Id doit rester exceptionnelle: en effet, il ne peut y avoir qu'un seul Id d'une valeur donnée par page, les Id html ressemblent donc fortement aux variables globales.

Règles de nommage:

Les Id xhtml seront écrits comme une constante globale (en majuscule, le caractère _ utilisé comme séparateur), en outre l'Id doit commencer par ID_:

<div id="ID_TITLE_CONTAINER">...</div>

noms de classe css

Chaque mot constituant le nom de la classe commence par une majuscule (même règle de nommage que pour les fonctions).

<span class="VeryImportantText">bla bla</span>

Sous-programmes:

Fonction ou procédure ?

Une procédure est un sous-programme qui ne renvoit pas de valeur. Une fonction est un sous-programme qui renvoit une ou plusieurs valeurs. Pour la clarté du code, Il est nécessaire de respecter les conventions suivantes:

  • Une procédure possède une ou plusieurs instructions return; : cela assure que la procédure renvoie la valeur undef.
  • Une fonction qui renvoie une seule valeur possède une ou plusieurs expressions du type return a;

Paramètres ou variables globales ?

Un sub ne doit pas modifier de variables globales. Au lieu de modifier une variable globale à l'intérieur d'un sub, on doit:

  • Passer la variable globale en paramètre
  • Dérouler le sub, en modifiant éventuellement la valeur passée
  • Renvoyer la valeur modifiée.

Fonction dans une fonction

  • Il est possible en javascript de définir une fonction à l'intérieur d'une fonction. Cela permet d'avoir un code très bien structuré, avec l'avantage d'avoir accès aux variables qui ont été déclarées dans la bloc avant la fonction: on a alors l'avantage des variables globales (moins de paramètres à passer), tout en ayant aussi l'avantage des variables locales.
  • Attention toutefois aux méthodes d'objets.

Programmation orientée objets

Il y a plusieurs manières de déclarer des objets, des héritages, etc. en javascript. Ce document définit une manière simple, utilisable avec prototypejs 1.6 minimum:

Déclaration d'un objet

Le code suivant déclare un objet avec son constructeur, une méthode, des données membres, et le commentaire associé:

/*
Title:     Narcisse Area class
Usage:     Base class for some objects 
Class:     Create an area element and declare some default event handlers on this element
Constructor Args:
               o_legend:         A div - We keep the Id, because $(legend) may change
               o_popup:          A DynamicPopupClass  object
               o_marker:         A DynamicMarkerClass object
               o_dessus_dessous: A DessusDessousClass object
Globals:   none

*/
NarcisseAreaClass = Class.create({
	initialize: function(legend,o_popup,o_marker,o_dessus_dessous)
	{
		this._legend = legend;
		this._o_popup  = o_popup;
		this.__o_marker = o_marker;
		this.__o_dessus_dessous = o_dessus_dessous;
	},
	/*
	Title:     MouseOver
	Usage:     through a mouseover event handler, called by the MouseOver functions of the derived objects
	Procedure: Manage the display property of some elements, marks the selected element
	Args:      (x1,y1,x2,y2) the coordinates of the area
	Globals:   none
	*/	
	MouseOver: function(x1,y1,x2,y2)
	{
		
		// If necessary, we hide the active tab of the control box - See DessusDessous for the details
		this.__o_dessus_dessous.LegendIsUp();		
		
		// Display the dynamic marker
		this.__o_marker.NewPosition(x1,y1,x2,y2);
	},
	MouseDown: function(){}
});

Déclaration d'un objet qui hérite d'un autre objet

Le code suivant déclare un objet avec indication d'héritage et utilisation des méthodes ou du constructeur de la superclasse:

/*
Title:     Chromosome Cursor class
Usage:     var o_cursor=new ChromosomCursorClass(legend,o_popup,o_marker,o_dessus_dessous);
Class:     Extends NarcisseAreaClass
Constructor Args: see NarcisseAreaClass
Globals:   none
*/

ChromosomeCursorClass = Class.create(NarcisseAreaClass,{
	initialize: function($super,legend,popup,marker,o_dessus_dessous){$super(legend,popup,marker,o_dessus_dessous);},

	/*
	Title:     MouseOver
	Usage:     through a mouseover event handler, called by the MouseOver functions of the derived objects
	Procedure: Display some information in the dynamic legend
	Args:x1,y1,x2,y2: see NarcisseAreaClass
             pos :   position (in base pairs) of the cursor
             orga:   the organism
             chrom:  the chromosome
             length: the chromosome length (in base pairs)
             start:  the 1st displayed base pair
             end:    the last displayed base pair
	Globals:   none
	*/	

	MouseOver: function($super,x1,y1,x2,y2,pos,orga,chrom,length,start,end)
	{
		$super(x1,y1,x2,y2);
		...
        },

	/*
	Title:     MouseDown
	Usage:     through a mousedown event handler
	Procedure: Open a popup
	Args:      x1,y1,x2,y2: Coordinates of the area
                   pos:   position (in base pairs) of the cursor
	Globals:   none	
	*/	
	MouseDown: function(x1,y1,x2,y2,pos)
	{
		var html='<h1>Position</h1>';
                ...
		this._o_popup.Open(this.x2+5,this.y2+5,html);
	}
});

Commentaires:

On doit avoir un paragraphe de commentaires avant toute définition de classe. Ce commentaire doit comporter:

  • Une ligne Class: qui définit brièvement ce que fait cette classe
  • Une ligne constructor Args qui décrit les arguments à passer au constructeur
  • Le mot class remplace function ou procedure pour décrire brièvement l'objet

Variables publiques, protégés, privées

Il n'y a pas de contrôle d'accès à ce niveau en javascript. Mais il est important de montrer à l'utilisateur de l'objet qui est privé, qui est protégé, qui est public. D'où la convention:

  • Variables privées: le nom doit commencer par un double souligné
  • Variables protégées: le nom doit commencer par un simple souligné
  • Variables publiques: mêmes règles de nommage qu'une variable locale ordinaire.

Méthodes publiques, protégées, privées

La même règle de nommage s'applique aux méthodes (fonctions-membres) de l'objet:

  • Les noms de méthodes sont écrits en mettant en majuscule la première lettre de chaque mot
  • Pour une méthode privée, faire précéder le nom d'un double souligné
  • Pour une méthode protégée, faire précéder le nom d'un simple souligné
  • Les méthodes doivent être précédées d'une documentation de même que pour une fonction ordinaire.

Fonction définie à l'intérieur d'une méthode

A l'intérieur d'une méthode, on a accès aux données membres par l'intermédiaire du mot-clé this. Cependant, si on définit une fonction à l'intérieur d'une méthode, le mot-clé this pointe sur le contexte de la fonction (celle-ci est elle-même un objet). Donc on n'a plus accès à l'objet lui-même. D'où le code qui suit:

SomeClass = Class.create({
	initialize: function(x)
        {
            this.member = 5;
        },
        SomeMethod: function()
        {
            var that = this;
            function Internal()
            {
                alert(that.member);    // this.member ne marche pas ici, on utilise that !!!
            }
        }
});

Travailler avec le DOM

prototype offre un grand nombre de fonctions pour interagir avec le DOM. Dans la mesure du possible, on utilisera ces fonctions, elles sont probablement plus portables que leurs équivalents natifs en javascript.

Il s'agit des fonctions permettant d'identifier un élément html, de se déplacer dans le DOM, de récupérer la valeur d'un champ (getValue ou $F), d'afficher ou cacher un élément, de changer son style (setStyle), …

Tout sur le DOM ici

Passage de données entre le serveur et le client

Le format le plus léger pour passer les données entre le serveur et le client est JSON.

Envoyer une structure perl

L'exemple suivant montre comment envoyer le contenu de ParamParser:

use JSON;

# $o_param est un objet ParamParser, je le recopie dans un tableau associatif avant de le transformer par JSON
my %h_param;
$o_param->Dump('HASH',\%h_param);  

my $json = new JSON;
print "<script type=\"text/javascript\">var O_PARAM = " . $json->objToJson(\%h_param) . ".evalJSON(true);</script>\n";

Utiliser les données ainsi envoyées à partir du javascript

Le code ci-dessus va générer une variable globale sous forme d'un objet on peut accéder à chaque paramètre de l'une des deux manières suivantes:

var input = O_PARAM['input'];
var color = O_PARAM.color;

Quelques objets tout faits

Les bibliothèques suivantes sont intégrées à lipmutils (répertoire javascript):

  • prototype v 1.6.0
  • wforms très pratique pour valider automatiquement ses formulaires
  • Tools.js quelques objets écrits par Emmanuel
  • InitForm.js une fonction permettant d'initialiser un formulaire à partir d'un objet (typiquement l'objet O_PARAM envoyé par JSON dans l'exmple ci-dessus)

Outils de développement recommandés

La méthode de développement qui me semble la plus commode est la suivante:

  • Installer firefox 2.0
  • Installer les extensions suivantes:
  • configurer l'éditeur (javascript/xhtml/css) pour les charactères utf-8
  • Vérifier à tout moment que le code html est correct en utilisant HTML validator
  • Déboguer le javascript avec firebug
  • Déboguer les css avec firebug et web developer
  • Lorsque ça marche sous firefox, tester sous Opera: la console (Tools/Advanced/Errors console) est très bien faite, et vous verrez des erreurs que firefox aura laissé passer, mais qui risquent de gêner MSIE.
  • Lorsque ça marche sous Opera, tester sous MSIE 7 et MSIE 6, puis sous Konqueror…

Exemple

Auteurs

Emmanuel Courcelle, Sébastien Letort

normesprogrammation/normesjavascript.txt · Last modified: 2009/02/02 14:41 by manu