Hier möchte ich Euch nun eine Variante vorstellen, um YouTube-Videos auf Eurer Website einzubinden.
Gerade wenn man mehrere YouTube-Videos auf einer Seite zur Verfügung stellen möchte, erkennt man schnell wie viel Daten von YouTube nachgeladen werden und das ohne auch nur ein Video gestartet zu haben. Einige von Euch möchten auch gerne den Besucher der Website darauf aufmerksam machen, dass externer Inhalt abgespielt wird und dadurch die Abrufdaten an YouTube gehen.
In dieser Variante habe ich das YouTube-iFrame, was in den meisten Implementierungen verwendet wird, durch ein DIV ersetzt, dieses bekommt das YouTube Thumbnail-/Poster-Bild als Hintergrund-Bild, welches durch das JavaScript von YouTube geladen wird oder noch besser welches man zuvor lokal auf den Server ablegt und in dem Attribut data-poster des DIV übergibt.
Passende Formate sind leicht durch einmaligen Download zu erhalten, folgende Möglichkeiten stehen zur Verfügung:
- Hohe Qualität: https://img.youtube.com/vi/[youtube-video-id]/hqdefault.jpg
- Medium Qualität: https://img.youtube.com/vi/[youtube-video-id]/mqdefault.jpg
- Standard Qualität: https://img.youtube.com/vi/[youtube-video-id]/sddefault.jpg
- Maximale Auflösung: https://img.youtube.com/vi/[youtube-video-id]/maxresdefault.jpg
- andere Formate wie WebP existieren auch, werden vom IE-11 allerdings nicht unterstützt
standard quality: https://i.ytimg.com/vi_webp/[youtube-video-id]/sddefault.webp
Im data-src Attribute muss die YouTube iFrame src komplett oder auch nur die YouTube Video-Id angegeben werden, über JavaScript wird sowieso nur die Id verwendet, egal ob für das Video als auch für das Thumbnail-/Poster-Bild.
Des Weiteren wird ein zentrierter Play-Button erzeugt, der wie der Play-Button im YouTube Player aussieht, zu dem kann durch das title-Attribut der Video-Titel eingeblendet werden und mit dem Attribut data-external ein Hinweistext eingeblendet werden, das externe Resourcen von YouTube nachgeladen werden.
Durch das Setzen des Attributes data-target="popup" ist es auch möglich das Video in einem simplen Popup anzuzeigen in der auf die Browser-Fenster Abmessungen angepassten Qualität. Dieses Popup startet das Video unmittelbar nach der Einblendung, nach Beendigung des Videos schließt sich das Popup automatisch.
Die YouTube Player API wird durch diese Implementierung erst beim Start eines Videos geladen, wie auch das Video, wird ein zweites Mal ein Video gestartet, wird die API nicht erneut geladen.
Natürlich werden die Videos von YouTube ohne Cookies eingebunden (https://www.youtube-nocookie.com) und durch Setzen des Meta-Tags referrer mit dem Wert no-referrer wird die Weitergabe des Referrers unterbunden.
Bei allen versuchen die Daten der Website-Besucher zu schützen, sorgen die YouTube Scripte natürlich dafür die Information in welcher Seite das Video eingebettet ist, an YouTube weiterzureichen (https://www.youtube-nocookie.com/embed/ScMzIvxBSi4?enablejsapi=1&origin=https://www.gocher.me&widgetid=1).
HTML:
<!DOCTYPE html >
<html lang="de">
<head>
<meta charset="utf-8" />
<title>External click YouTube</title>
<link rel="stylesheet" href="youtube.css" />
<meta http-equiv="Content-Security-Policy" content="default-src 'self' https://fonts.gstatic.com https://www.youtube-nocookie.com/; script-src 'self' https://www.youtube.com/; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://img.youtube.com https://i.ytimg.com/ googlevideo.com" />
</head>
<body>
<div class="youtube" title="YouTube video player" data-external="true" data-poster="/media/youtube_poster/bHQqvYy5KYo.jpg" data-src="https://www.youtube.com/embed/bHQqvYy5KYo?origin=https%3A%2F%2Fdevelopers.google.com&autohide=1&showinfo=0&video-id=bHQqvYy5KYo&enablejsapi=1&widgetid=1"></div>
<div class="youtube" title="YouTube placeholder video" data-external="true" data-poster="/media/youtube_poster/ScMzIvxBSi4.jpg" data-src="ScMzIvxBSi4"></div>
<div class="youtube" title="YouTube placeholder video POPUP" data-external="true" data-target="popup" data-poster="/media/youtube_poster/ScMzIvxBSi4.jpg" data-src="ScMzIvxBSi4"></div>
<script src="youtube.js"></script>
</body>
</html>
JavaScript:
// coding: utf-8
/** Created by: Udo Schmal | https://www.gocher.me/ */
(function () {
'use strict';
function youtube(el) {
var player, id;
// don't send referrer infos to external services
if (!document.querySelector('meta[name="referrer"]')) {
let meta = document.createElement('meta');
meta.name = 'referrer';
meta.content = 'no-referrer';
document.head.appendChild(meta);
}
// get YouTube video id
if (el.dataset.src && el.dataset.src.length === 11) {
id = el.dataset.src;
} else {
let src = el.dataset.src;
let regExp = /^.*((youtube\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#&?]*).*/;
let match = src.match(regExp);
id = (match && match[7].length == 11) ? match[7] : src;
}
el.id = id;
// show in popup, other defined target or replace el with player
var target = el.dataset.target || id;
// create placeholder background image
if (el.dataset.poster) {
el.style.backgroundImage = "url('" + el.dataset.poster + "')";
} else {
// high quality: https://img.youtube.com/vi/<insert-youtube-video-id-here>/hqdefault.jpg
// medium quality: https://img.youtube.com/vi/<insert-youtube-video-id-here>/mqdefault.jpg
// standard quality: https://img.youtube.com/vi/<insert-youtube-video-id-here>/sddefault.jpg
// maximum resolution: https://img.youtube.com/vi/<insert-youtube-video-id-here>/maxresdefault.jpg
//el.style.backgroundImage = "url('https://i.ytimg.com/vi_webp/" + id + "/sddefault.webp')"; // not supported by IE11
el.style.backgroundImage = "url('https://img.youtube.com/vi/" + id + "/sddefault.jpg')";
}
// show placeholder title
if (el.title) {
let title = document.createElement('div');
title.className = 'title';
title.appendChild(document.createTextNode(el.title));
el.appendChild(title);
}
// show placeholder external message
if (el.dataset.external) {
let external = document.createElement('div');
external.className = 'external';
let language = window.navigator.userLanguage || window.navigator.language;
if (!language) {
language = 'en';
}
let txt;
switch (language.substr(0,2)) {
case 'de': txt = 'Lädt externe Inhalte von YouTube'; break;
case 'fr': txt = 'Charger du contenu externe à partir de YouTube'; break;
case 'it': txt = 'Carica contenuti esterni da YouTube'; break;
case 'es': txt = 'Cargar contenido externo de YouTube'; break;
default: txt = 'Load external Content from YouTube'; break;
}
external.appendChild(document.createTextNode(txt));
el.appendChild(external);
}
// show placeholder youtube like play button
var play = document.createElement('div');
play.className = 'play';
el.appendChild(play);
// create popup
function createPopup() {
var overlay = document.createElement('div');
overlay.className = 'overlay';
document.body.appendChild(overlay);
var content = document.createElement('div');
content.className = 'popup-content';
content.id = 'popup-content';
overlay.appendChild(content);
var close = document.createElement('span');
close.className = 'popup-close';
overlay.appendChild(close);
close.addEventListener('click', function (event) {
player.stopVideo();
document.body.removeChild(overlay);
});
return content.id;
}
// youtube player API
function onPlayerReady(event) {
event.target.playVideo();
}
function onPlayerStateChange(event) {
if (event.data == window.YT.PlayerState.ENDED) {
player.stopVideo();
document.body.removeChild(overlay);
}
}
// create youtube player
function createPlayer() {
window.YT.ready(function() {
var dest, obj = {
videoId: id,
host: 'https://www.youtube-nocookie.com',
events: {
'onReady': onPlayerReady
}
};
var sizes = [
{ width: '426', height: '240' },
{ width: '640', height: '360' },
{ width: '854', height: '480' },
{ width: '1280', height: '720' },
{ width: '1920', height: '1080' }
];
if (target == 'popup') {
dest = createPopup();
obj.events['onStateChange'] = onPlayerStateChange;
for(var i = 0; i < sizes.length; i++) {
if (sizes[i].width <= window.innerWidth) {
obj.height = sizes[i].height;
obj.width = sizes[i].width;
} else {
break;
}
}
} else {
dest = target;
for(var i = 0; i < sizes.length; i++) {
if (sizes[i].width >= el.offsetWidth) {
obj.height = sizes[i].height;
obj.width = sizes[i].width;
break;
}
}
}
// create YouTube player
player = new window.YT.Player(dest, obj);
});
}
// load youtube external content
function load() {
// init once
if (!document.getElementById('YouTubeApi')) {
// load YouTube API
var script = document.createElement('script');
script.id = 'YouTubeApi';
script.src = 'https://www.youtube.com/iframe_api';
document.head.appendChild(script);
script.addEventListener('load', createPlayer);
} else {
createPlayer();
}
if (target !== 'popup') {
// remove placeholder click event
play.removeEventListener('click', load);
}
}
play.addEventListener('click', load);
}
var media = document.querySelectorAll('.youtube[data-src]');
for (let i=0; i < media.length; i++) {
new youtube(media[i]);
}
})();
Sind viele Videos auf der Seite kann auch lazy loading Sinn machen, kombiniert würde lazy loading und YouTube in diesem Fall natürlich noch effektiver sein youtube_lazy.js. (9,28 kByte) 13.07.2021 11:07
StyleSheet:
/* coding: utf-8 */
div.youtube {
position: relative;
background-repeat: no-repeat;
background-size: cover;
background-position: center;
width: 100%;
height: 0;
padding-top: 56.25%;
margin: 5px;
float: left;
}
div.youtube .title, div.youtube .external {
font-family: Arial, Helvetiva, sans-serif;
text-align: center;
}
div.youtube .title {
position: absolute;
top: 15px;
width: 100%;
color: #fff;
text-shadow: 1px 1px 2px black, 0 0 1em black, 0 0 0.2em black;
}
div.youtube .external {
position: absolute;
top: 30%;
width: 100%;
color: red;
text-shadow: 1px 1px 2px white, 0 0 1em white, 0 0 0.2em white;
}
div.youtube .play {
background: #333;
border-radius: 50% / 10%;
color: #fff;
font-size: 2em;
height: 1.5em;
width: 2em;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
cursor: pointer;
}
div.youtube .play:hover {
background: red;
}
div.youtube .play::before {
background: inherit;
border-radius: 5% / 50%;
bottom: 9%;
content: "";
left: -5%;
position: absolute;
right: -5%;
top: 9%;
}
div.youtube .play::after {
border-style: solid;
border-width: 1em 0 1em 1.732em;
border-color: transparent transparent transparent rgba(255, 255, 255, 0.75);
content: " ";
font-size: 0.35em;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 0;
}
.overlay {
z-index: 100;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,.8);
}
.popup-content {
position: absolute;
top: 50%;
left: 50%;
max-width: 100%;
transform: translate(-50%, -50%);
}
.popup-close {
position: absolute;
top: 30px;
right: 30px;
cursor: pointer;
}
.popup-close::before {
display: inline-block;
content: "\00d7";
color: #fff;
font-size: 42px;
font-weight: bold;
text-shadow: 1px 1px 2px black, 0 0 1em black, 0 0 0.2em black;
cursor: pointer;
}