【JavaScript】モーダルウィンドウを開いて、YouTubeを再生する
2025年09月16日
WEBサイト制作
- #HTML
- #css
- #JavaScript
こんにちは!
上毛印刷WEB制作担当のソーヤです。
今回は需要が多いであろう、モーダルウィンドウとYouTube埋め込み動画の合せ技tipsを紹介します。
実装例が少し特殊なので、デモページを作成しました。
アクセスしてみてください。
デモページはこちら(別タブで開きます)
HTML例
今回はYouTube Player APIを使います。
◆公式ドキュメントはコチラ(外部サイトに遷移します)
まず<head>内に下記を記述してください。
<script src="https://www.youtube.com/iframe_api"></script>
そうしたら、準備完了。
HTMLを記述してください。
<div class="item">
<img class="thumb js-movie" src="./thumbnail01.jpg" data-video="mp-auQGeJEE" alt="thumb1">
<div class="c-modal">
<div class="c-modal__container">
<div class="c-modal__inner">
<div class="c-modal__contents">
<div class="js-player movie-player"></div>
<div class="c-modal__button__wrap">
<button class="c-modal__button" type="button" aria-label="閉じる">✕</button>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="item">
<img class="thumb js-movie" src="./thumbnail02.jpg" data-video="7bKrMeZRbgw" alt="thumb2">
<div class="c-modal">
<div class="c-modal__container">
<div class="c-modal__inner">
<div class="c-modal__contents">
<div class="js-player movie-player"></div>
<div class="c-modal__button__wrap">
<button class="c-modal__button" type="button" aria-label="閉じる">✕</button>
</div>
</div>
</div>
</div>
</div>
</div>
data-video属性に動画のIDを記述してください。
CSS例
.c-modal {
-webkit-overflow-scrolling: touch;
-webkit-backface-visibility: hidden;
display: none;
z-index: 9999;
position: fixed;
top: -10px;
right: 0;
bottom: -10px;
left: 0;
overflow: hidden;
overflow-y: auto;
backface-visibility: hidden;
background-color: rgba(0, 0, 0, 0.45);
}
.c-modal__container {
display: table;
min-width: 500px;
min-height: 500px;
width: 100%;
height: 100%;
padding: 10px 0;
}
.c-modal__inner {
display: table-cell;
padding: 2.7em 2em;
vertical-align: middle;
}
.c-modal__contents {
-webkit-box-shadow: 0 1px 5px rgba(0, 0, 0, 0.2);
position: relative;
max-width: 1300px;
margin: 0 auto;
padding: 30px;
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.2);
text-justify: inter-ideograph;
padding-bottom: 53.5%;
}
.js-player{
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.c-modal__contents::after {
display: table;
clear: both;
content: '';
}
.c-modal__contents > p {
line-height: 1.7;
text-indent: 1em;
}
.c-modal__button__wrap {
position: absolute;
top: 20px;
right: 20px;
}
.c-modal__button {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
display: inline-block;
position: relative;
width: 24px;
height: 24px;
border: none;
outline: none;
background: transparent;
line-height: 1;
text-indent: -9999px;
vertical-align: middle;
cursor: pointer;
}
.c-modal__button::before {
position: absolute;
top: -45px;
left: 10px;
width: 35px;
height: 3px;
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
background-color: #fff;
content: '';
cursor: pointer;
}
.c-modal__button::after {
position: absolute;
top: -45px;
left: 10px;
width: 35px;
height: 3px;
transform: rotate(-45deg);
background-color: #fff;
content: '';
cursor: pointer;
}
JavaScript例
(function(){
const players = {};
let activeModal = null;
function whenYouTubeReady(cb) {
if (window.YT && window.YT.Player) return cb();
if (!window.__ytApiHooked) {
const orig = window.onYouTubeIframeAPIReady;
window.onYouTubeIframeAPIReady = function() {
if (typeof orig === 'function') orig();
document.dispatchEvent(new Event('youtube-api-ready'));
};
window.__ytApiHooked = true;
}
document.addEventListener('youtube-api-ready', function handler() {
document.removeEventListener('youtube-api-ready', handler);
cb();
});
}
document.addEventListener('DOMContentLoaded', function() {
const html = document.documentElement;
const body = document.body;
const header = document.querySelector('header');
document.querySelectorAll('.js-movie').forEach(function(img){
img.addEventListener('click', function() {
const youtubeId = this.dataset.video;
if (!youtubeId) return;
const item = this.closest('.item');
if (!item) return;
let modal = item.querySelector('.c-modal');
if (!modal) return;
let playerContainer = modal.querySelector('.js-player');
if (!playerContainer) {
const contents = modal.querySelector('.c-modal__contents') || modal;
playerContainer = document.createElement('div');
playerContainer.className = 'js-player movie-player';
contents.insertBefore(playerContainer, contents.firstChild);
}
openModal(modal, youtubeId, playerContainer);
});
});
document.querySelectorAll('.c-modal').forEach(function(modal){
modal.addEventListener('click', function(e){
if (!e.target.closest('.c-modal__contents')) {
closeModal(modal);
}
});
const btn = modal.querySelector('.c-modal__button');
if (btn) {
btn.addEventListener('click', function(){ closeModal(modal); });
}
});
function openModal(modal, youtubeId, playerContainer) {
if (activeModal && activeModal !== modal) closeModal(activeModal);
activeModal = modal;
const sbw = window.innerWidth - document.documentElement.clientWidth;
if (sbw) html.style.paddingRight = sbw + 'px';
html.style.overflow = 'hidden';
body.style.overflow = 'hidden';
if (header) header.style.display = 'none';
modal.style.display = 'block';
requestAnimationFrame(function(){ modal.style.opacity = '1'; });
let touchStartY = 0;
function onTouchStart(e){ touchStartY = e.changedTouches[0].screenY; }
function onTouchMove(e){
const currentY = e.changedTouches[0].screenY;
const height = modal.offsetHeight;
const isTop = touchStartY <= currentY && modal.scrollTop === 0; const isBottom = touchStartY >= currentY && (modal.scrollHeight - modal.scrollTop === height);
if (isTop || isBottom) e.preventDefault();
}
modal._touchStart = onTouchStart;
modal._touchMove = onTouchMove;
modal.addEventListener('touchstart', onTouchStart, {passive:false});
modal.addEventListener('touchmove', onTouchMove, {passive:false});
if (!playerContainer.id) playerContainer.id = 'player-' + youtubeId;
if (!players[youtubeId]) {
whenYouTubeReady(function(){
players[youtubeId] = new YT.Player(playerContainer.id, {
height: '100%',
width: '100%',
videoId: youtubeId,
playerVars: { autoplay: 1, rel: 0, playsinline: 1 },
events: {
onReady: function(e){ e.target.playVideo(); }
}
});
});
} else {
try {
const iframe = players[youtubeId].getIframe();
if (iframe && iframe.parentNode !== playerContainer) {
playerContainer.appendChild(iframe);
}
} catch (err) {
console.warn('iframe move failed', err);
}
try { players[youtubeId].playVideo(); } catch(e){}
}
}
function closeModal(modal) {
if (!modal) return;
const item = modal.closest('.item');
const img = item ? item.querySelector('.js-movie') : null;
const youtubeId = img ? img.dataset.video : null;
if (youtubeId && players[youtubeId] && typeof players[youtubeId].pauseVideo === 'function') {
try { players[youtubeId].pauseVideo(); } catch(e){}
}
if (modal._touchStart) modal.removeEventListener('touchstart', modal._touchStart);
if (modal._touchMove) modal.removeEventListener('touchmove', modal._touchMove);
delete modal._touchStart; delete modal._touchMove;
modal.style.opacity = '0';
setTimeout(function(){ modal.style.display = 'none'; }, 300);
html.removeAttribute('style');
body.removeAttribute('style');
if (header) header.style.display = '';
if (activeModal === modal) activeModal = null;
}
});
})();
まとめ
今回もjQueryではなく、Vanilla.jsで実装してみました。
コピペしてガンガン使ってください!
この記事に対するご意見・ご感想・ご質問等ありましたら、
ぜひ下記フォームにてお送りください。