Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use YT player API to force autoplay, resolving double-tap bug #109

Merged
merged 10 commits into from
Sep 11, 2022
1 change: 1 addition & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ <h3>View isolated demos:</h3>
<li><a href="./variants/pe.html">lite-youtube-embed - progressively enhanced</a>
<li><a href="./variants/custom-poster-image.html">lite-youtube-embed - custom poster image</a>
<li><a href="./variants/params.html">lite-youtube-embed - with parameters</a></li>
<li><a href="./variants/multiple-embeds.html">lite-youtube-embed - multiple embeds on same page</a>
<li><a href="./variants/yt.html">normal youtube embed</a>
</ul>
</body>
Expand Down
54 changes: 48 additions & 6 deletions src/lite-yt-embed.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,13 @@ class LiteYTEmbed extends HTMLElement {
// TODO: In the future we could be like amp-youtube and silently swap in the iframe during idle time
// We'd want to only do this for in-viewport or near-viewport ones: https://github.com/ampproject/amphtml/pull/5003
this.addEventListener('click', this.addIframe);
}

// // TODO: Support the the user changing the [videoid] attribute
// attributeChangedCallback() {
// }
// Chrome & Edge desktop have no problem with the basic YouTube Embed with ?autoplay=1
// However Safari desktop and most/all mobile browsers do not successfully track the user gesture of clicking through the creation/loading of the iframe,
// so they don't autoplay automatically. Instead we must load an additional 2 sequential JS files (1KB + 165KB) (un-br) for the YT Player API
// TODO: Try loading the the YT API in parallel with our iframe and then attaching/playing it. #82
this.needsYTApiForAutoplay = navigator.vendor.includes('Apple') || navigator.userAgent.includes('Mobi');
}

/**
* Add a <link rel={preload | preconnect} ...> to the head
Expand Down Expand Up @@ -96,13 +98,53 @@ class LiteYTEmbed extends HTMLElement {
LiteYTEmbed.preconnected = true;
}

addIframe(e) {
fetchYTPlayerApi() {
Copy link
Owner Author

@paulirish paulirish Nov 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alsoo wondering if there's a lazy way to attach... #82 hmmm!

EDIT: see also #146 (comment)

if (window.YT || (window.YT && window.YT.Player)) return;

this.ytApiPromise = new Promise((res, rej) => {
var el = document.createElement('script');
el.src = 'https://www.youtube.com/iframe_api';
el.async = true;
el.onload = _ => {
YT.ready(res);
};
el.onerror = rej;
this.append(el);
});
}

async addYTPlayerIframe(params) {
this.fetchYTPlayerApi();
await this.ytApiPromise;

const videoPlaceholderEl = document.createElement('div')
this.append(videoPlaceholderEl);

const paramsObj = Object.fromEntries(params.entries());
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

im a little scared this newish feature will pose problems with various people's tsc/acorn/estree versions, if they're too old. but hopefully its fine.


new YT.Player(videoPlaceholderEl, {
width: '100%',
videoId: this.videoId,
playerVars: paramsObj,
events: {
'onReady': event => {
event.target.playVideo();
}
}
});
}

async addIframe(){
if (this.classList.contains('lyt-activated')) return;
e.preventDefault();
this.classList.add('lyt-activated');

const params = new URLSearchParams(this.getAttribute('params') || []);
params.append('autoplay', '1');
params.append('playsinline', '1');
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


if (this.needsYTApiForAutoplay) {
return this.addYTPlayerIframe(params);
}

const iframeEl = document.createElement('iframe');
iframeEl.width = 560;
Expand Down
25 changes: 25 additions & 0 deletions variants/multiple-embeds.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>lite-youtube-embed</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="../src/lite-yt-embed.css" />
</head>
<body>
<h1>multiple embeds</h1>

<lite-youtube videoid="ogfYd705cRs" playlabel="Play: Keynote (Google I/O '18)"></lite-youtube>


<lite-youtube videoid="n57U2_-3NLQ" playlabel="Play: Chrome Dev Summit 2021 recap"></lite-youtube>


<script src="../src/lite-yt-embed.js"></script>

<!-- <script>
// Simulate duplicate iframe problem. https://github.com/paulirish/lite-youtube-embed/issues/92
document.body.addEventListener('click', e => e.target.click());
</script> -->
</body>
</html>
4 changes: 0 additions & 4 deletions variants/solo.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,5 @@ <h1><code>lite-youtube</code> custom element</h1>

<script src="../src/lite-yt-embed.js"></script>

<!-- <script>
// Simulate duplicate iframe problem. https://github.com/paulirish/lite-youtube-embed/issues/92
document.body.addEventListener('click', e => e.target.click());
</script> -->
</body>
</html>
6 changes: 3 additions & 3 deletions variants/yt.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
</head>
<body>

<h3>typical iframe embed</h3>
<h1>typical iframe embed</h1>
<iframe
width="560"
height="315"
width="720"
height="405"
src="https://www.youtube.com/embed/ogfYd705cRs"
frameborder="0"
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
Expand Down