dimanche 17 août 2014

Accélérez vos pages contenant des vidéos YouTube

Accélérez vos pages contenant des vidéos YouTube

Nous voulons que nos visiteurs reçoivent leur contenu aussi vite que possible, c’est-à-dire que celui-ci soit aussi léger que possible tout en limitant le nombre de requêtes nécessaires. Mais nous désirons aussi qu’ils restent sur nos pages, et qu’ils s’y divertissent. C’est là que les vidéos entrent en scène. Elles illustrent notre contenu textuel, lui apportent plus de vie, et sont servies par des sites tiers. Que demander de plus ? Cependant, elles présentent un coût caché : elles sont lentes et lourdes à télécharger, même si vos visiteurs ne les regardent pas.
Ce billet est une traduction de l’article Faster YouTube Embeds with JavaScript que j’ai publié sur le site Sitepoint.

Une seule vidéo sur une page appelée par une innocente iframe ajoute jusqu’à 6 requêtes HTTP et 450ko de contenu. La solution proposée ici peut réduire ce coût à une seule requête et environ 50ko par vidéo, plus quelques octets de javascript (en plus de la bibliothèque jQuery si vous n’appréciez pas le javascript minimaliste).

Et vous savez quoi ? Cette solution n’est même pas nouvelle. Elle a été proposée par Amit Agarwal en avril 2013.

Alors, quel est le truc ?


Dans cette solution, le DOM est parsé par le javascript sur le document load, et chaque appel à une vidéo YouTube (via un div spécifique, pas une iframe classique) est remplacé par une image de prévisualisation à laquelle on attache l’iframe lorsqu’on clique dessus. De cette façon, on obtient une jolie image toujours servie par un serveur tiers à une fraction du prix du lecteur vidéo complet (le lecteur vidéo n’est chargé que lorsque la vidéo est regardée).

Ma petite valeur ajoutée


J’ai réécrit le code d’Amit en javascript classique et en notation jQuery. J’ai gardé les commentaires originaux dans le code pour qu’il reste aussi compréhensible que possible. Une nouvelle fonctionnalité apparaît cependant dans le paramètre data du HTML5 vous permettant d’ajouter n’importe quel paramètre à votre URL YouTube pour personnaliser votre lecteur.

YouTube offre en effet une liste de paramètres pour montrer ou cacher les contrôles, leur logo et les infos associées à la vidéo, ainsi que pour paramétrer la qualité de la vidéo ou sa frame de départ.
  • controls : passez-le à 0, et la couche de contrôles n’est plus affichée sur le lecteur.
  • modestbranding : passez-le à 1, et le logo YouTube disparaît de la barre de contrôle.
  • rel : passez-le à 0, et aucune vidéo similaire ne sera proposée à la fin de la lecture.
  • showinfo : passez-le à 0, et le lecteur n’affichera plus d’information comme le titre de la vidéo ou la personne l’ayant téléchargée avant que la vidéo ne commence à jouer.
  • start : donnez-lui un nombre de secondes, et le lecteur commencera à jouer la vidéo à partir de cet instant (ou plutôt à partir de l’image clé la plus proche).
  • vq : indiquez-lui la qualité vidéo requise, si celle-ci est supportée (exp. : hd720 quand la haute qualité est disponible)
Lorsqu’on ajoute l’iframe de YouTube sur l’événement (clic souris ou touche [Entrée]), certains paramètres reçoivent une valeur prédéfinie, à savoir autoplay (nous voulons que la lecture commence dès que la miniature est cliquée) et autohide (pour cacher la barre de progression de la vidéo et les contrôles du lecteur quand aucune interaction n’est détectée).

Les miniatures YouTube supportées


Chaque vidéo YouTube est livrée avec une liste d’images prégénérées. Vous pouvez les retrouver via l’URL http://img.youtube.com/vi/<youtube-video-id>/<youtube-thumbnail> (où img.youtube.com peut même être raccourci en i.ytimg.com). Celles qui nous intéressent sont les suivantes :
  • default.jpg (version par défaut, 120px * 90px)
  • hqdefault.jpg (version haute qualité, 480px × 360px)
  • mqdefault.jpg (version de qualité moyenne, 320px × 180px)
  • sddefault.jpg (version de qualité standard, 640px × 480px)
  • maxresdefault.jpg (version en résolution maximale, 1 280px × 720px)
