Publié dans : La gadgetière

Dans un blog OB on rédige les commentaires dans un popup (un "diablog" en jargon OB – ô Roland Dubillard !). Ce popup se plante au centre de l'écran et il n'y a pas moyen de le déplacer. Il s'affiche par-dessus une vitre très sombre et on ne peut rien changer à cette vitre.

Alors j'ai pris ma… ma référence JavaScript et mon couteau, et j'ai taillé un petit bout de programme JS pour améliorer tout ça. Tant que j'y étais, le bouzin traite en fait tous les diablogs présents sur un blog. Voilà – mais ça ne vaut que pour les blogs de niveau "Privilège" ou "Premium".

Avertissement honnête avant de poursuivre : ce programme, vous vous en doutez, exploite à fond ce que je sais de la structure d'un blog OB (petite publicité : tout est résumé dans cet article et les suivants). Donc si OB modifie cette structure il ne me demandera pas mon avis et le programme pourra ne plus marcher. Dans un tel cas, résignez-vous à le désactiver en attendant que j'en ponde une version à jour.


Deux parties dans cet article, comme toujours : un mode d'emploi simple et bref (j'espère) puis, lecture totalement facultative, le démontage du programme si vous voulez comprendre comment ça marche. Aujourd'hui ce démontage sera moins fouillé que d'habitude parce que le programme est long et emploie un tas de trucs vicieux : je me bornerai aux grandes lignes. Pas trop déçus ?


Installation

Si vous n'avez jamais installé de JavaScript chez vous rendez-vous ici pour les détails.

Copiez/collez le programme suivant dans un fichier sur votre ordinateur :

