jquery jQuery プラグイン Masonry の使い方と設定

2014年12月6日

ブラウザの幅に応じて、配置した要素のレイアウトを整列してくれる jQuery プラグイン Masonry の使い方や設定に関する個人的なメモ。

Lazy Load や Magnific Popup も一緒に使ってみた。

★ CSS Grid + JavaScript で Masonry レイアウト も御覧ください(こちらの方が新しい情報です)。

Masonry-01

この時点でのバージョン:v3.2.1

目次

jQuery プラグイン Masonry のダウンロード

Masonry のページの「Download masonry.pkgd.min.js」を右クリックで「名前をつけてリンク先を保存」を選択するか、「Download masonry.pkgd.min.js」をクリックしてファイルメニューから「名前を付けてページを保存」を選択して適当な場所に保存。

Masonry-02

jQuery プラグイン Masonry の読み込み

Masonry は jQuery は必須ではないがあった方が便利なので、jQuery とダウンロードした「masonry.pkgd.min.js」の読み込みを body 閉じタグの直前や head 内等に記述。ファイル(masonry.pkgd.min.js)へのパスは適宜環境に合わせて記述。(jQuery を読み込んでおけば jQuery プラグインとして機能する。)

<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script src="js/masonry.pkgd.min.js"></script> 
<script>
jQuery(function($){
    //ここに Masonry プラグインのイニシャライズやオプションを記述
    //(別ファイルに記述してもOK。そのファイルを読み込む必要あり)
    //jQuery プラグイン Masonry のイニシャライズを参照
});
</script>

Masonry の HTML の記述

「id=”container”」という親要素(div 要素)内に配置した「class=”item”」という要素(div 要素)を整列させる処理を行う場合の例。「class=”x2″」は幅が2倍の要素。

<div id="container">
  <div class="item">...</div>
  <div class="item x2">...</div>
  <div class="item">...</div>
  ...
</div>

この例では「class=”item”」という div 要素内に、img 要素や p 要素を配置。

<div id="wrapper">
  <div id="container">
    <div class="item"><img src="images/001.jpg"  alt="">
      <p>title 1</p>
    </div>
    <div class="item x2"><img src="images/002.jpg" alt="">
      <p>title 2</p>
    </div>
    <div class="item"><img src="images/003.jpg" alt="">
      <p>title 3</p>
    </div>
    ...
  </div>
</div>

Masonry の CSS の記述

CSS で適当な大きさに整形

#wrapper {
  position: relative;
  max-width: 900px;
  margin: 0 auto;   /*全体の中央寄せ*/
}
#container {
  width: 100%;
  margin: 0 auto;   /*中央寄せ*/
  position: relative;
}
.item {
  margin: 10px;
  width: 140px;
  padding: 8px;
  border: 1px solid #eee;
}
.item.x2 {
  width: 320px;
}
.item.x3 {
  width: 500px;
}
.item img {
  width: 100%;
}

「class=”item”」という div 要素の全体幅は、margin, padding, border を含めると、140 + 10×2 + 8×2 + 1×2 = 180px。

全てが同じ幅だとカラムレイアウトとしてうまく揃うが、それぞれがバラバラの幅だと当然綺麗には揃わない。

レスポンシブやリキッドレイアウトなどでもうまくカラムが揃うようにするには、幅を調整すると良いみたいなので、以下のようにする。

最小幅の次に大きな幅は以下のように指定してみる。

「class=”item x2″」という div 要素の幅は「class=”item”」という div 要素の width (140px)に全体幅(180px)を加えた320px とする。

「class=”item x3″」という div 要素の幅は「class=”item x2″」という div 要素の width (320px)に全体幅(180px)を加えた500px とする。

図を描くとわかりやすい。

Masonry-03

jQuery プラグイン Masonry のイニシャライズ

jQuery プラグイン Masonry のイニシャライズ(初期設定)を記述。

<script>
jQuery(function($){  
  $('#container').masonry({
    itemSelector: '.item',
    columnWidth: 180, 
    isFitWidth: true  //親要素の幅に合わせてカラム数を自動調整
  });  
});
</script>

上記で指定したオプション

  • itemSelector:対象にするセレクタを指定
  • columnWidth:カラム幅を設定
  • isFitWidth:コンテナの幅をカラム数に合わせて調整するオプション(true/false)。true に設定した場合、更にCSSでコンテナを中央寄せすることができる。

サンプル1

imagesLoaded プラグイン

Masonry のページの下のほうに「imagesLoaded プラグイン」の記述がある。