Dans le code suivant, nous utilisons la miniature sddefault.jpg. Selon vos besoins et les capacités de l’écran de vos utilisateurs, elle peut être remplacée par une autre des miniatures définies précédemment.

Note : Cette solution n’est pas responsive. Je vous invite à consulter les commentaires de l’article original pour une adaptation à l’écran de vos visiteurs.

Le code HTML


Le code HTML définit l’identifiant de la vidéo YouTube, la taille de la vidéo (largeur et hauteur) et liste les paramètres de l’URL si nécessaire. Cette implémentation gère aussi l’accessibilité : l’utilisateur peut soit cliquer sur l’image pour lancer la vidéo, soit la sélectionner avec la touche [Tab] (via l’attribut tabindex) puis appuyer sur la touche [Entrée].
    <div class="youtube" id="lR4tJr7sMPM" style="width:500px;height:281px;" tabindex="1"></div>

    <div class="youtube" id="fsrJWUVoXeM" data-params="modestbranding=1&showinfo=0&controls=0&vq=hd720" style="width:640px;height:360px;" tabindex="2"></div>


Le code CSS


Dans les deux vidéos utilisées en exemple, les images sont en 16/9e, ce qui renvoit une image sddefault.jpg avec des bandes horizontales noires. Pour les dissimuler lorsqu’on montre la miniature, la propriété background-position est settée à center, et la largeur et la hauteur de l’image sont ajoutés directement en ligne dans la balise div (style="width:500px;height:281px;"). De cette façon, il est possible de montrer différentes tailles de vidéo sur la même page.

L’icône de lecture indique aux visiteurs que le contenu n’est pas juste une image et qu’ils peuvent interagir avec lui. Elle est ajoutée dans un layer au-dessus de la miniature avec une transition d’opacité pour la mettre en valeur. J’utilise ici un PNG sous forme de data URI encodé en base 64 (via IconFinder), ce qui économise une requête HTTP et est déjà compatible avec IE8.
.youtube {
    background-position: center;
    background-repeat: no-repeat;
    position: relative;
    display: inline-block;
    overflow: hidden;
    -webkit-transition: all 200ms ease-out;
    -moz-transition: all 200ms ease-out;
    -o-transition: all 200ms ease-out;
    transition: all 200ms ease-out;
    cursor: pointer;
}

.youtube .play {
    background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAERklEQVR4nOWbTWhcVRTHb1IJVoxGtNCNdal2JYJReC6GWuO83PM/59yUS3FRFARdFlwYP1CfiojQWt36sRCUurRIdVFXIn41lAoVdRGrG1M01YpKrWjiYmaSl8ybZJL3cd+YA//NLObd3++eO8x79z5jSq5Gw+8kov0AP8vMR5l1BtBZQM4B8ks75wCdZdYZZj5qLZ4hov2Nht9Z9vhKKSIaB/gI4M4w62KeAO6Mte4lYOq20FxrlqqOibhHmeWbvNC9ZfDX1mLae391aN6limO/gwgvAPJbWeAZuSDingdwXTBw7/0IsyaA/Fkh+KqOkD+YNfHej1QKD+y7iVlOhgLvFqFfNJvNGyuBJ+KDAF8MDd0tgS8y64OlgSdJMsysL4cG7SOHkyQZLhTee7+d2R2rAVy/S+Jd7/32ouBHAP4gNNRGQyTHc/84NhqNywZp5rvjjnnvt21aABFeCQ+RLwAf2hQ8s7sv9OCLk6AHNgQvIrvbfzKCD76g/O6cu7lf/iER/aQGgy448pExZmhdegAPhR9sObFWH1gT3lp7DaA/5bkIgJhZPgsNmz02novj+KqeApj1ubwXWe4kdyeznAgNvTpE/HQmvKqOMeuFogTUVQSRno+iaLRLAJF7uIgL9O4ubgL8aWgB7S44mNX+35YpICUiAvS9sBLkq1WzT+NFffl6AuoiApi6NT37h6sWkBIRZGkQ8YtLgyji6e1mBYTqCEBPG2Naz+0BWQgtoGoRgCzEsd9hAN1X5BfnFZASUfrSAFQNsyZ1FJASUVpHiLinDJG8U2cBZYogkrcNs5waBAGdstbeU9zdqpw0gPwwSAI6VUxHyFlDpOcHUUBBIuYNs14aZAE5RVwyzPr3/0EAEY0TyfGNjBWQvwZ+CTSbehfAH29mrID8bET0+0EUkAd8WYDOmqJ3ecsG30yr9wqRfm6Y+a1BEFDEjHfHvWmY9ck6CygHvBVr8Xhtb4ZE5HZA3y8DvBNA1TjnrmXWf+sioMwZX5V/VHXMGGMMoKdDCxCRvRWBdzKzdHEO+EisilbPyopHYqp6S9UCAsz4iojI7hUDAtyXVQgIDd6KnOoaWNkbI6FaPSuZGyMArsi7MZoloB4zviI/Nhr3X95jltwTRQmoIfgisy5ai+me67OI7fE4nrqjrqfK1t0eby0FPRB6oGVlchL3rgnfrq19RKbVBdhV9IOSwJmfmJi4vi/4ThERitwyCxVAFqydshuCX5awhQ9KtmuIWd8IDZED/nXT77rvVVv6sHRKwjYi91poqP7Dr+Y6JJ1VSZIMA3wkPNy6bX+o8Bcm0sXMdwM8Fxo0A3xORPaWBp6uPXsmbxCRD0NDL0dOANhVCXy6iAjMcjbcrMt3RITKwdMVRdFo+y5yvkL4eWZ+zHt/ZVD4dEVRNGotpst+dZZZH8k86lqn2pIvT/eqrNfn2xuyqYPZ8mv7s8pfn/8Pybm4TIjanscAAAAASUVORK5CYII=") no-repeat center center;
    background-size: 64px 64px;
    position: absolute;
    height: 100%;
    width: 100%;
    opacity: .8;
    filter: alpha(opacity=80);
    -webkit-transition: all 0.2s ease-out;
    -moz-transition: all 0.2s ease-out;
    -o-transition: all 0.2s ease-out;
    transition: all 0.2s ease-out;
}