DiablogPlus={
        event:{
                add:function(o,e,f) {
                        if (o.addEventListener) o.addEventListener(e,f,false);
                        else if (o.attachEvent) o.attachEvent('on'+e,f);
                        }
                ,remove:function(o,e,f) {
                        if (o.removeEventListener) o.removeEventListener(e,f,false);
                        else if (o.detachEvent) o.detachEvent('on'+e,f);
                        }
                }
        ,DOM:{
                hasClass:function(r,reg){
                        return r.className&®.test(r.className)
                        }
                ,getStyle:      window.getComputedStyle ?
                                                function(n,s) {return window.getComputedStyle(n,null)[s]}
                                : (     document.body.currentStyle ?
                                                function(n,s) {return n.currentStyle[s]}
                                :               function(n,s) {return null}
                                )
                ,getScroll:     typeof window.pageYOffset =='number' ?
                                                function(){return [window.pageXOffset,window.pageYOffset]}
                                : (     document.documentElement && (typeof document.documentElement.scrollTop=='number') ?
                                                function(){return [document.documentElement.scrollLeft,document.documentElement.scrollTop]}
                                : (     document.body && (typeof document.body.scrollTop=='number') ?
                                                function(){return [document.body.scrollLeft,document.body.scrollTop]}
                                :               function(){return [0,0]}
                                ))
                ,windowSize:
                        typeof window.innerWidth == 'number' ?
                                function(){return [window.innerWidth,window.innerHeight]}
                : (     document.documentElement && (document.documentElement.clientWidth||document.documentElement.clientHeight) ?
                                function(){return [document.documentElement.clientWidth,document.documentElement.clientHeight]}
                : (     document.body && (document.body.clientWidth||document.body.clientHeight) ?
                                function(){return [document.body.clientWidth,document.body.clientHeight]}
                :               function(){return null}
                ))
                }
        ,diablog:{
                redo:function(r,type){var t;
                        r.previousSibling.className='vitre';
                        r.className+=' plus '+type;
                        if(type=='eMail'){
                                var n=10,that=this,thrd=setInterval(function(){
                                        if(!n--) clearInterval(thrd);
                                        for (var i=0,t,tt=r.getElementsByTagName('legend');t=tt[i];++i) if (DiablogPlus.DOM.hasClass(t,/\btitle\b/)) break;
                                        if(t) {
                                                with(r.firstChild){className='title';innerHTML=t.innerHTML}
                                                t.className='';
                                                t.innerHTML='';
                                                that.finish(r);
                                                clearInterval(thrd);
                                                }
                                        },200);
                                }
                        else this.finish(r);
                        }
                ,finish:function(r){
                        var t=r.firstChild;
                        t.setAttribute('title',"Cliquez pour déplacer le popup");
                        DiablogPlus.glisse.setup(r,t
                                ,function(){
                                        var wSize=DiablogPlus.DOM.windowSize(),dW=r.clientWidth;
                                        return [-dW*0.7,0,wSize[0]-dW*0.3,wSize[1]-60]
                                        }
                                );
                        }
                }
        ,glisse:{
                o: undefined
                ,X0: undefined
                ,Y0: undefined
                ,limits:undefined
                ,setup:function(r,h,f) {
                        DiablogPlus.event.add( (h?h:r)
                                ,'mousedown'
                                ,(function(_r,_f) {
                                                return (function(e){return DiablogPlus.glisse.start(e,_r,_f)})
                                                })(r,f) 
                                );
                        }
                ,start:function(e,r,f) {
                        var evt = e || window.event;
                        with(DiablogPlus.glisse){
                                if (o) stop();
                                o = r;
                                o.className += ' glisse';
                                DiablogPlus.event.add(document,'mousemove',move);
                                DiablogPlus.event.add(document,'mouseup',stop);
                                if(f) {limits= f();var scr=DiablogPlus.DOM.getScroll();limits[0]+=scr[0];limits[1]+=scr[1];limits[2]+=scr[0];limits[3]+=scr[1]}
                                X0 = parseInt(DiablogPlus.DOM.getStyle(o,'left'))-evt.clientX;
                                Y0 = parseInt(DiablogPlus.DOM.getStyle(o,'top'))-evt.clientY;
                                }
                        }
                ,move:function(e) {
                        var evt = e || window.event;
                        with(DiablogPlus.glisse){
                                var X = X0 + evt.clientX,Y = Y0 + evt.clientY;
                                if(limits) {
                                        X = Math.max(Math.min(X,limits[2]),limits[0]);
                                        Y = Math.max(Math.min(Y,limits[3]),limits[1]);
                                        }
                                o.style.left = X+'px';
                                o.style.top = Y+'px';
                                }
                        }
                ,stop:function() {
                        with(DiablogPlus.glisse){
                                DiablogPlus.event.remove(document,'mousemove',move);
                                DiablogPlus.event.remove(document,'mouseup',stop);
                                o.className = o.className.replace(/\bglisse\b/,'');
                                o = null;
                                limits = null;
                                }
                        }
                }
        ,init:function(id){
                var r,i,rr;
                function addHandler(r,type) {
                        DiablogPlus.event.add(r,'click',function(){
                                var n=5;
                                var t=setInterval(function() {
                                        if(!n--) clearInterval(t);
                                        var r=document.body.lastChild;
                                        while(r&& !DiablogPlus.DOM.hasClass(r,/\bdiablog\b/))r=r.previousSibling;
                                        if(r) {DiablogPlus.diablog.redo(r,type);clearInterval(t)}
                                        },200);
                                })
                        }
                for(i=0,rr=document.getElementById(id).getElementsByTagName('a');r=rr[i];i++)
                        {if (!r.className) continue;
                        if (/\blinkAddComment\b/.test(r.className)) addHandler(r,'addComment');
                        if (/\blinkRecommend\b/.test(r.className)) addHandler(r,'eMail');
                        if (/\blink\b/.test(r.className)) addHandler(r,'shortURL');
                        }
                }
        }
DiablogPlus.init('cl_1_0');

Faites deux modifications dans ce fichier :

  1. essentielle : remplacez cl_1_0 par l'identifiant de la cellule de blog contenant vos articles. C'est cl_1_0 s'il n'y a aucun module à gauche, cl_1_1 s'il y a une colonne de modules à gauche, cl_1_2 s'il y a deux colonnes de modules à gauche.
  2. facultative : remplacez le texte Cliquez pour déplacer le popup par un message plus à votre goût. Il s'agit de l'infobulle qui apparaît au survol de la barre de titre du popup ;

