How to embed YouTube video in a webpage with a custom play-button, original poster at the best resolution and responsive container, keeping aspect ratio
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

README.md 5.9KB

3 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. # styled-youtube-embedding-labwork
  2. > Embed YouTube video in a webpage with a custom play-button, original poster at the best resolution and responsive container, keeping aspect ratio.
  3. [Try it](https://chiefred.github.io/styled-youtube-embedding-labwork/) in action!
  4. Often we need to embed a YouTube video in a custom design (with a custom play button), but for that purpose, we only have the URL of that video. Let`s disassemble the task in several steps.
  5. ## Get \<id-of-video\> from URL
  6. YouTube video URLs can be provided in several formats, like:
  7. - https://youtu.be/jIHvgUAW5vE
  8. - https://youtu.be/jIHvgUAW5vE?t=2
  9. - https://www.youtube.com/embed/jIHvgUAW5vE
  10. - https://www.youtube.com/watch?v=jIHvgUAW5vE&ab_channel=MihaEr%C5%BEen
  11. In all the examples presented, the desired video identifier will be `jIHvgUAW5vE`. So, we need a way to extract it.
  12. In most cases, we will do this on the server side by regular expression and even in combination with check: if we have a valid YouTube video URL (of any format), then we can, for example, insert its poster image into the page.
  13. I will use PHP:
  14. ```php
  15. <? if (
  16. preg_match(
  17. '/[\/\=]{1}([a-zA-Z0-9_-]{11})([\?\&]{1}|$)/',
  18. $anyYouTubeVideoURL,
  19. $matches
  20. )
  21. ): ?>
  22. <img
  23. src="https://img.youtube.com/vi/<?=$matches[1]?>/maxresdefault.jpg"
  24. alt="video"
  25. />
  26. <? endif ?>
  27. ```
  28. ## Get the poster image for YouTube video
  29. As already shown above, poster image can be loaded from `img.youtube.com`. Best resolution images available at URLs like:
  30. ```html
  31. https://img.youtube.com/vi/<id-of-video>/maxresdefault.jpg</id-of-video>
  32. ```
  33. But in some cases they may not exist (when the original video was in low resolution).
  34. We can find the following variations of the image file names used:
  35. - maxresdefault
  36. - mqdefault
  37. - sddefault
  38. - hqdefault
  39. - default
  40. And we need to determine if the image exists or not. With the 404 error response, YouTube also transmits a default placeholder image, which prevents the `img` tag's `onerror` event handler from being called. Thus, we can only check the "natural dimensions" of the resulting image.
  41. The idea is to try to load the highest resolution image (`maxresdefault.jpg`) and test the result with onload script:
  42. ```html
  43. <img
  44. src="https://img.youtube.com/vi/<id-of-video>/maxresdefault.jpg"
  45. onload="window.youtube_img_load_check(this)"
  46. alt="video"
  47. />
  48. ```
  49. But before the `img` tag, we must register the `youtube_img_load_check` function in the `head` section of the web page:
  50. ```js
  51. window.youtube_img_load_check = function (e) {
  52. var thumbnail = [
  53. "maxresdefault",
  54. "mqdefault",
  55. "sddefault",
  56. "hqdefault",
  57. "default",
  58. ];
  59. var url = e.getAttribute("src");
  60. if (e.naturalWidth === 120 && e.naturalHeight === 90) {
  61. for (var i = 0, len = thumbnail.length - 1; i < len; i++) {
  62. if (url.indexOf(thumbnail[i]) > 0) {
  63. e.setAttribute("src", url.replace(thumbnail[i], thumbnail[i + 1]));
  64. break;
  65. }
  66. }
  67. }
  68. };
  69. ```
  70. If loading `maxresdefault.jpg` fails, the script will try the next option from the array. Etc.
  71. The default YouTube stub image size is 120 x 90 pixels. Thus, we can detect errors when checking the `naturalWidth` and `naturalHeight` of the resulting image.
  72. ## Preserve the aspect ratio of the poster image in a responsive design
  73. The YouTube documentation says, "The standard aspect ratio for YouTube on a computer is 16:9". And most videos are in this format.
  74. As I found, even videos with a 4:3 aspect ratio in most cases have a poster in 16:9 format, until `default.jpg` which 120x90 (same as 404 error image).
  75. Thus, we cannot determine the aspect ratio of the video when measuring the loaded poster. That's why I just think all YouTube videos in 16:9 format. The result for my cases is acceptable. Here is the layout.
  76. HTML:
  77. ```html
  78. <div class="youtube-video">
  79. <div class="youtube-video__aspect">
  80. <div class="youtube-video__wrapper">
  81. <img
  82. class="youtube-video__poster"
  83. src="https://img.youtube.com/vi/<id-of-video>/maxresdefault.jpg"
  84. onload="window.youtube_img_load_check(this)"
  85. alt="video"
  86. loading="lazy"
  87. />
  88. <div
  89. class="youtube-video__play-icon"
  90. data-link="https://www.youtube.com/embed/<id-of-video>"
  91. ></div>
  92. </div>
  93. </div>
  94. </div>
  95. ```
  96. CSS:
  97. ```css
  98. .youtube-video {
  99. width: 100%;
  100. }
  101. .youtube-video__aspect {
  102. width: 100%;
  103. height: 0;
  104. position: relative;
  105. padding-top: 56.25%; /* This line gives 16:9 aspect ratio */
  106. }
  107. .youtube-video__poster {
  108. width: 100%;
  109. height: 100%;
  110. position: absolute;
  111. top: 0;
  112. left: 0;
  113. }
  114. .youtube-video__wrapper {
  115. /* Needed to properly resize video */
  116. width: 100%;
  117. height: 100%;
  118. position: absolute;
  119. top: 0;
  120. left: 0;
  121. display: flex;
  122. justify-content: center;
  123. align-items: center;
  124. }
  125. .youtube-video__play-icon {
  126. ...;
  127. }
  128. .youtube-video__iframe {
  129. /* Needed to properly resize video */
  130. width: 100%;
  131. height: 100%;
  132. }
  133. ```
  134. ## Launch YouTube video player
  135. One final piece of the puzzle - we need to replace the poster image with a real YouTube player when the user hits the play button. This JS can be placed at the bottom of the page.
  136. ```js
  137. const videos = document.querySelectorAll(
  138. ".youtube-video .youtube-video__play-icon"
  139. );
  140. videos.forEach(function (video) {
  141. video.addEventListener("click", function (e) {
  142. const link = e.target.dataset.link || null;
  143. const parent = e.target.closest(".youtube-video__wrapper");
  144. if (link && parent) {
  145. parent.classList.add("loading");
  146. parent.innerHTML =
  147. '<iframe class="youtube-video__iframe" src="' +
  148. link +
  149. '?autoplay=1" frameborder="0" allowfullscreen' +
  150. 'allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"' +
  151. "></iframe>";
  152. }
  153. });
  154. });
  155. ```
  156. You can optionally use the `.youtube-video__wrapper.loading` CSS selector to show the loading indicator.
  157. Now [try it](https://chiefred.github.io/styled-youtube-embedding-labwork/) in action!