Unloaded images can throw off Isotope layouts and cause item elements to overlap. imagesLoaded resolves this issue. Read more about using imagesLoaded with Masonry.

ロードされていない(読み込まれていない)画像はレイアウトを崩して、それらの要素が重なってしまうことがあるが「imagesLoaded プラグイン」を使うことによりこれを解決することができるとのこと。

https://github.com/desandro/imagesloaded からダウンロード可能。

Masonry-04

「imagesloaded.pkgd.min.js」を右クリックで「名前をつけてリンク先を保存」を選択するか、「imagesloaded.pkgd.min.js」をクリックしてファイルメニューから「名前を付けてページを保存」を選択して適当な場所に保存して読み込む。

imagesLoaded は全ての画像が読み込まれた後にコールバック関数をトリガーするらしい。

var $container = $('#container');
// 全ての画像が読み込まれてから Masonry を初期化(イニシャライズ) 
$container.imagesLoaded( function() {
  $container.masonry();
});

前述の例の場合

<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script src="js/masonry.pkgd.min.js"></script> 
<script src="js/imagesloaded.pkgd.min.js"></script> <!-- ファイルの読み込み -->
<script>
jQuery(function($){
  var $container = $('#container'); 
    $container.imagesLoaded(function(){
      $container.masonry({
        itemSelector: '.item', 
        isFitWidth: true, 
        columnWidth: 180
      });
    });
});
</script>

それでも上下が重なる場合

ボックス内にテキストと画像を表示させた時、img 要素の height, width を指定しても何故かうまくいく場合といかない場合があったので、以下のようにしたら重ならずに表示された。前述の例を「$(window).load」を使って少し変更。これが正しいかは不明だが、一応機能する。

<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script src="js/masonry.pkgd.min.js"></script> 
<script src="js/imagesloaded.pkgd.min.js"></script> <!-- ファイルの読み込み -->
<script>
jQuery(function($){
  $(window).load(function(){
    var $container = $('#container'); 
    $container.imagesLoaded(function(){
      $container.masonry({
        itemSelector: '.work_item', 
        isFitWidth: true, 
        columnWidth: 180
      });
    });
  });
});
</script>

imagesLoaded のページ

一定の幅以下の場合に jQuery Masonry プラグインを無効化する

一定の幅以下になった場合に、何故か期待どおりのレイアウトにならない場合があるので、その場合 jQuery Masonry プラグインを無効にする。

以下のページを参考にさせていただきました。

レスポンシブWebデザイン制作にjQuery Masonryを利用するための5つのポイント

ページ読み込み時とリサイズ時にブラウザの幅をチェックし、一定の幅(この例では480px)未満になった場合に、destroy()メソッドで、jQuery Masonry プラグインを無効する。

<script>
jQuery(function($){
    
  var min_width = 480;
  //画面幅による分岐と imagesLoaded, Masonry のイニシャライズを関数化
  function masonry_update() {
    var $container = $('#container');
    if ( $('html').width() < min_width ) {
      $container.masonry('destroy');
    } else {
      $container.imagesLoaded(function(){
        $container.masonry({
          itemSelector: '.item', 
          isFitWidth: true, 
          columnWidth: 180
        });
      });
    }
  }
  
  masonry_update();
  
  //リサイズ時の処理
  var timer = false;
  $(window).resize(function(){
    if (timer !== false) {
      clearTimeout(timer);
    }
    timer = setTimeout(function() {
      masonry_update();
    }, 200);
  });  
});
</script>

CSS に以下を追加(この例の場合。かなりいい加減。)

@media screen and (max-width: 480px) {
  .item {
    margin: 5px auto;
    width: 140px;
    padding: 4px;
    border: 1px solid #eee;
  }
  
  .item.x2 {
    width: 300px;
  }
}

レスポンシブにするには、viewport などの指定も必要(詳細は省略)
参考:「Viewport と Media Queries に関するメモ

<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />

サンプル2

一定の幅以下の場合に columnWidth を変更する

ブラウザの幅を検出して、columnWidth を変更する方法。

function masonry_update() {
    ww = $(window).width();
    var cw = 180;
    if(ww < 460) { cw = 160; }  //幅により columnWidth を変更    
    var $works_list = $('#works_list');
    $works_list.imagesLoaded(function(){
        $works_list.masonry({
            itemSelector: '.work_item', 
            isFitWidth: true, 
            columnWidth: cw
        });
    });
}
<!-- end of code -->

プラグイン Lazy Load の使用