Stockez le programme ainsi retouché dans votre espace Mes Documents/Autres fichiers.

Ajoutez une ligne au pied de page du blog :

<script type="text/javascript" src="URL_du_programme"></script>

Remplacez bien sûr URL_du_programme par l'url effective du fichier.

Si vous êtes en configuration avancée, assurez-vous d'ajouter cette ligne dans le pied de blog ARTICLE et ACCUEIL, ainsi que dans celui de PAGES.

Voilà pour l'installation.

Fignolage(s) CSS

Une fois installé, le programme fonctionne sans autre ajout. Mais, tant qu'à regarder de près la structure des diablogs, j'en ai profité pour ajouter de quoi présenter plus finement tout le truc.

La vitre fumée

Le div (bien peu) transparent intercalé entre le popup et le blog reçoit la classe vitre. Même si ce div vous agace je vous déconseille formellement de le supprimer en ajoutant à votre CSS

.vitre {display:none}

… parce que le visiteur pourrait alors cliquer n'importe où dans votre blog tout en écrivant un commentaire : caca-beurk garanti.

Utilisez plutôt quelque chose dans ce goût-là :

.vitre {opacity:0.3!important;background-color:#272020!important}

Les deux !important sont… importants pour que votre règle ait une chance de marcher, la valeur de l'opacité (opacity) et celle de la couleur de vitre (background-color) sont à votre choix.

Le diablog

Tous les diablog portent la classe diablog, donnant ainsi une première possibilité de mise en forme.

Le programme, quand il se déroule correctement, leur ajoute la classe plus. Cette classe supplémentaire permet de donner une présentation différente aux diablogs déplaçables et à ceux qui ne le sont pas. Ça servira le jour où OB ajoutera un nouveau diablog, ça peut servir dès maintenant à voir si le programme a bien fait son travail ou non.

Toujours s'il se déroule correctement, le programme ajoute à chaque diablog une classe précisant sa nature : addComment pour le diablog de saisie de commentaire, shortURL pour le diablog affichant l'URL simplifiée, eMail pour les diablogs "Recommander" ce blog ou un article.

Le diablog en cours de déplacement

Lorsque le lecteur clique sur la barre de titre du diablog pour le faire glisser à l'écran, celui-ci prend la classe glisse et la perd dès que le lecteur relâche le bouton de la souris. Exemple d'utilisation :

.glisse{border-style:dotted!important;}

Je l'emploie sur le présent blog et, honnêtement, je ne me rappelle plus si le !important est vraiment indispensable…


Démontage

Faut-il redire que cette partie n'est à lire que si la tripe de la bête vous intéresse ? En particulier, ne cherchez pas ici la réponse aux difficultés que vous pourriez rencontrer pour installer ce programme – mieux vaut dans ce cas laisser un commentaire ou m'écrire.

Je commencerai par des explications de principe, en français espéré clair, avant de présenter leur traduction en JavaScript.

Fonctionnement d'origine

Regardons d'abord la situation avant tout charcutage.

Une page de blog contient (peut contenir) des boutons ouvrant un diablog. Ces boutons sont des ancres a. Les classes possibles pour ces ancres sont, actuellement, linkAddComment, linkRecommend ou link, selon le type de diablog à créer.

Si vous regardez le code source de la page vous verrez bien ces liens, mais aucun gestionnaire d'événements du genre onclick="openDiablog();" ni aucun renvoi tel que href="diablog.php" (technique employée dans des versions précédentes d'OB). En outre la page ne contient dans son code HTML aucun diablog, même habilement dissimulé.

Voilà donc deux questions : d'où sort le diablog ? pourquoi les boutons ont-ils un effet ?

Y a un truc : un programme JavaScript conçu par OB se charge, lorsque la page est complètement chargée, de trouver les boutons et d'y greffer un gestionnaire d'événements (un onclick).

