================================ Pages dynamiques en Javascript ================================ .. role:: lat .. role:: en .. highlight:: javascript Motivation ========== Architecture Client-Serveur --------------------------- .. figure:: client-server.png :height: 8ex Source image http://commons.wikimedia.org/wiki/File:Client-server-model.svg * Jusqu'à maintenant, le gros du travail était fait par le serveur. * On souhaite pouvoir déporter une partie de la **logique applicative** coté client. Programmation coté client ------------------------- * Ceci suppose d'avoir un langage de programmation *généraliste* (≠ HTML/CSS) compris par tous les navigateurs. * Actuellement, ce langage est Javascript. .. note:: Javascript a beaucoup évolué au cours de son histoire. La version que nous présentons ici est ES6, une version relativement récente (2015) qui a apporté beaucoup de nouveautés au langage, mais également des incompatibilités avec les versions précédentes. Gardez cela en tête lorsque vous trouverez des exemples en ligne. Syntaxe ======= Inspiration ----------- * Comme son nom l'indique, la syntaxe de Javascript est (librement) inspirirée de celle de Java (ou du C). * La similitude s'arrête là : Javascript n'est pas basé sur Java. Condition --------- .. container:: comparison .. code-block:: javascript if (i < 10) { j = j+1; k += i; } else { j = 0; } .. code-block:: python if i < 10: j = j+1 k += i else: j = 0 Boucles ------- .. container:: comparison .. code-block:: javascript while (i < 10) { j = j*i; i += 1; } .. code-block:: python while i < 10: j = j*i i += 1 .. container:: comparison .. code-block:: javascript for(let i of [1,1,2,3,5,8]) { j = j*i; } .. code-block:: python for i in [1,1,2,3,5,8]: j = j*i .. container:: comparison .. code-block:: javascript for(let i=2; i<1000; i=i*i) { console.log(i); } .. code-block:: python i = 2 while i<1000: print(i) i = i*i Fonctions --------- .. container:: comparison .. code-block:: javascript function fact(n) { let f = 1; while (n>1) { f = f*n; n -= 1; } return f; } .. code-block:: python def fact(n): f = 1 while n>1: f = f*n n -= 1 return f Exceptions ---------- .. container:: comparison .. code-block:: javascript if (i < 0) { throw new Error( "negative value"); } .. code-block:: python if i < 0: raise Exception( "negative value") .. container:: comparison .. code-block:: javascript try { i = riskyFunction(); } catch (err) { i = -1; } .. code-block:: python try: i = riskyFunction() except Exception as err: i = -1 Tableaux -------- .. container:: comparison .. code-block:: javascript let a = [4,1,3,6,4]; let i = 1; while (i 32) { // erreur: undefined n'a pas de longueur // ... } .. _dom: L'arbre DOM =========== Présentation ------------ La structure d'un fichier HTML peut être vue comme un *arbre*. .. graphviz:: graph { node [ shape=box, style=rounded ] html -- head head -- title -- title_txt html -- body body -- h1 -- h1_txt body -- p p -- p_txt p -- a -- a_txt body -- img a [ label="a\nhref='./link'" ] img [ label="img\nsrc='./pic'" ] title_txt [ shape=box, style=filled, label="Le titre" ] h1_txt [ shape=box, style=filled, label="Le titre" ] p_txt [ shape=box, style=filled, label="Bonjour le " ] a_txt [ shape=box, style=filled, label="monde" ] } Terminologie ------------ * Cet arbre s'appelle l'arbre DOM (pour :en:`Document Object Model`). * Les nœuds correspondant aux balises sont appelés des **éléments**. * Les nœuds contenant le contenu textuels sont simplement appelés des « nœuds texte ». .. note:: Il existe d'autres types de nœuds (par exemple les nœuds commentaire), mais ils sont plus rarement utiles. L'objet ``document`` -------------------- En JS, la variable globale ``document`` contient un objet représentant le document HTML. Elle permet d'accéder aux éléments du document : * `document.getElementById `_, * `document.getElementsByTagName `_, * `document.getElementsByClassName `_, * `document.querySelector `_, * `document.querySelectorAll `_ .. note:: Il est plus efficace d'utiliser les méthodes ``getElement*`` que d'utiliser ``querySelector*`` avec les sélecteurs correspondants (``tagname`` ou ``#identifier``). Attributs et méthodes d'un élément ---------------------------------- ``textContent``: permet de consulter *et modifier* le contenu textuel de l'élément ``style``: permet de consulter et modifier l'attribut ``style`` de l'élément, sous forme d'un objet ayant un attribut pour chaque propriété CSS. (:lat:`e.g.` ``e.style.fontSize`` pour la propriété ``font-size``) .. note:: Pour faciliter l'utilisation en Javascript, la typographie des attributs de ``style`` n'est pas la même que celle des propriétés CSS correspondantes. Les tirets (``-``) sont remplacés par une mise en majuscule de la letter suivante (`CamelCase `_). .. nextslide:: ``classList``: permet de consulter et modifier l'attribut ``class`` de l'élément, grâce aux méthodes suivantes : * ``add(cls)``: ajoute la classe `cls` a l'élément. * ``remove(cls)``: retire la classe `cls` a l'élément. * ``contains(cls)``: indique si l'élément possède actuellement la classe ``cls``. * ``toggle(cls)``: inverse l'état de la classe `cls` (présente/absente) sur l'élément. .. note:: Comme en HTML+CSS, il est préférable de spécifier la mise en forme à l'aide de classes dans le CSS, et de modifier ces classes dans le code Javascript, plutôt que la spécifier directement dans le code Javascript à travers l'attribute ``style``. .. nextslide:: Les éléments possèdent de nombreux autres attributs; en particulier, chaque attribut HTML a une contrepartie en Javascript. On peut notamment citer : * ``href`` (pour les ````) * ``src`` (pour les ````) * ``value`` (pour les ````) * ``disabled`` (pour tous les éléments de formulaire) * ``checked`` (pour les cases à cocher) * :lat:`etc`... Expérimentez sur `cet exemple`__. __ http://champin.net/enseignement/intro-js/_static/exemples/element_manipulation.html Parcours du DOM --------------- Récupérer des nœuds depuis un élément ``e``\  : * `e.getElementsByTagName `_, * `e.getElementsByClassName `_, * `e.querySelector `_, * `e.querySelectorAll `_, * `e.childNodes `_, * `e.children `_, * `e.parentNode `_, Modification du DOM ------------------- Création d'un nœud : * `document.createElement `_, * `document.createTextNode `_, * `n.cloneNode `_ Une fois créé, le nœud est encore *hors* de l'arborescence du document (et donc, non affiché). Il est nécessaire de le rattacher à un nœud parent par l'une des méthodes suivante : * `n.insertBefore `_, * `n.replaceChild `_, * `n.removeChild `_, * `n.appendChild `_ Intégration JS dans HTML ======================== Avertissement ------------- Il existe de nombreuses méthodes. Celle proposée ici vise à être simple et évolutive, mais suppose un navigateur moderne. Intégration d'un script à une page ---------------------------------- On include dans le HTML (dans le ``head`` ou le ``body``) une balise ``script`` ayant la structure suivante : .. code-block:: html Le script sera exécuté après le chargement complet du code HTML. Programmation événementielle ---------------------------- * En programmation impérative classique, la fonction principale (``main``) décrit dans quel ordre les différentes fonctions du programme doivent s'exécuter. * En programmation événementielle, on « abonne » chaque fonction à un (ou plusieurs) **événement(s)**. * La fonction s'exécute lorsqu'un événement auquel elle est abonnée se produit. * Les événement sont (souvent) liés aux interactions de l'utilisateur avec l'application. Quelques événements utiles -------------------------- * `click `_ * `mouseover `_ * `keypressed `_ * `input `_ * `change `_ * `submit `_ Des listes plus exhaustives sont disponibles `ici `_ et `là `_. .. note:: Souvent, et pour des raisons historiques, les noms des événements sont préfixés par ``on``. Attention : le véritable nom de l'événement n'inclut *pas* ce préfixe. Mise en œuvre ------------- .. code:: js function incrementCounter() { let i = document.getElementsByTagName("input")[0]; i.value = Number(i.value) + 1; } let b = document.querySelector("button"); b.addEventListener('click', incrementCounter); http://jsbin.com/mexuna/1/edit?html,js,output .. note:: * La méthode ``addEventListener`` associe un *comportement* (fonction) à un événement émis par un élément. * ⚠ Attention : le deuxième paramètre de ``addEventListener`` est le **nom** d'une fonction, **sans** parenthèses (ce n'est pas un *appel* de la fonction). Autre exemple ------------- .. code:: js function incrementCounter() { let b = document.getElementsByTagName("body")[0]; let p = document.createElement("p"); p.textContent = "Vous avez cliqué"; p.class.add("message"); b.appendChild(p); } let b = document.querySelector("button"); b.addEventListener('click', incrementCounter);