Mansory で表示した時に要素が重なる件

Webページを作っていて,画像などの大きさの異なる要素を繰り返しで表示する場合にはタイル状(レンガ状?)に敷き詰めて配置できると綺麗ですね.Masonry という JavaScript ライブラリを使うとこれを非常に簡単にやってくれるので便利.なのですが,配置した要素が重なり合ってしまうという問題が発生しました.この問題の解決策を書きます.

問題の原因

Masonry では,要素の配置場所の決定をそれぞれの要素のサイズをもとに行っています.このため,当該要素が読み込まれる前に Masonry の計算処理が実行されてしまうと,要素の配置場所がおかしなことになります.今回は画像をリストする要素としていたのですが,その画像が読み込まれる前に Masonry による処理が走ってしまっていたことが原因でした.

問題が発生したコードは以下.

$(function () {
    $('#container').masonry({
        ...
    });
});

$(function)() で初期化していますが,$(function)() での初期化は DOM ツリー(HTML要素)の読み込みが完了した時点で実行されます.そして,このタイミングではまだ画像や動画は読み込みが完了してない可能性があります.このように読み込みが完了していない時に Masonry の処理が走ると上記の問題が発生してしまいます.

解決策① JS 実行のタイミングを変える

以下のコードのように,$(window).load() を使います.

$(window).load(function(){
    $('#container').masonry({
         ...
    });
});

$(window).load() は,画像やフォントなどの関連データを読み込んだ後に実行されるので,Masonry がちゃんと画像サイズを取得できます.
$(function)() と $(window).load() の違いは以下.

$(function(){}) と $(window).on('load',function(){}) の違い - Qiita

解決策② ImagesLoaded を使う

今回初めて知ったのですが,ImagesLoaded という画像の読み込みが完了した後に処理を実行するためのライブラリがありました.これでも上記の問題を解決できます.
まずは以下からダウンロード.

https://imagesloaded.desandro.com/

後は js ファイルを読み込んで,以下のようなコードに変更.

$(function () {
    var container = $('#container');
    container.imagesLoaded(function () {
        container.masonry({
            ...
        });
    });
});

これで #container 配下の画像を読み込んだ後に Masonry が実行されることが保証されます.

画像読み込み後の処理による表示時のガタツキが気になる

fakeLoader などでローディング処理を入れることで綺麗になります.

Masonry 公式

masonry.desandro.com

github.com

おわり

このような問題にハマらないためにも,フロント周りのライブラリを導入する際には,読み込みや処理の実行順序をある程度意識しておきたいですね.おわり.