Lorsqu'on clique (SI on clique) sur un bouton à diablog, le gestionnaire greffé au bouton crée à la volée l'équivalent de tout le code HTML du diablog, y compris la vitre fumée qui l'isole du blog – inutile de vous dire que ce gestionnaire est un programme assez gros et pas vraiment simple.

Lorsque, pour finir, on clique sur un bouton de fermeture du diablog, ce bouton fait son travail (=poster le commentaire ou autre) ou rien du tout si c'est un bouton Fermer ou Annuler ou équivalent, puis détruit le diablog et la vitre fumée : la page de blog revient à l'état précédent.

Enrichir le fonctionnement d'origine

Pas question, bien sûr, de modifier les programmes OB ni de les remplacer par des variantes personnelles, du moins tant que je ne suis pas salarié d'OB. La seule voie raisonnable est d'ajouter quelque chose à côté des programmes OB, en exploitant au mieux ce qu'ils produisent.

Dans le cas présent, il nous faudra deux sortes d'ajouts :

  1. évidemment, ajouter quelque chose au diablog pour le rendre mobile
  2. et, puisque ce diablog n'existe pas au départ mais est engendré par un clic sur un bouton, ajouter autre chose au bouton, autre chose qui se charge d'enrichir le diablog après sa création.

Cette deuxième partie est la plus simple à expliquer, autant commencer par elle.

Ajouter un gestionnaire d'événements aux boutons

Le programme est invoqué dans le pied de page du blog, il s'exécute donc après la mise en place de tous les boutons à diablog. Il peut alors chercher ces boutons dans la page et, à chacun d'eux, rajouter un gestionnaire d'événéments (un onclick) perso. Ce gestionnaire vient s'ajouter au gestionnaire OB, pas le remplacer. Jusqu'ici, c'est un décalque de ce que fait OB.

Ensuite… D'une part, ce nouveau gestionnaire doit trafiquer le diablog, donc s'exécuter après la création du diablog, donc s'exécuter après le gestionnaire OB qui, justement, crée ce diablog. D'autre part, si on clique sur le bouton, les deux gestionnaires s'exécuteront. Mais dans quel ordre ? On ne sait pas trop, ce n'est normalisé nulle part.

Il faut donc employer une première astuce diabolique : mon gestionnaire tente plusieurs fois, à intervalles réguliers, de trouver le diablog. S'il le trouve il le rend mobile – et c'est fini. S'il ne le trouve pas il attend la tentative suivante. Par prudence le nombre de tentatives est limité à 5, par intervalles de 200ms : si au bout d'une seconde il n'y a toujours pas de diablog, c'est qu'on a un gros problème ailleurs et que mieux vaut laisser tomber.

Rendre le diablog mobile

Ce que l'on désire est facile à décrire : placer la souris sur la barre de titre du diablog, enfoncer le bouton. Ensuite, tout en maintenant le bouton enfoncé, promener la souris : le diablog doit suivre le mouvement. Enfin relâcher le bouton : le diablog reste à sa nouvelle place, on peut promener la souris librement.

Ceci implique d'abord de traiter non pas un seul événement mais trois : enfoncer le bouton sur la barre de titre, promener la souris, relâcher le bouton. Ensuite, il ne faut pas tout traiter à la fois : il serait idiot qu'un passage accidentel sur la barre de titre déplace le diablog sans qu'on ait d'abord appuyé sur le bouton.

L'idée est donc d'attacher à la barre de titre un gestionnaire de l'événement mousedown (= appuyer sur le bouton). Ce gestionnaire, qui ne s'exécute que si l'on appuie sur le bouton de la souris au survol de la barre de titre, attache alors au document deux gestionnaires d'événements : l'un pour mousemove (= bouger la souris), l'autre pour mouseup (=relâcher le bouton de souris). Le gestionnaire de mousemove, naturellement, déplace le diablog en même temps que le pointeur de souris. Le gestionnaire de mouseup, lui, fait le ménage : enlever le gestionnaire de mousemove et s'enlever lui-même, ce qui rétablit la situation d'avant le clic sur la barre de titre. En procédant ainsi on est certain de ne déplacer le diablog qu'après avoir cliqué sur sa barre de titre. Subtil, non&nbp;?

