forked from paulirish/lite-youtube-embed
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
210 lines (185 loc) · 8.35 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
<!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" />
</head>
<body>
<h1><code>lite-youtube</code> custom element</h1>
<style>
lite-youtube {
width: 560px;
height: 315px;
background-color: #000;
position: relative;
contain: strict;
display: block;
background-position: center center;
background-size: cover;
cursor: pointer;
}
/* gradient */
lite-youtube::before {
content: '';
display: block;
position: absolute;
top: 0;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAADGCAYAAAAT+OqFAAAAdklEQVQoz42QQQ7AIAgEF/T/D+kbq/RWAlnQyyazA4aoAB4FsBSA/bFjuF1EOL7VbrIrBuusmrt4ZZORfb6ehbWdnRHEIiITaEUKa5EJqUakRSaEYBJSCY2dEstQY7AuxahwXFrvZmWl2rh4JZ07z9dLtesfNj5q0FU3A5ObbwAAAABJRU5ErkJggg==);
background-position: top;
background-repeat: repeat-x;
height: 60px;
padding-bottom: 50px;
width: 100%;
transition: all 0.2s cubic-bezier(0, 0, 0.2, 1);
}
/* play button */
lite-youtube .lty-playbtn {
width: 70px;
height: 46px;
background-color: #212121;
z-index: 1;
opacity: 0.8;
border-radius: 14%; /* TODO: Consider replacing this with YT's actual svg. Eh. */
transition: all 0.2s cubic-bezier(0, 0, 0.2, 1);
}
lite-youtube:hover .lty-playbtn {
background-color: #f00;
opacity: 1;
}
/* play button triangle */
lite-youtube .lty-playbtn:before {
content: '';
border-style: solid;
border-width: 11px 0 11px 19px;
border-color: transparent transparent transparent #fff;
}
lite-youtube .lty-playbtn,
lite-youtube .lty-playbtn:before {
position: absolute;
top: 50%;
left: 50%;
transform: translate3d(-50%, -50%, 0);
}
/* Post-click styles */
lite-youtube.lyt-activated {
cursor: unset;
}
lite-youtube.lyt-activated::before,
lite-youtube.lyt-activated .lty-playbtn {
opacity: 0;
pointer-events: none;
}
</style>
<lite-youtube data-videoid="ogfYd705cRs"></lite-youtube>
<script>
/**
* A lightweight youtube embed. Still should feel the same to the user, just MUCH faster to initialize and paint.
*
* Thx to these as the inspiration
* https://storage.googleapis.com/amp-vs-non-amp/youtube-lazy.html
* https://autoplay-youtube-player.glitch.me/
*
* Once built it, I also found these:
* https://github.com/ampproject/amphtml/blob/master/extensions/amp-youtube (👍👍)
* https://github.com/Daugilas/lazyYT
* https://github.com/vb/lazyframe
*/
class LiteYTEmbed extends HTMLElement {
constructor() {
super();
// Gotta encode the untrusted value
// https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#rule-2---attribute-escape-before-inserting-untrusted-data-into-html-common-attributes
this.videoId = encodeURIComponent(this.dataset.videoid);
/**
* Lo, the youtube placeholder image! (aka the thumbnail, poster image, etc)
* There is much internet debate on the reliability of thumbnail URLs. Weak consensus is that you
* cannot rely on anything and have to use the YouTube Data API.
*
* amp-youtube also eschews using the API, so they just try sddefault with a hqdefault fallback:
* https://github.com/ampproject/amphtml/blob/6039a6317325a8589586e72e4f98c047dbcbf7ba/extensions/amp-youtube/0.1/amp-youtube.js#L498-L537
* For now I'm gonna go with this confident (lol) assersion: https://stackoverflow.com/a/20542029, though I'll use `i.ytimg` to optimize for origin reuse.
*
* Worth noting that sddefault is _higher_ resolution than hqdefault. Naming is hard. ;)
* From my own testing, it appears that hqdefault is ALWAYS there sddefault is missing for ~10% of videos
*
* TODO: Do the sddefault->hqdefault fallback
* - When doing this, apply referrerpolicy (https://github.com/ampproject/amphtml/pull/3940)
* TODO: Consider using webp if supported, falling back to jpg
*/
this.posterUrl = `https://i.ytimg.com/vi/${this.videoId}/hqdefault.jpg`;
// Warm the connection for the poster image
LiteYTEmbed.addPrefetch('preload', this.posterUrl, 'image');
// TODO: support dynamically setting the attribute via attributeChangedCallback
}
connectedCallback() {
this.style.backgroundImage = `url("${this.posterUrl}")`;
const playBtn = document.createElement('div');
playBtn.classList.add('lty-playbtn');
this.append(playBtn);
// On hover (or tap), warm up the TCP connections we're (likely) about to use.
this.addEventListener('pointerover', LiteYTEmbed.warmConnections, {once: true});
// Once the user clicks, add the real iframe and drop our play button
// 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', e => this.addIframe());
}
// // TODO: Support the the user changing the [videoid] attribute
// attributeChangedCallback() {
// }
/**
* Add a <link rel={preload | preconnect} ...> to the head
*/
static addPrefetch(kind, url, as) {
const linkElem = document.createElement('link');
linkElem.rel = kind;
linkElem.href = url;
if (as) {
linkElem.as = as;
}
linkElem.crossorigin = true;
document.head.append(linkElem);
}
/**
* Begin preconnecting to warm up the iframe load
* Since the embed's netwok requests load within its iframe,
* preload/prefetch'ing them outside the iframe will only cause double-downloads.
* So, the best we can do is warm up a few connections to origins that are in the critical path.
*
* Maybe `<link rel=preload as=document>` would work, but it's unsupported: http://crbug.com/593267
* But TBH, I don't think it'll happen soon with Site Isolation and split caches adding serious complexity.
*/
static warmConnections() {
if (LiteYTEmbed.preconnected) return;
// The iframe document and most of its subresources come right off youtube.com
LiteYTEmbed.addPrefetch('preconnect', 'https://www.youtube.com');
// The botguard script is fetched off from google.com
LiteYTEmbed.addPrefetch('preconnect', 'https://www.google.com');
// Not certain if these ad related domains are in the critical path. Could verify with domain-specific throttling.
LiteYTEmbed.addPrefetch('preconnect', 'https://googleads.g.doubleclick.net');
LiteYTEmbed.addPrefetch('preconnect', 'https://static.doubleclick.net');
LiteYTEmbed.preconnected = true;
}
addIframe(){
// https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#rule-2---attribute-escape-before-inserting-untrusted-data-into-html-common-attributes
const escapedVideoId = encodeURIComponent(this.videoId);
const iframeHTML = `
<iframe width="560" height="315" frameborder="0"
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen
src="https://www.youtube.com/embed/${escapedVideoId}?autoplay=1"
></iframe>`;
this.insertAdjacentHTML('beforeend', iframeHTML);
this.classList.add('lyt-activated');
}
}
// Register custom element
customElements.define('lite-youtube', LiteYTEmbed);
//# sourceURL=./customelem.js
</script>
<h3>View isolated demos:</h3>
<ul>
<li><a href="./variants/solo.html">lite-youtube-embed</a>
<li><a href="./variants/yt.html">normal youtube embed</a>
</ul>
</body>
</html>