.youtube .play:hover {
    opacity: 1;
    filter: alpha(opacity=100);
}

Implémentation en javascript classique


Sans dépendance aucune et avec l’implémentation la plus rapide, la version en javascript classique utilise ici le plus petit test de chargement du DOM que j’ai pu trouver. Les spécificités des navigateurs doivent être prises en considération, comme le manque de support de la fonction getElementsByClassName par IE8 (si vous voulez le supporter).
"use strict";
function r(f){/in/.test(document.readyState)?setTimeout('r('+f+')',9):f()}
r(function(){
    if(!document.getElementsByClassName) {
        // IE8 support
        var getElementsByClassName = function(node, classname) {
            var a = [];
            var re = new RegExp('(^| )'+classname+'( |$)');
            var els = node.getElementsByTagName("*");
            for(var i=0,j=els.length; i<j; i++)
                if(re.test(els[i].className))a.push(els[i]);
            return a;
        }
        var videos = getElementsByClassName(document.body,"youtube");
    }
    else {
        var videos = document.getElementsByClassName("youtube");
    }

    var nb_videos = videos.length;
    for (var i=0; i<nb_videos; i++) {
        // Based on the YouTube ID, we can easily find the thumbnail image
        videos[i].style.backgroundImage = 'url(http://i.ytimg.com/vi/' + videos[i].id + '/sddefault.jpg)';

        // Overlay the Play icon to make it look like a video player
        var play = document.createElement("div");
        play.setAttribute("class","play");
        videos[i].appendChild(play);

        videos[i].onkeypress = function(event) {
            // return key
            if (event.keyCode == 13) {
                document.getElementById(this.id).click();
            }
        }

        videos[i].onclick = function() {
            // Create an iFrame with autoplay set to true
            var iframe = document.createElement("iframe");
            var iframe_url = "https://www.youtube.com/embed/" + this.id + "?autoplay=1&autohide=1";
            if (this.getAttribute("data-params")) iframe_url+='&'+this.getAttribute("data-params");
            iframe.setAttribute("src",iframe_url);
            iframe.setAttribute("frameborder",'0');

            // The height and width of the iFrame should be the same as parent
            iframe.style.width  = this.style.width;
            iframe.style.height = this.style.height;

            // Replace the YouTube thumbnail with YouTube Player
            this.parentNode.replaceChild(iframe, this);
        }
    }
});

