Je reprends ce que je racontais il y a quelques jours au sujet du passage de données entre PHP et Javascript et entre Javascript et CSS, histoire de ne pas avoir de script javascript ou de styles inline dans le code.
Je vais prendre pour exemple le plugin hScroll qui permet de définir quelques paramètres utilisés côté CSS.
Notez que cette version du plugin n’est pas encore diffusée et qu’elle le sera après la sortie de la version 2.15 de Dotclear, dont elle dépend.
Mise en place des paramètres avec PHP
Voilà le traitement — je n’ai mis que la partie qui nous intéresse — effectué sur le behavior publicHeadContent :
…
echo \dcUtils::jsJson('hscroll', [
'color' => ($core->blog->settings->hscroll->color ?: '#e9573f'),
'top' => ($core->blog->settings->hscroll->position == 'top' ? "$offset" . 'px' : 'unset'),
'bottom' => ($core->blog->settings->hscroll->position == 'bottom' ? "$offset" . 'px' : 'unset'),
'shadow' => ($core->blog->settings->hscroll->shadow ? '1' : '0')
]);
echo
\dcUtils::jsLoad($core->blog->getPF('util.js')) .
\dcUtils::jsLoad($core->blog->getPF('hScroll/js/cssvar.js')) .
\dcUtils::cssLoad($core->blog->getPF('hScroll/css/hscroll.css'));
…
- L’utilisation de dcUtils::jsJson() permet d’inclure les paramètres au format JSON dans l’entête.
- L’inclusion de util.js permet d’avoir accès la fonction javascript de décodage.
- cssvar.js gère le passage des paramètres du côté des CSS
- Enfin inclusion de la feuille de style qui utilisent les variables CSS
Le code de la fonction dcUtils::jsJson() est le suivant :
public static function jsJson($id, $vars)
{
// Use echo dcUtils::jsLoad($core->blog->getPF('util.js')); to use the JS getData() decoder in public mode
$ret = '<script type="application/json" id="' . html::escapeHTML($id) . '-data">' . "\n" .
json_encode($vars, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES) . "\n" . '</script>';
return $ret;
}
Sachant que le script proprement dit du plugin (affichage de la barre de lecture) est inclus en fin de page via le behavior publicFooterContent :
…
echo
'<div id="hscroll-bar"><div id="hscroll-bar-inner"></div></div>' . "\n" .
\dcUtils::jsLoad($core->blog->getPF('hScroll/js/hscroll.js'));
…
Le code du-dit script étant le suivant :
function hscroll_bar() {
const t = document.querySelector('#hscroll-bar');
t.style.width = `${window.pageYOffset / (document.body.clientHeight - window.innerHeight) * 100}%`;
}
window.addEventListener('load', hscroll_bar);
window.addEventListener('scroll', hscroll_bar);
Récupération des données JSON et mise en place des variables CSS
Là c’est le script cssvar.js qui fait le job :
const hscroll_data = getData('hscroll');
…
document.documentElement.style.setProperty('--hscroll-color', hscroll_data.color);
document.documentElement.style.setProperty('--hscroll-top', hscroll_data.top);
document.documentElement.style.setProperty('--hscroll-bottom', hscroll_data.bottom);
document.documentElement.style.setProperty('--hscroll-shadow', hscroll_data.shadow);
La fonction getData() fournie dans util.js est utilisée pour récupérer les variables qui sont ensuite définies côté CSS. La fonction getData() est codée ainsi :
var getData = getData || function(id, clear = true) {
let data = {};
// Read the JSON-formatted data from the DOM. (from https://mathiasbynens.be/notes/json-dom-csp)
// To be use with: <script type="application/json" id="myid-data">{"key":value, …}</script>
const element = document.getElementById(`${id}-data`);
if (element) {
try {
data = JSON.parse(element.textContent);
if (clear) {
// Clear the element’s contents
element.innerHTML = '';
}
} catch (e) {}
}
return data;
};
Vous noterez que j’utilise un var et un test logique qui permet de ne pas redéfinir la fonction si jamais le fichier était appelé plusieurs fois.
Récupération/utilisation des variables côté CSS
Enfin, dans la CSS, les variables sont utilisées ainsi :
#hscroll-bar {
height: 4px;
position: fixed;
top: var(--hscroll-top);
bottom: var(--hscroll-bottom);
left: 0;
z-index: 3000;
-webkit-backface-visibility: hidden;
-moz-backface-visibility: hidden;
backface-visibility: hidden
}
#hscroll-bar-inner {
box-shadow: var(--hscroll-shadow);
height: 100%;
background-color: var(--hscroll-color);
}
Conclusion
Aucun script inline, aucun style inline non plus, tout est passé/décodé via JSON. Ça rend le code plus simple, chacun se débrouillant avec ce dont il a besoin et on ne mélange pas code HTML/Javascript/CSS dans le markup.
Tout bénéf’ donc \o/
Côté administration, il existe une fonction équivalente à dcUtils::jsJson() : dcPage::jsJson(), avec les mêmes paramètres. La fonction Javascript de récupération des données, getData(), est quant à elle disponible en permanence.
1 De Da Scritch -
J’essaie de comprendre…. pourquoi n’as-tu pas directement utilisé une génération PHP d’une feuille de style comportant tes variables CSS ? Du coup, tu pourrais cacher cette feuille de style, gagner en vitesse de rendu, et éliminer l’étape cssvar.js , non ?
Perso, j’en suis plus à utiliser le cheminement inverse : d’une variable CSS, en faire une variable JS pour l’utiliser. Cela permet de contextualiser la variable en question selon un XPath particulier, ou un mode de rendu (largeur du viewport < 320pt par exemple)
2 De Franck -
Je ne suis pas personnellement favorable à la création de fichiers (feuille de style dans ce cas) vu qu’on peut, par exemple, être dans un environnement
.Sinon je note pour le cheminement inverse, ça pourrait éventuellement rendre service, c’est une bonne idée.
3 De Nicolas -
Ce n’est pas toujours possible de faire une feuille de style css générée en php (à condition de l’envoyer au navigateur avec le bon type mime bien évidemment) car d’une part cela oblige à faire une requête http de plus et d’autre part, il faut aussi avoir le contexte de ce qu’on veut générer. Le dit contexte dépend de l’utilisateur (langue, préférences,…) mais aussi du contexte d’affichage (billets, liste de billets, tags, …). D’autre part ces morceaux de javascript peuvent être générés par partie par des plugins tiers. En gros ce n’est pas simple.
4 De Da Scritch -
Bonjour Nicolas.
J’ai jamais eu de souci pour générer des CSS dynamiques avec le bon type-mime, surtout que c’est hyper facile avec le système de templates de clearbricks.
Ensuite, avec HTTP2, la requête supplémentaire n’a plus de coût. La réponse peut aussi être cachée par le serveur http avec l’entête Expire. Dans la plupart des cas, l’option est même activée par défaut, sans avoir à toucher à la conf.
Quant au contexte, non, on sert une CSS pour un site entier, pas pour une seule page. L’exception pouvant être une WebComponent, et encore, il suffit de bien architecturer. Et c’est justement ce que je fais avec les CSS vars qui sont lues par le JS?
5 De Franck -
Eh ben dis-donc, t’es bien affirmatif ! J’ai des CSS utiles uniquement pour le
de Dotclear et qui ne sont pas utilisées ailleurs. Aucune raison de charger ça sur les autres pages de l’administration.Ne pas oublier que le
de certains est le d’autres !