Enfin, pourquoi attacher les deux derniers gestionnaires au document ? D'abord parce que ça réagit plus vite, ensuite parce que ça garantit de ne pas décrocher le diablog de la souris si celle-ci va vraiment trop vite pour que le navigateur suive.

Synthèse

Voici le scénario d'ensemble :

  1. en fin de chargement de la page, trouver tous les boutons à diablog ;
  2. attacher à chacun de ces boutons un onclick – décrit ci-dessous ;
  3. le gestionnaire onclick des boutons à diablog détecte le diablog (et la vitre fumée), ajoute quelques classes là où il faut, et attache à la barre de titre du diablog un onmousedown – décrit ci-dessous ;
  4. le gestionnaire onmousedown de la barre de titre du diablog ajoute la classe glisse au diablog, note la position courante de la souris et celle du diablog, et attache au document un gestionnaire onmousemove et un gestionnaire onmouseup ;
  5. le gestionnaire onmousemove du document détecte la nouvelle position de la souris et déplace le diablog en conséquence ;
  6. le gestionnaire onmouseup du document enlève la classe glisse du diablog, enlève le gestionnaire onmousemove du document, et s'enlève lui-même du document.

Oufffff… Il n'y a plus qu'à.

Assemblage

Comme annoncé plus haut, je ne vais pas reconstituer ici le programme dans tous ses détails, seulement présenter ses composants et expliquer les choix que j'ai pu faire.

Encapsulation

Le programme consiste en la description d'un unique objet nommé DiablogPlus. La dernière ligne du programme, en invoquant la méthode init de cet objet, déclenche tout le traitement. Cette encapsulation dans un objet est une protection contre les interactions inopinées avec d'autres programmes JS, je la pratique pour tous les programmes que je vous propose.

Survol rapide

L'objet DiablogPlus se compose de quatre sous-objets :

event
deux méthodes pour attacher ou enlever un gestionnaire d'événements ;
DOM
quelques utilitaires pour récupérer des renseignements dans la page ;
diablog
des méthodes pour enrichir le diablog après sa création ;
glisse
les variables et les méthodes utilisées pour réaliser le drag'n'drop d'un élément du document – pas uniquement un diablog, donc ;

et d'une seule méthode : init déjà mentionnée.

Cette division en quatre sous-objets n'est pas techniquement indispensable mais permet d'y voir plus clair, pour programmer comme pour exposer la suite.

Le sous-objet event

Classique : deux méthodes, add et remove, pour attacher ou enlever à l'objet o le gestionnaire f de l'événement e. Le test à l'intérieur de chaque méthode sert à employer l'outil que propose le navigateur : à la mode IE ou à la mode standard.

Le sous-objet DOM

Divers utilitaires :

hasClass
savoir si tel objet possède telle classe
getStyle
connaître le style effectif d'un élément, en tenant compte du CSS et du style local (style="...")
getScroll
savoir de combien de pixels la fenêtre a été "scrollée"
windowSize
connaître la taille de la fenêtre (en pixels)

La première méthode recourt à une RegExp, les trois dernières sont pleines de tests pour s'adapter à ce que propose le navigateur. Si vous voulez vous en inspirer, sachez que je ne garantis pas qu'elles marchent dans tous les cas de figure. Si vous les regardez de très près, vous verrez que les tests servent à définir la fonction une fois pour toutes plutôt qu'à guider son exécution à chaque appel : écriture plus complexe mais exécution plus rapide.

Le sous-objet diablog