Voir le Pen Faster YouTube embeds (vanilla js) par Alexis Ulrich (@mancko) sur CodePen.



Note au sujet du mode strict de l’ECMAScript5


Le mode strict de l’ECMAScript5 aide à écrire un code javascript plus portable. Pour l’activer, le code javascript doit commencer par :
"use strict";
Dans ce mode strict, les définitions de fonctions peuvent être déclarées au niveau le plus élevé, ou au niveau le plus élevé du corps de la fonction. Le code suivant présente donc une erreur de syntaxe.
if(!document.getElementsByClassName) {
    function getElementsByClassName(node, classname) {
Cette erreur est corrigée en utilisant la notation suivante (seulement dans un bloc) :
if(!document.getElementsByClassName) {
    var getElementsByClassName = function(node, classname) {


Implémentation avec jQuery


Bien qu’étant à mes yeux plus expressive et bénéficiant d’un plus large support de navigateurs, l’implémentation avec jQuery vient au prix de l’ajout de la bibliothèque jQuery (environ 82ko pour sa dernière mouture).
"use strict";
$(function() {
    $(".youtube").each(function() {
        // Based on the YouTube ID, we can easily find the thumbnail image
        $(this).css('background-image', 'url(http://i.ytimg.com/vi/' + this.id + '/sddefault.jpg)');

        // Overlay the Play icon to make it look like a video player
        $(this).append($('<div/>', {'class': 'play'}));

        // accessibility handling: click on mouse left button (keycode 1) or [Return] key (keycode 13)
        $(document).delegate('#'+this.id, 'click keydown', function(event) {
            if (event.which == 1 || event.which == 13) {
                event.preventDefault();

                // Create an iFrame with autoplay set to true
                var iframe_url = "https://www.youtube.com/embed/" + this.id + "?autoplay=1&autohide=1";
                if ($(this).data('params')) iframe_url+='&'+$(this).data('params');

                // The height and width of the iFrame should be the same as parent
                var iframe = $('<iframe/>', {'frameborder': '0', 'src': iframe_url, 'width': $(this).width(), 'height': $(this).height() })

                // Replace the YouTube thumbnail with YouTube HTML5 Player
                $(this).replaceWith(iframe);
            }
        });
    });
});

Voir le Pen Faster YouTube embeds (jQuery style) par Alexis Ulrich (@mancko) sur CodePen.



Résultats


Parlons maintenant de ce que vous pouvez gagner en situation réelle.

Cette solution a été implémentée sur la page « Les Cantiques de Sainte Marie et le galaïco-portugais », un article contenant trois vidéos YouTube. Voici les résultats :
  • Avant d’implémenter cette solution, nous avions 20 requêtes HTTP, 636,2ko de contenu téléchargé, ce qui prenait 2,22s (3,59s onload)
  • Une fois implémentée, nous sommes descendus à 17 requêtes HTTP, 370,7ko de contenu, et un temps de chargement de 1,05s (733ms onload)
  • Cela signifie 15% de requêtes en moins, une page 41% plus légère et 52% plus rapide (80% plus rapide sur le onload)

Les résultats sont déjà bons même avec une seule vidéo YouTube, comme sur la page « Étymologie du yoga », notre second exemple. En voici les résultats :
  • Avant d’implémenter cette solution, nous avions 20 requêtes HTTP, 684,4ko de contenu téléchargé, ce qui prenait 2,13s (2,14s onload)
  • Une fois implémentée, nous sommes descendus à 17 requêtes HTTP, 322,4ko de contenu, et un temps de chargement de 1,24s (975ms onload)
  • Cela signifie 15% de requêtes en moins, une page 53% plus légère et 42% plus rapide (54% plus rapide sur le onload)

Conclusion


Je pense que nous sommes tous d’accord pour dire que réduire le poids d’une page de 40% à 50% vaut bien un peu de travail, n’est-ce pas ?

Si vous avez des idées pour améliorer ce code, vous pouvez le forker sur CodePen (javascript classique, version jQuery). Vos commentaires sont aussi les bienvenus, soit sur ce blog, soit sur l’article publié sur Sitepoint.

Crédit photo : Miroir de Cendres

Acelere sus páginas con vídeos de YouTube (en espagnol)
Acelere suas páginas com vídeos YouTube (en portugais)

Aucun commentaire:

Enregistrer un commentaire