読者です 読者をやめる 読者になる 読者になる

Webサイト専門プログラマの言いたい放題

元システムエンジニアがサイト制作とプログラミングについて好き放題しゃべります。

【スマホ向けYoutube埋め込み最適化のタネ証し】

f:id:wp-lesson:20170315214640j:plain

まず前提知識から。
 
全てのウェブページはHTMLタグと呼ばれる構文を使って掲載内容を定義し、
CSSと呼ばれる構文を使ってページ上の配置などを設定することによって
ブラウザの画面に表示されます。
 
ただし、Youtubeなど埋め込み型のコンテンツは自分のサイト以外のサーバーに置いてあるので、
自分のサイトにコピーすることは不可能です。
 
そこで、<iframe>というHTMLタグの登場です。
 
<iframe>は、自分のサイトのページに穴を開けて、
穴を通して指定した他サイトのページが見えるようにするというものです。
 
↓ iframeタグの解説ページ
http://www.htmq.com/html/iframe.shtml
 
Youtubeならこんな感じです。

<iframe width="640" height="360" src="●●"></iframe>

 
●●の部分に、Youtubeに置いてある動画のURLを入れます。
 
この、<iframe>タグで表示される部分をフレームと呼びます。
 
フレームの表示サイズは<iframe>タグのwidthとheightで横幅と縦幅を指定できるほか、
スタイルシートでも同じ指定ができます。
 
表示サイズを固定したい場合は上記のように、
widthとheightが付いた<iframe>タグを使います。
 

<iframe>タグの問題点

 
しかし、スマホで表示すると動画がブラウザの画面を
はみ出したり、縦と横のバランスが崩れたりします。
 
なぜでしょうか?
 
widthとheightでサイズを固定してしまっているからです。
 
スマホでも縦横が同じ比率で縮小した表示にするには、
widthとheightを固定してはいけないのです。
 
でも、固定しなかったらPCで見たとき崩れます。
どういたらいいのでしょうか?
 
幸いにも、横幅を指定するwidthには、ブラウザの
画面サイズを基準とする指定方法があります。
 
スタイルシートで width に「100%」と指定すれば、
<iframe>タグを囲む、1つ外側のタグの横幅いっぱいまで
動画のフレームが広がって表示されます。
 
ですから一般のテンプレートでは、
動画や地図の埋め込みに使われる<iframe>タグに、
widthを「100%」で指定するスタイルシート
あらかじめ組み込まれています。
 
すると、スマホで表示しても横幅はブラウザを
はみ出すことなくきちんと収まります。
 
しかし、縦幅は<iframe>タグに付いているheightの
設定値が有効なままなので、
 
「横幅と縦幅のバランスがおかしい・・・」
 
という見え方になってしまうのです。
 
 
どうやったら解決できるでしょうか?
 
そうですね。縦幅を表す height が、常に横幅と同じ比率になるように変化させればいいですね。
 

スマホでのフレームサイズを最適化する考え方

 
この例で考えてみましょう。

<iframe width="640" height="360" src="●●"></iframe>

横幅(width)に対する縦幅(height)の割合は、
 
height ÷ width = 360 ÷ 640 = 0.5625
 
なので、横幅を100とすると縦幅は56.25という比率になることがわかりますね。
 
じゃあ、スタイルシートでフレームの横幅に100% と指定したのなら、
縦幅は 56.25% と指定すれば、同じ比率を維持したまま
どんなスマホで見ても最適な表示になるのではないでしょうか?
 
ほぼ正解なのですが、落とし穴があります。
 
縦幅を表す height を % で指定した場合、
横幅に対する割合になってくれません。
 
フレームのwidth を % で指定した場合は、
フレームの1つ外側を囲むタグの横幅に対する割合になるので良いのですが、
 
height の % は、フレームの1つ外側を囲むタグの「縦幅」に対する割合になります。
ブラウザの画面幅に対する割合ではありません。
 
そのため、思った通りの表示になりません。
 
そこで、height ではなく padding を使います。
発想の転換です。
 
height は、そのタグ自身の高さという意味ですが、
paddingは、そのタグの内側に付ける余白の意味。
 
例えば、フレームの padding-top を 30px とすると、
フレームの上端から下へ30px空いた場所が動画の上端になります。
 
padding は height と違って、% で指定したとき、
そのタグ自身の横幅に対する割合になります。
 
ですから、フレームをすっぽり囲むタグを付けて、
そのタグに padding-top で 56.25% と指定すれば、
 
フレームを囲むタグの内側には、動画が入る高さに加えて、
フレームの横幅の 56.25% つまり 360px 分の余白が確保されることになります。
 
動画が入る高さを確保するために、
height が使えない代わりにpadding-topを使うのがポイントです。
 
そうすると、動画が入る高さはpadding-topで確保されているので、
height の分だけ余白を削らないといけません。
 
ですから、padding-top の指定に加えて、
フレームを囲むタグのheightを0に指定します。
 

フレームをレスポンシブに対応させよう

 
最後に、フレームを囲むタグのサイズいっぱいに動画が広がって表示されるように、
 
フレームを囲むタグには
position: relative;
 
フレームのタグには
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
 
を指定します。
 
まとめるとこうなりますね。
 
▼HTML

<div class="embed-youtube">
 <iframe width="640" height="360" src="●●"></iframe>
</div>

スタイルシート

.embed-youtube {
 position: relative;
 height: 0;
 padding-top: 56.25%;
}

.embed-youtube iframe {
 position: absolute;
 top: 0;
 left: 0;
 width: 100%;
 height: 100%;
}

どんな動画サイズにも自動でフィットさせよう

 
さらに汎用的な仕組みにしてしまいましょう。
 