Deux méthodes : redo et finish, deux étapes pour traiter un diablog. Deux étapes parce que le diablog Recommander n'a pas son titre au même endroit que les autres diablogs (détails anatomiques ici : HTML de blog [6] Diablogs). La méthode redo, dans ce cas, doit donc attendre que le diablog soit complètement construit avant de poursuivre. Pour cela elle teste à intervalles réguliers l'existence de l'élément attendu et ne peut lancer la deuxième étape qu'après l'avoir trouvé.

Dans ces deux méthodes r est la référence du diablog et type son type (=addComment, eMail ou shortURL, valeurs sorties de mon imagination féconde). Dans finish la variable t est la référence de la barre de titre du diablog, que ce soit celle d'origine ou celle reconstituée par redo.

Le sous-objet glisse

Il est un peu plus complexe que l'article de Peter-Paul Koch dont je me suis inspiré parce que

  • il fallait distinguer le diablog de sa barre de titre : cliquer dans le diablog ailleurs que sur la barre de titre ne doit pas permettre de le déplacer ;
  • j'ai voulu limiter le déplacement du diablog dans la fenêtre, histoire qu'un visiteur impatient ne l'entraîne pas trop loin vers le bas ou la droite.

On trouve d'abord quatre propriétés (=variables) dans cet objet :

o
référence de l'élément de document à rendre mobile (ici, le diablog)
X0 et Y0
position initiale du diablog, rapportée au curseur de la souris
limits
référence de la fonction limitant les déplacements du diablog

Ensuite quatre méthodes :

setup
comme son nom l'indique : préparation de l'élément par l'ajout de start sur la barre de titre comme gestionnaire de l'événément mousedown. Cette fonction s'exécute à la fin du chargement de la page.
Arguments : r est la référence de l'élément, h celle de sa barre de titre, f celle de la fonction de limitation des déplacements.
start
calcule X0 et Y0, ajoute la classe "glisse", ajoute move et stop comme gestionnaires des événements mousemove et mouseup. S'exécute lorsqu'on clique sur la barre de titre.
Arguments : e est la référence de l'objet événement fourni par le navigateur (= tout ce qu'on peut savoir sur l'événement), r la référence de l'élément, f celle de la fonction de limitation des déplacements).
move
calcule les nouvelles coordonnées de l'élément et le déplace. S'exécute quand on bouge la souris après avoir cliqué sur la barre de titre.
Argument : e est la référence de l'objet événement.
stop
défait tout ce qu'avait fait start. S'exécute quand on relâche le bouton de la souris.
Pas d'arguments.

La méthode init

Elle contient essentiellement la fonction addHandler, traitement d'un créateur de diablog, et un passage en revue des boutons présents dans la page pour détecter les ancres à traiter.

La partie intéressante est addHandler, qui ajoute au bouton de quoi enrichir le diablog : un gestionnaire d'événements. Ce gestionnaire contient une boucle temporelle (= l'appel à setInterval) pour rechercher le diablog. On se trouve devant une fonction anonyme(= la boucle) dans une fonction anonyme (= le gestionnaire) dans une fonction (= addHandler) qui, ouf, ne l'est pas !

Petit mot pour finir

Ainsi s'achève la revue du programme. Je l'ai faite, comme toutes les autres, parce que passer de "hello world" à une application concrète ne va pas forcément de soi ; désosser un programme réaliste peut aider à franchir le pas. Si vous cuisinez du JS dans votre coin, n'hésitez pas à reprendre certaines idées, certaines méthodes, certains petits bouts, à poser des questions sur certains recoins obscurs – et il y en a, je n'ai pas tout dit -)

Lundi 28 novembre 2011 1 28 /11 /Nov /2011 00:00
- Par Aïe mes doigts ! - Communauté : Aide
Mazette ! Déjà 2 mots ! - Et si vous écriviez quelque chose ?

Commentaires

Calendrier

Mai 2012
L M M J V S D
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      
<< < > >>

overblog

Images...

  • Rue Suger
  • Magnolia
  • Minotaure
Créer un blog gratuit sur over-blog.com - Contact - C.G.U. - Rémunération en droits d'auteur - Signaler un abus - Articles les plus commentés