画像が多い場合にページに含まれる画像のデータの読み込みを遅らせる(後から読み込ませる)ためのプラグイン Lazy Load の使用を追加してみる。

Lazy Load の使い方や設定は以下を参照ください。

jQuery プラグイン Lazy Load の使い方と設定

Lazy Load を使う場合、画像要素の記述を変更する必要がある。

具体的には、以下のような感じ。

  • src 属性にダミー画像へのパスを指定
  • data-original 属性に実際の画像へのパスを指定
  • 高さと幅を指定
  • 画像要素に識別するためのクラス(この例では class=”lazy”)を指定

HTML の例

<div id="wrapper">
  <div id="container">
    <div class="item x2 clearfix">
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Non alias dolore totam vero blanditiis harum quod. Perferendis, sit, praesentium possimus nostrum quisquam quod nisi fugiat consequuntur. Atque, recusandae reiciendis mollitia!</p>
    </div>
    <div class="item"><img class="lazy" src="images/dummy.gif" data-original="images/001.jpg" width="140" height="272" alt="">
      <p>title 1</p>
    </div>
    <div class="item"><img class="lazy" src="images/dummy.gif" data-original="images/002.jpg" width="140" height="183" alt="">
      <p>title 2</p>
    </div>
    ...
  </div>
</div>

CSS では、div 要素(class=”item”)に「float: left」を追加。これを追加しないと、画面上に表示されている部分の画像の一部がロードされない場合がある。おそらく Lazy Load から見るとそれらの要素は画面外にあるものと認識されてしまうため?

.item {
  margin: 10px;
  width: 140px;
  padding: 8px;
  border: 1px solid #eee;
  float: left;   /*Lazy Load 用に追加 */
}

jQuery

<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script> 
<script src="js/jquery.lazyload.min.js"></script><!-- lazyload の読み込み --> 
<script src="js/masonry.pkgd.min.js"></script> 
<script src="js/imagesloaded.pkgd.min.js"></script> 
<script>
jQuery(function($){
  
  //この部分が lazyload のイニシャライズ
  $("img.lazy").lazyload({
    effect: 'fadeIn',
    effectspeed: 1000,
    threshold: 400
  });

  //以下は同じ  
  var flag;
  var min_width = 480;
  
  //画面幅による分岐と imagesLoaded, Masonry のイニシャライズを関数化
  function masonry_update() {
    var $container = $('#container');
    if ( $('html').width() < min_width ) {
      $container.masonry('destroy');
      flag = 0;
    } else {
      $container.imagesLoaded(function(){
        $container.masonry({
          itemSelector: '.item', 
          isFitWidth: true, 
          columnWidth: 180
        });
      });
    }
  }
  
  masonry_update();
    
  //リサイズ時の処理
  var timer = false;
  $(window).resize(function(){
    if (timer !== false) {
      clearTimeout(timer);
    }
    timer = setTimeout(function() {
      masonry_update();
    }, 200);
  });
  
});
</script>

サンプル3

画像が重なる場合

前述のサンプルでは問題がなかったが、同じ幅の画像(高さは異なる)を使用した際にいくつかの画像が重なって表示されてしまった。Lazyload は画像の読み込みを遅延させるもので、Masonry はそれらを配置する際に高さと幅を取得する必要があるらしく考えてみると矛盾した組み合わせなのかも知れない。。。

Lazyload のコールバックでもあればと思って検索したところ以下のページを発見。

Stack Overflow:Jquery Lazyload callback

これを参考にして、以下のコード(それぞれの画像がロードされたときに Masonry を実行)を使ってみるとなんとかうまくいった。

この方法だと画像の幅や高さも記述しなくても問題なく表示できたが、全てのケースに当てはまるわけではないと思う。。。
(何故問題なく表示される場合と、画像が重なって表示される場合があるのかは定かではない)

$('img.lazy').load(function() {
    masonry_update();
});

HTML

<div id="works_list">
    <div class="work_item x2 description">
        <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Non alias dolore totam vero blanditiis harum quod. Perferendis, sit, praesentium possimus nostrum quisquam quod nisi fugiat consequuntur. Atque, recusandae reiciendis mollitia!</p>
    </div><!-- end of .work_item-->
    <div class="work_item">
        <img class="lazy" src="images/dummy.gif" data-original="images/works/thumb/001.jpg" alt="">
        <p>title 1</p>  
    </div><!-- end of .work_item-->
    <div class="work_item">
        <img class="lazy" src="images/dummy.gif" data-original="images/works/thumb/002.jpg" alt="">
        <p>title 2</p>  
    </div><!-- end of .work_item-->
         ...
