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.
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).
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.
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 :
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 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].
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.
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).
Le mode strict de l’ECMAScript5 aide à écrire un code javascript plus portable. Pour l’activer, le code javascript doit commencer par :
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).
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 :
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 :
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.
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)
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)
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