このままでは、縦横の比率を 100:56.25 で埋め込むフレームでしか
正確な最適化になりませんね。
 
スタイルシートの padding-top: 56.25%; の数字が固定されてしまうからです。
 
なんとかして、HTMLに記述された

<iframe width="600" height="400" src="●●"></iframe>

の、width と height の数字の割合に応じた padding-top を設定できないものでしょうか?
 
この例だと、横幅に対する縦幅の比率は
400 ÷ 600 = 0.6666... なので、ほぼ 66.66% ですね。
 
ですから、スタイルシート
 
padding-top: 56.25%; ではなく、
 
padding-top: 66.66%; にしたいところです。
 
残念ながら、スタイルシートのファイル(.css)を自動的に書き替えることは不可能です。
 
しかし、ブラウザの画面には、PCのメモリに読み込まれたHTMLやスタイルシート
設定内容が反映されますので、メモリの内容を書き換えれば、ブラウザの画面の表示も変わります。
 
「そんなことできるの?」
 
と思われるかもしれませんが、できるのです。
 
スタイルシートのファイルが置いてあるサーバー上ではなくPCのメモリ上で、
つまり私たちのPCやスマホ上で動作するプログラムを使えば可能です。
 
それが、javascript というプログラム言語です。
 
javascript はサーバー上ではなく、私たちのPCやスマホ上で動作します。
 
javascript の主な役目は、ブラウザに読み込まれた
HTMLやスタイルシートの内容を(メモリ上で)書き換えることにあります。
 
あくまでもメモリ上での書き換えなので、
サーバーに置いてあるページのデータには影響しません。
 
何もこれは特別なことではなく、
そもそもブラウザでインターネット上のページを見る
という仕組みがそうなっているのです。
 
マンツーマンレッスンで『ウェブの仕組み』を
学ばれた方は「目からウロコが落ちた」と仰いますが、
それほど基本的なことを、私たちは見過ごしている
ということがよくわかります。
 
基本って、馬鹿にできません。
知ってるようで知らない大事なことが沢山あるんですね。
 
 
そして、javascriptで書いたプログラムが起動する
タイミングは、いくつかあって、
 
・ページが表示された瞬間
・ボタンがクリックされた瞬間
・マウスが乗った瞬間
・マウスが離れた瞬間
・ブラウザをスクロールした瞬間

 
のように、私たちユーザーの操作(=アクション)
がきっかけ(=トリガー)となります。
 
ブラウザ上で発生するこれらのタイミングのことを、
プログラム用語では「イベント(event)」と呼びます。
 
日常用語のイベントと本質的には同じ意味です。
イベント=出来事 みたいなニュアンスなのでしょうね。
ちょっと面白いですね。
 
さて、javascriptを使ってフレームのタグに
次のような style属性を追加すれば、
スタイルシートの設定に関係なく、
styleの設定値が優先されます。
 

▼HTMLに記述した内容
<div class="embed-youtube">
 <iframe width="600" height="400" src="●●"></iframe>
</div>
↓
▼javascriptで動的に書き替える内容
<div class="embed-youtube" style="padding-top:66.66%;">
 <iframe width="600" height="400" src="●●"></iframe>
</div>

ここでも重要だけど意外と知られていない
基本知識があります。
 
それはスタイルシートの優先度というルールです。
 
スタイルシートのファイル(.css)に書いた設定よりも、
HTMLのタグに style="..." で記述した設定のほうが
優先度が高いため、ブラウザは padding-top:66.66%;
を使ってフレームを表示しようとします。
 
=========================
 
66.66%の部分には縦横の比率を入れるわけですが、その計算をするには、
やはりjavascriptを使ってフレームのタグに書いてある width と height を
読み取って割り算をします。
 
そのためには、フレームのタグにアクセスする必要がありますが、
目印がなくてはアクセスできないので、次のように自分で決めた適切な名前の
class をHTMLにあらかじめ記述しておきます。
 
class="embed-youtube" という部分です。
   
▼HTMLの記述部分

<div class="embed-youtube">
 <iframe width="640" height="360" src="●●"></iframe>
</div>

embed(エンベッド)とは、埋め込みの意味です。
class="umekomi" でも構いませんが、
英単語のほうがカッコイイですよね。
翻訳サイトでサッと調べれば英単語が出てきます。
 
javascriptの記述部分

jQuery(function($){
 $('.embed-youtube').each(function(){
  var width = $(this).find('iframe').attr('width');
  var height = $(this).find('iframe').attr('height');
  var aspect = height / width;
  $(this).attr('style', 'padding-top:' + aspect + '%;');
 });
});

要約すると、ページ内で embed-youtube という classが付いた全てのタグにアクセスし、
それぞれの内側にある<iframe>タグから縦横のサイズを読み取って比率を計算し、
<div class="embed-youtube">のタグにpadding-topを追加するという意味になります。
 
▼ブラウザ内でのHTML

<div class="embed-youtube" style="padding-top:66.66%;">
 <iframe width="600" height="400" src="●●"></iframe>
</div>

タグを追加しなくても自動でタグが付くようにしよう

 
さらに汎用的にしようと思えば、
 
<div class="embed-youtube">というタグで<iframe>を
いちいち囲まなくても済むようにできます。
 
プログラムの論理だけ書くと次の通りです。
 
・ページ内でyoutubeのURLが付いた<iframe>タグを探す
・<iframe>の外側のタグが<div class="embed-youtube">でなければ、<div class="embed-youtube">を追加する。

 
ウェブサイトを更新する人がタグを付け忘れていたなら、
javascriptのプログラム側で付けてあげましょう、
という考え方ですね。
 
やろうと思えば何だってできそうですね。