0
0

More than 1 year has passed since last update.

HTMLの<picture>要素をIE11でも使えるように見せかける簡易版JSを作った

Posted at

IE11も対象の案件。ブラウザの画面幅に応じてページのレイアウトが変わる際、一部の<img>要素の画像については縦横比が異なるものを表示させる必要がありました。そこで、サイズや縦横比が異なる複数の画像をあらかじめ用意しておき、画面幅に応じて自動で切り替わるようにしたい。最近のブラウザならHTMLの<picture>要素をサポートしているので、mediaクエリを付けた<source>要素を並べて書くだけで実現できますが、IE11は<picture>要素が未対応。もちろん、polyfill picturefill.jsの存在は知っています。が、今回はとりあえずmax-widthだけを基準にして画像を切り替えできれば十分だったので、自分でIE11用に簡易版のコードをJavaScriptで書いてみることにしました1
HTML5 picture element for IE11-low.gif
※コンテントの画像 via Unsplash

簡易版の仕様

簡易版jsが扱うことができるシンタックスは以下のとおりです。

  • <source>要素にはsrcset属性とmedia属性のみ指定します。
  • media属性にはmax-width1つのみ、px単位で指定します。
  • <source>要素はmax-widthの値が小さい順に並べなければなりません。
example.html
<picture>
  <source srcset="/path/to/img1" media="(max-width: 360px)">
  <source srcset=/path/to/img2" media="(max-width: 420px)">
      .....
  <source srcset="/path/to/imgN" media="(max-width: 768px)">
  <img src="/path/to/fallback/img" alt="">
</picture>

See the Pen [JS] picture element in IE11 by Kazuhiro Hashimoto (@kaz_hashimoto) on CodePen.

このCodePenはIE11以外でも動作を確認できるように2、HTMLネイティブの機能の代わりに簡易版jsを使って<picture>要素の画像を切り替え表示しています。実際の場面では、IE11以外には不要なコードなので、最初にブラウザのUA文字列によってIE11かどうかを判定し、IE11でなければ即returnさせます。

javascript
document.addEventListener('DOMContentLoaded', function() {
  // 実際に使用する時は最初にUAでブラウザ判定を行い、
  // IE以外のブラウザでは以降のコードが実行されないようにする。
  // 例)
  // const parser = new UAParser();
  // const ua = parser.getResult();
  // if (ua.browser.name != 'IE') {
  //   return;
  // }
  .....

実装メモ

処理の本体はIE11でも実行可能なようにコーディングする必要があるため、NodeListの各要素に対してcallback関数を呼ぶ箇所は、一旦Arrayを作ってからforEachを呼ぶ形式で書いてあります。

javascript
const targets = document.querySelectorAll('picture');
Array.prototype.forEach.call(targets, function(picture) {
...

<source>要素に設定した一連のmedia="(max-width:は、区間が重ならないように
"(min-width: A px) and (max-width: B px)"
に変換したクエリでMediaQueryListオブジェクトを作成し、リスナーを設定します。

クエリとsrcsetの値のペアは、ハンドラ内からも参照するためMapオブジェクトで保持しておきます。ここで、Mapのキーに使うクエリ文字列は、matchMediaの戻り値から取り出した方のmql.mediaを使います。IE11の場合、それはmatchMedia(query)に指定したquery文字列と必ずしも一致しないためです。

javascript
const mql = window.matchMedia(query);
item.map.set(mql.media, source.getAttribute('srcset'));
const func = handler.bind(item)
mql.addListener(func);
func(mql);

変数itemは<picture>要素ごとに設定情報を保持するオブジェクトです。ハンドラー内でitemthisキーワードから参照可能にするため、bind()を呼んで生成した関数の方をリスナーに設定します。

javascript
function handler(mql) {
  if (!mql.matches) {
    return;
  }
  const item = this;
  const src = item.map.get(mql.media);
  if (!src) {
    return;
  }
  item.img.src = src;
}

ハンドラーが呼ばれると、対象の<picture>要素に紐付けたitemを取り出し、ステータスが変化したmediaクエリをキーにsrcsetの値を取り出し、<img>要素のsrcに画像をセットします。


  1. picturefill.jsは2017年3月リリースのver.3.0.3を最後に更新が止まっているようで、projectのstatusがよくわからなかったのも1つの理由。 

  2. CodePenはIE11をサポートしないため、このPenをIE11で確認したい時はPenのソース一式をExportし、localhost上のコンテンツとしてIE11からアクセスします。 

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0