やりたいこと
- 処理A:append() などで複数のimg要素を追加・表示する。
- 処理B:Aで追加したimg要素に対して操作を行う。処理Bは、img要素がすべて表示された後に行いたい。
調べたところ、img以外の要素や、動的に追加されたものではないimg要素に対する手法しか見つからなかったのでメモ。
#採用した方法
@piouc さんにいただいたコメントより追記です。ありがとうございます。
自分の実装よりスマートなので最終的に使用させていただくこととしました。
###1. jQuery.Deferredを使う
function main(){
// DOMの操作は同期的に行われるため$.whenを利用する必要はない。
$('最初からある要素').append('追加したいimg要素')
// 追加されたそれぞれの要素をDeferred化し全てのロードが完了するのを$.whenで待つ
$.when.apply($, $('追加したimg要素').map(waitForLoading)).then(function(){
// ロード完了後に行う処理
})
}
function waitForLoading(index, img){
var dfd = $.Deferred()
// img要素のロード完了はimg.completeで取得できます。
if(img.complete){
// すでにロードが完了していれば即時resolve。
dfd.resolve()
} else {
// ロードが完了していなかった場合、イベントリスナー登録しロードの完了を待つ
$(img).on('load', function(){
resolve()
})
}
return dfd
}
2.jQueryを利用しない場合、async, awaitを使う
async function main(){
//ここで画像の追加
await Promise.all(Array.from(document.querySelectorAll('追加したimg要素')).map(waitForLoading))
// ロード完了後に行う処理
}
function waitForLoading(img){
return new Promise(resolve => {
if(img.complete){
resolve()
} else {
img.addEventListener('load', () => resolve())
}
})
}
当初やってみたもの
setTimeout()でポーリング+画像の高さが1以上なら表示完了、という力技です。
jQueryの使用を前提としています。
- 処理A→処理Bの非同期処理にjQuery.when()を使う。
- 処理Bの「img要素が表示された後」の処理にsetTimeout()を使って表示されるまで待つ。
function main() {
$.when(
// 処理A
$('最初からある要素').append('追加したいimg要素');
).then(function() {
loadingCheck();
});
}
function loadingCheck() {
var timer;
var count = 0;
// 複数でないならループは不要
$('追加したimg要素').each(function() {
// 表示されていれば height > 0
if ($(this).height()) {
count++;
}
});
// 追加した要素数と、height > 0 の要素数が一致していればすべて表示されている
if ($('追加したimg要素').length === count) {
// 処理B
// $('追加したimg要素')になんらかの処理
// setTimeout()の停止
clearTimeout(timer);
} else {
// すべて表示されていなければ100ミリ秒(要素数やサイズで調整)待ってもう一度実行
timer = window.setTimeout(loadingCheck, 100);
}
}