</div><!-- end of #works_list -->

CSS ではブラウザの幅が480px以上と以下で画像のサイズやパディングを変更

CSS

#works_list {
  margin: 0 auto;
}

.work_item {
  margin: 10px;
  width: 120px;
  max-width: 120px;
  padding: 4px;
  background-color: #fcfcfc;
  float: left;
}

.work_item.x2 {
  width: 300px;
  max-width:300px;
}

.work_item img {
  width: 100%;
}
.work_item p {
  text-align: center;
  margin: 10px auto 0;
}

.work_item.description p {
  text-align: left;
  padding: 1em;
  margin: 0 auto;
}

@media screen and (min-width : 480px){
  .work_item {
  margin: 10px;
  width: 140px;
  max-width: 140px;
  padding: 9px;
  float: left;
  }

  .work_item.x2 {
  width: 320px;
  max-width:320px;
  }   
}

jQuery では masonry の初期化などを masonry_update() という関数にしておく。この関数では更にブラウザの幅により、columnWidth の値を変更するように指定している(レスポンシブ用)。

そして決めては $(‘img.lazy’).load で関数 masonry_update() を呼び出すこと。それとリサイズの際も masonry_update() を呼び出す(レスポンシブ用)。

jQuery

<script>
jQuery(function($){
    
    $("img.lazy").lazyload({
        effect: 'fadeIn',
        effectspeed: 1000,
        threshold: 200
    });
  
    $('img.lazy').load(function() {
        masonry_update();
    });
  
    function masonry_update() {
        ww = $(window).width();
        var cw = 180;
        if(ww < 460) { cw = 160; }      
        var $works_list = $('#works_list');
        $works_list.imagesLoaded(function(){
            $works_list.masonry({
                itemSelector: '.work_item', 
                isFitWidth: true, 
                columnWidth: cw
            });
        });
     }
    
    var timer = false;
    $(window).resize(function(){
        ww = $(window).width();
        if (timer !== false) {
            clearTimeout(timer);
        }
        timer = setTimeout(function() {
            masonry_update();
        }, 200);
    });  
});
</script>

プラグイン Magnific Popup の使用

Lightbox のようなモーダルウィンドウで表示できるプラグイン Magnific Popup を追加してみる。

Magnific Popup の使い方や設定は以下を参照ください。

Magnific Popup を使ってみる

Magnific Popup を使用するために HTML の記述を変更。

div 要素(class=”item”)の記述を以下のように変更。

img 要素を a 要素で囲み、その href 属性に画像へのパスを記述。

HTML

<div class="item"><a href="images/large/001.jpg"><img class="lazy" src="images/dummy.gif" data-original="images/001.jpg" width="140" height="272" alt="ニューヨークのグラフティ"></a>
      <p>title 1</p>
    </div>

jQuery

<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script> 
<script src="js/jquery.lazyload.min.js"></script> 
<script src="js/masonry.pkgd.min.js"></script> 
<script src="js/imagesloaded.pkgd.min.js"></script> 
<script src="js/jquery.magnific-popup.min.js"></script> <!-- Magnific Popup の読み込み -->
<script>
jQuery(function($){
    
  //lazyload のイニシャライズ
  $("img.lazy").lazyload({
    effect: 'fadeIn',
    effectspeed: 1000,
    threshold: 400
  });
  
  var min_width = 480;
  
  //画面幅による分岐と imagesLoaded, Masonry のイニシャライズを関数化
  function masonry_update() {
    var $container = $('#container');
    if ( $('html').width() < min_width ) {
        $container.masonry('destroy');
    } else {
      $container.imagesLoaded(function(){
        $container.masonry({
          itemSelector: '.item', 
          isFitWidth: true, 
          columnWidth: 180
        });
      });
    }
  }
   
  masonry_update();
  //リサイズ時の処理
  var timer = false;
  $(window).resize(function(){
    if (timer !== false) {
      clearTimeout(timer);
    }
    timer = setTimeout(function() {
      masonry_update();
    }, 200);
  });
  
    //lmagnificPopup のイニシャライズ
    $('#container').magnificPopup({
          delegate: 'a',
          type: 'image',
          disableOn: function() {
              if( $(window).width() < 480 ) {
                  return false;
              }
              return true;
          },
          gallery: { //ギャラリーオプション
              enabled:true
          },
          image: {
              // image コンテントタイプのオプション
              cursor: null,
              titleSrc: function(item) {
                  return item.el.find('img').attr('alt');
              }
          }
    });  
});
</script>

サンプル4