Date de première publication : 2020/05/01
Cet exercice vise à introduire les éléments suivants :
- le modèle "classique" de classe en JS
- les éléments personnalisés
- une touche de CSS
Ce que l'on va faire marche sous Chrome et Firefox (en général, c'est ce dernier qui est le plus contraignant).
On cherche à faire une SPA ou single page application. L'application comportera trois pages. Voici le corps :
Les débuts
<page id="page1">
<h1>Page 1</h1>
<a href="#page2" >Vers la page 2</a>
</page>
<page id="page2">
<h1>Page 2</h1>
<a href="#page1" >Vers la page 1</a>
</page>
<page id="error">
<h1>Page d'erreur</h1>
<a href="#page1" >Retour à la page 1</a>
</page>
La balise <page>
n'existe pas mais cela ne perturbe pas le navigateur.
Avec une pincée de CSS, chacune des pages occupe l'écran
:root {
box-sizing : border-box;
overflow-x : hidden;
}
page {
display : block;
width : 100vw;
min-height : 100vh;
}
Avec une pincée de CSS, la page d'erreur que j'ai faite ressemble à cela :
On va ajouter un peu de javascript :
class App {
_current;
constructor() {
this.hideAllPages();
}
hideAllPages() {
}
}
var app = new App();
On vient de créer un classe App
qui à l'instanciation cache toutes les pages sauf peut-être la première :-)
L'attribut _current
est un attribut public. La convention d'écriture veut que l'on considère comme un attribut privé. Cette notion n'existe pas encore sous FF mais elle existe sous Chrome. Il faut alors nommer l'attribut #current
Nous allons ajouter un peu de routage maintenant. L'idée est de n'afficher qu'une seule page donnée dans l'adresse de la page
https://localhost/index.html#page2
Afficher la première page, c'est facile ! Comment afficher celle qui est donnée ? Il suffit d'analyser l'URL de la page et en particulier le champ hash
d'un objet URL
new URL(document.location)
Pour le fun, on peut compter le nombre de pages uniquement avec le CSS
counter-reset : page;
pour la balise:root
counter-increment : page ;
pour la balisepage
content : "page" counter(page);
par exemple sur l'élément qui affiche la page
la propriété content
devrait pourvoir s'appliquer à tout élément mais si cela ne marche pas utilisez uniquement ::before
et ::after
défini en CSS2.
Le mieux est d'utiliser l'identifiant de la page si elle en a un et d'en générer un dans le cas contraire, ce que l'on fera facilement dans la section suivate.
Balises personnalisées
La navigateur n'a pas été perturbé par la balise page
mais ce n'est pas bon pour l'indexation. Nous allons donc utiliser la technique pour créer des composants web à savoir les éléments personnalisés ou CustomElements
Il y a une contrainte sur les noms d'éléments personnalisés, il faut que le nom contienne un tiret. Je propose de renommer la balise <page>
en <simple-page>
(il faut également mettre à jour le CSS éventuellement).
Il faut ensuite enregistrer l'élément :
customElements.define('simple-page', SimplePage);
Et créer la classe qui va bien :
class SimplePage extends HTMLElement {
constructor() {
super();
}
}
Il n'y a pas d'héritage "usuel" dans un langage à objet à prototype mais cette syntaxe permet de se raccrocher à ce que l'on a l'habitude de manipuler.
Cet élément, au titre de composant web va construire son propre arbre de donnée au sein d'un ShadowDOM
où l'on mettra le code html, le style et autres ...
var shadow = this.attachShadow({mode: 'open'});
On modifiera le constructeur pour calculer un identifiant s'il n'est pas fourni ou afficher un message d'erreur avec la console ou la page d'erreur.
Compléments
La page d'erreur
Dans un framework, la page d'erreur est fondamentale pour tout ce qui pourrait mal se passer, l'erreur de routage étant probablement la plus fréquente
L'idée est de créer un page d'erreur à partir de simple-page
avec un texte et style paramétré. On peut utiliser pour ce faire l'héritage pour faire une page d'erreur spécifique. Il y a deux possibilités avec l'une des syntaxes suivantes :
<error-page></error-page>
<simple-page is="error-page"></simple-page>
Si l'utilisateur ne définit pas de page d'erreur, il faut que l'application en crée un.
"display" d'une page
La page est pour l'instant affichée grâce à la propriété CSS display : block;
Si l'utilisateur choisit un autre mode d'affichage, il peut être intéressant de sauvegarder ce mode dans un attribut pour réafficher sereinement la page.
Pour ce faire, pour traiter "tous" les cas (et pas seulement le style donné en balise), il faut considérer le style "calculé".
let cs = window.getComputedStyle(element,null);
cs.getPropertyValue("display");
Autres
- Si l'URL contient un identifiant qui n'existe pas, la page d'erreur est affichée. Si aucun identifiant n'est fourni, le premier élément est affiché.
- J'ai fait le choix d'afficher les pages une à la fois avec des liens. Cela pourrait être amusant de passer d'une page à une autre par un swipe comme expliqué dans cet article
Typescript
L'idée maintenant est d'écrire le code de cette application, non plus en JS directement mais en Typescript. Pour cela, il faut sortir le code écrit en JS dans des fichiers externes et vous pouvez copier ces fichiers dans un sous-répertoire ts
en modifiant l'extension (js devient ts).
Pour faire du TS, vous avez besoin du compilateur tsc
qui s'installe avec npm
(pour tous les utilisateurs de la machine)
npm install -g typescript
npm peut s'installer tout seul mais c'est le gestionnaire de paquet livré avec node.js
Pour transpiler "à la main", il faut exécuter la commande suivante :
tsc fichier.ts
La prochaine étape serait d'utiliser un outil pour transpiler automatiquement le typescript en javascript.