Archives mensuelles : juillet 2013

[JS] API Fullscreen : déclencher le plein écran

Lorsque vous naviguez, vous pouvez presque tout le temps passer en plein écran : via la touche F11 si vous avez un clavier ou une option d’un quelconque menu sur vous êtes sur un appareil tactile. Ceci permet de masquer le navigateur (barre d’adresses, menus, …) pour ne voir que ce qui vous intéresse, à savoir le contenu de la page en cours. C’est très pratique si vous présentez une application web à un client par exemple 😉
Toutefois la manipulation à effectuer n’est pas à la portée de n’importe qui, vous avez donc peut être envie de proposer un joli bouton à vos utilisateurs.
Mais si vous êtes sur un site où une miniature est disponible, souvent un bouton « zoom » permet de l’afficher en plus grand. Vous aimeriez pourtant qu’une photo haute résolution prennent 100% de l’espace disponible en plein écran !!

Solution : ces deux options sont disponibles grâce à l’API « Fullscreen ».
Problème : c’est un API non finalisé, et donc chaque navigateur a fait son truc dans son coin…

Dans cet article, je vous propose une solution pérenne pour utiliser l’API tranquillement et facilement.

Présentation de l’API

  • element.requestFullscreen() : passage en mode plein écran
  • document.cancelFullscreen() : sortie du mode plein écran
  • document.fullscreenElement : élément actuellement en plein écran
  • Vous ne pouvez pas décrire l’API plus simplement…

    L’élément sur lequel la première méthode est appelée sera mis en plein écran, c’est à dire que les tous les autres éléments HTML de la page qui ne sont pas enfants de cet élément deviendront invisibles pour l’utilisateur.

    Hélas il y a plusieurs petits détails qui ont leur importance.

    Le premier est que chaque navigateur a fait son truc dans son coin, et différemment d’une version à l’autre d’un même navigateur ! Vous pourrez donc trouver :
    – element.requestFullscreen()
    – element.requestFullScreen()
    – element.webkitRequestFullscreen()
    – element.webkitRequestFullScreen()
    – element.webkitEnterFullscreen()
    – element.webkitEnterFullScreen()
    – element.mozRequestFullscreen()
    – element.mozRequestFullScreen()
    – etc.
    (Notez la casse de « fullscreen/fullScreen » qui en Javascript est primordiale)

    Le deuxième est que les méthodes ne peuvent être utilisées que lors d’un événement « click ».
    C’est plutôt rassurant car ainsi une action de l’utilisateur est requise, il n’est donc pas possible de faire exploser l’écran et déclencher une crise d’épilepsie par exemple. Enfin pas avec cet API en tout cas…

    Le troisième est que chaque navigateur a encore fait son truc dans son coin, et par exemple Firefox mettra l’élément en pleine page (100% de l’espace disponible) tandis que Chrome conservera la taille de l’élément en le centrant sur un fond noir. Vous devez donc galérer un peu pour normaliser tout ça en attendant qu’ils se mettent d’accord.

    La quatrième est qu’il n’y a pas de moyen de faire un thème CSS différent en plein écran et hors plein écran. Chrome propose un :-webkit-full-screen mais selon mes tests il ne fonctionne pas correctement, par exemple impossible de faire un :-webkit-full-screen > span pour thèmer les éléments enfants de l’élément en plein écran. Sous Firefox je n’ai même pas essayé mais j’ai l’habitude d’être déçu par Mozilla donc je vais leur accorder le bénéfice du doute et dire qu’ils ont géré, eux :p

    Enfin un dernier petit détail : au moment de la première utilisation de l’API, un message propose à l’utilisateur d’autoriser le site à passer en plein écran.

    Mon cahier des charges

    Comme d’habitude je vais utiliser mon style de programmation objet : des méthodes définies indépendamment et utilisées en une seule classe.

  • GFullscreen(handler,target) : fait tout à ma place
  • Si l’API est disponible la fonction renverra « true » et un clic sur l’élément « handler » va activer ou désactiver le plein écran sur l’élément « target ».
    Si l’API n’est pas disponible la fonction renverra « false » et l’élément « handler » sera masqué.

  • private GFullscreen.toggle(event) : passe en plein écran ou sort du plein écran
  • Ainsi on peut lier cette méthode à l’événement « click » de l’élément « handler » qui fera l’action adaptée à la situation (c’est un bouton « plein écran »).

  • private static GFullscreen.available() : l’API est-il disponible ?
  • Avant que quelqu’un imagine l’API, il n’existait pas. Avant que votre navigateur ait implémenté l’API, il ne l’implémentait pas.
    J’ai donc besoin d’une méthode pour savoir cela, elle doit renvoyer « true » le cas échéant ou « false » dans le cas contraire.

  • private static GFullscreen.check() : ajout/retrait de la classe CSS « g_fullscreen » sur l’élément
  • À intervalle régulier je vérifie si on est en plein écran.
    J’ai besoin d’avoir une classe en CSS pour thèmer tendrement, donc si elle n’est pas présente sur l’élément actuellement en plein écran, je la rajoute.
    Si elle est présente sur des éléments qui ne sont pas en plein écran, je l’enlève.

  • private static GFullscreen.compat(affix) : renvoi le nom de la méthode ou de la propriété à utiliser
  • C’est pour la gestion de la compatibilité entre les navigateurs, dans la famille « request » je demande la méthode de l’élément, dans la famille « cancel » je demande la méthode du document, dans la famille « element » je demande la propriété du document. Et plus vite que ça !

    Le code

    var GFullscreen = function(handler,target) {
        if(!GFullscreen.available()) {
            handler.style.display = 'none';
            return false;
        }
        handler.onclick = GFullscreen.toggle;
        handler.g_fullscreen_target = target;
        return true;
    };
    GFullscreen.toggle = function(event) {
        event.stopPropagation();
        if(!document[GFullscreen.compat('element')]) {
            this.g_fullscreen_target[GFullscreen.compat('request')]();
            GFullscreen.check();
        }
        else document[GFullscreen.compat('cancel')]();
    };
    GFullscreen.available = function() {
        return typeof(document.documentElement[GFullscreen.compat('request')]) == 'function';
    };
    GFullscreen.check = function() {
        var space = ' ';
        var classname = 'g_fullscreen';
        var full = document[GFullscreen.compat('element')];
        var has = full && (space+full.className+space).indexOf(space+classname+space) != -1;
        if(full && !has) {
            full.className += (full.className?space:'')+classname;
            GFullscreen.check.last = full;
        }
        else if(!full && GFullscreen.check.last) {
            GFullscreen.check.last.className = (space+GFullscreen.check.last.className+space).replace(space+classname+space,' ');
            if(GFullscreen.check.last.className == space) GFullscreen.check.last.className = '';
            return;
        }
        setTimeout(GFullscreen.check,100);
    };
    GFullscreen.compat = function(affix) {
        if(typeof(GFullscreen.compat.names) == 'undefined') GFullscreen.compat.names = {};
        if(typeof(GFullscreen.compat.names[affix]) == 'undefined') {
            GFullscreen.compat.names[affix] = [];
            var prefixes = ['','webkit','moz','o','ms'];
            var methods = ['fullscreen','Fullscreen','fullScreen','FullScreen'];
            var affixes = [affix,affix.substr(0,1).toUpperCase()+affix.substr(1)];
            var alternative = '';
            if(affix == 'request') alternative = 'enter';
            if(affix == 'cancel') alternative = 'exit';
            if(alternative != '') {
                affixes.push(alternative);
                affixes.push(alternative.substr(0,1).toUpperCase()+alternative.substr(1));
            }
            for(var p=0; p<prefixes.length; p++)
            for(var m=0; m<methods.length; m++)
            for(var a=0; a<affixes.length; a++)
                GFullscreen.compat.names[affix].push(prefixes[p]+(affix=='element'?'':affixes[a])+methods[m]+(affix=='element'?affixes[a]:''));
        }
    
        var obj = affix == 'request' ? document.documentElement : document;
        for(var n=0; n<GFullscreen.compat.names[affix].length; n++) {
            var name = GFullscreen.compat.names[affix][n];
            if(typeof(obj[name]) != 'undefined') {
                if(GFullscreen.compat.names[affix].length > 1) GFullscreen.compat.names[affix] = [name];
                return name;
            }
        }
        return null;
    };
    

    Démonstration

    Un peu de HTML

    <div id="g_fulldemo">
        <input type="button" id="g_fulldemo_handler" value="Démo" />
        Je suis le texte contenu dans le div mis en plein écran si vous cliquez sur le bouton !
    </div>
    

    Un peu de JS

    $ = function(id){return document.getElementById(id);}
    GFullscreen($('g_fulldemo_handler'),$('g_fulldemo'));
    

    Résultat :

    Et ensuite ?

    Ensuite il n’y a plus qu’à attendre que la spécification soit terminée et que tous les navigateurs fasse la même chose.
    Pour l’instant vous pouvez voir où en est qui sur caniuse.com