質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.46%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

2回答

1810閲覧

動的に追加した要素のsrc, srcsetをjsで設定できない

erp

総合スコア46

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

0クリップ

投稿2021/10/15 02:57

編集2021/10/20 09:10

前提・実現したいこと

動的に追加した要素について、画像の遅延読み込みを loading="lazy" に対応しているブラウザでは、lazysizes を読み込まず、data-src の値を src にセットしたいです。
また、インクルードするパーツは別のhtmlファイルにまとめているものを用いています。

発生している問題・エラーメッセージ

エラーは特にないです。

該当のソースコード

// インクルード

js

1if (document.getElementsByClassName("nav") != null) { 2 window.addEventListener('DOMContentLoaded', function () { 3 fetch('/assets/include/nav.html') 4 .then(response => response.text()) 5 .then(data => { 6 const parser = new DOMParser(); 7 const html = parser.parseFromString(data, "text/html"); 8 const p_elements = html.getElementsByClassName('nav')[0]; 9 const file_area = document.getElementsByClassName("nav")[0]; 10 file_area.innerHTML = p_elements.innerHTML; 11 }); 12 }); 13}

// lazyload の読み込み

js

1document.addEventListener('DOMContentLoaded', function () { 2 if ("loading" in HTMLImageElement.prototype) { 3 var images = document.querySelectorAll('img[loading="lazy"]'); 4 var sources = document.querySelectorAll("source[data-srcset]"); 5 sources.forEach(function (source) { 6 source.srcset = source.dataset.srcset; // ここが効かない 7 }); 8 images.forEach(function (img) { 9 img.src = img.dataset.src; // ここが効かない 10 }); 11 } else { 12 var script = document.createElement("script"); 13 script.src = "https://cdnjs.cloudflare.com/ajax/libs/lazysizes/5.3.2/lazysizes.min.js"; 14 script.async = true; 15 document.body.appendChild(script); 16 var script_02 = document.createElement("script"); 17 script_02.src = "https://cdnjs.cloudflare.com/ajax/libs/lazysizes/5.3.2/plugins/object-fit/ls.object-fit.min.jss"; 18 script_02.async = true; 19 document.body.appendChild(script_02); 20 } 21})

// インクルードするパーツ

HTML

1<div class="nav"> 2 <picture> 3 <source type="image/webp" data-srcset="/images/7.png.webp"> 4 <img class="lazyload" loading="lazy" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" data-src="/images/7.png"> 5 </picture> 6</div>

試したこと

js

1document.addEventListener('DOMContentLoaded', function () {

で囲んでもダメだったのでどうすればいいでしょうか。

コールバックを使ってみてもダメでした。

js

1function fetchApi(func) { 2 // インクルードjs 3func(); 4}; 5var lazyload = function () { 6 // lazyload 読み込み 7}; 8fetchApi(lazyload);

追記

js

1document.addEventListener('DOMContentLoaded', () => { 2 function lazyload(node) { 3 if ("loading" in HTMLImageElement.prototype) { 4 let images = node.querySelectorAll('img[loading="lazy"]'); 5 let sources = node.querySelectorAll("source[data-srcset]"); 6 // 省略 7 } else { 8 let script = document.createElement("script"); 9 // 省略 10 } 11 } 12 lazyload(document); 13 14 if (document.getElementsByClassName("nav")[0] != null) { 15 fetch('/assets/include/nav.html') 16 .then(response => response.text()) 17 .then(data => { 18 const parser = new DOMParser(); 19 const html = parser.parseFromString(data, "text/html"); 20 const p_elements = html.getElementsByClassName('nav')[0]; 21 const file_area = document.getElementsByClassName("nav")[0]; 22 file_area.innerHTML = p_elements.innerHTML; 23 }).then(node => { 24 lazyload(node) 25 }); 26 } 27});

として、fetchの順番を守ったのですが、エラーが出てしまいました。

error

1TypeError: node is undefined

node が未設定となっているようです。
let images = node.querySelectorAll('img[loading="lazy"]'), のところです。
node に何を設定すればいいでしょうか。

補足情報(FW/ツールのバージョンなど)

Firefox 最新版、Safari 604.1

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

dameo

2021/10/16 02:58

Windows 10の chrome バージョン: 94.0.4606.81(Official Build) (64 ビット) firefox 93.0 (64 ビット) で、以下のHTMLを試しました。 <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <script> document.addEventListener( 'DOMContentLoaded', (ev) => { if ("loading" in HTMLImageElement.prototype) { document.querySelectorAll('img[loading="lazy"]').forEach((img) => { img.src = img.dataset.src; }); } else { console.log('cannot be lazy'); } }); </script> </head> <body> <div style='padding-top: 1000em;'> <img loading="lazy" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" data-src="https://gahag.net/img/201607/13s/gahag-0105896765-1.jpg"> <img loading="lazy" src="https://gahag.net/img/201607/27s/gahag-0110296134-1.jpg" data-src="https://gahag.net/img/201607/13s/gahag-0105896765-1.jpg"> </div> </body> </html> 結果はchrome/firefoxともにgahag-0105896765-1.jpgが正しく表示されました。 mac or iOS製品及びsafariはないので分かりませんが、lazyに対応していないのでしょうか? ソースのコメントを見ると、対応している前提で「ここが効かない」と書いているように見えるのですが・・・
dameo

2021/10/16 03:40

早とちりしてました。動的に追加したものに限定した話で、lazy未対応のブラウザの話ではなかったんですね。基本的にfetchが非同期で読み込んでる間に書き換えてるのが問題なので、fetch完了して要素を追加した後に再度書き換えればいいと思います。 <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <script> document.addEventListener( 'DOMContentLoaded', (ev) => { function changeImageSrcToDataSrc(node) { if ("loading" in HTMLImageElement.prototype) { node.querySelectorAll('img[loading="lazy"]').forEach((img) => { img.src = img.dataset.src; }); } else { console.log('cannot be lazy'); } } changeImageSrcToDataSrc(document); if (document.getElementsByClassName("nav") != null) { function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } delay(0) .then(() => { const file_area = document.getElementsByClassName("nav")[0]; file_area.innerHTML = '\ <img loading="lazy" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="\ data-src="https://gahag.net/img/201607/13s/gahag-0105896765-1.jpg">\ <img loading="lazy" src="https://gahag.net/img/201607/27s/gahag-0110296134-1.jpg"\ data-src="https://gahag.net/img/201607/13s/gahag-0105896765-1.jpg">' return file_area; }) .then((node)=> { // ここで再び呼べばOK //changeImageSrcToDataSrc(node); }); } }); </script> </head> <body> <div style='padding-top: 1000em;'> <img loading="lazy" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" data-src="https://gahag.net/img/201607/13s/gahag-0105896765-1.jpg"> <img loading="lazy" src="https://gahag.net/img/201607/27s/gahag-0110296134-1.jpg" data-src="https://gahag.net/img/201607/13s/gahag-0105896765-1.jpg"> </div> <div class="nav"></div> </body> </html> fetchの代わりにsetTimeoutで非同期を作りましたが、コメントのままなら同じ現象が出ます。 コメントを外せば期待した結果になるようです。
erp

2021/10/20 08:00

回答に気づくのが遅れてしまいすみません。回答ありがとうございます。 実はインクルードするパーツはhtmlで別ファイルにまとめており、js で呼び出しています。できれば、呼び出しのコードとパーツは別にしたいとと考えています。その旨追記しておきます。 せっかく回答くださったのにすみません。
dameo

2021/10/20 08:06

回答ではなく、原因と思しきものを単体で実行できるコードで説明したつもりです。 erpさんのコードは単体で動作しないので。 原因が違っていればお付き合いしますが、対策はご自分で行ってください。
erp

2021/10/20 09:30

fetchに順番を追記することで思ったように実装できました。原因についてのご指摘大変助かりました。ありがとうございます。
guest

回答2

0

自己解決

js

1document.addEventListener('DOMContentLoaded', (ev) => { 2 function lazyload(node) { 3 node = document; 4 if ("loading" in HTMLImageElement.prototype) { 5 let images = node.querySelectorAll('img[loading="lazy"]'); 6 let sources = node.querySelectorAll("source[data-srcset]"); 7 sources.forEach(function (source) { 8 source.addEventListener('load', function () { 9 source.classList.add('fadeIn'); 10 }); 11 source.srcset = source.dataset.srcset; 12 }); 13 images.forEach(function (img) { 14 img.addEventListener('load', function () { 15 img.classList.add('fadeIn'); 16 }); 17 img.src = img.dataset.src; 18 }); 19 } else { 20 let script = document.createElement("script"); 21 script.src = "https://cdnjs.cloudflare.com/ajax/libs/lazysizes/5.3.2/lazysizes.min.js"; 22 script.async = true; 23 document.body.appendChild(script); 24 let script_02 = document.createElement("script"); 25 script_02.src = "https://cdnjs.cloudflare.com/ajax/libs/lazysizes/5.3.2/plugins/object-fit/ls.object-fit.min.jss"; 26 script_02.async = true; 27 document.body.appendChild(script_02); 28 } 29 } 30 lazyload(document); 31 32 if (document.getElementsByClassName("nav")[0] != null) { 33 fetch('/assets/include/nav.html') 34 .then(response => response.text()) 35 .then(data => { 36 const parser = new DOMParser(); 37 const html = parser.parseFromString(data, "text/html"); 38 const p_elements = html.getElementsByClassName('nav')[0]; 39 const file_area = document.getElementsByClassName("nav")[0]; 40 file_area.innerHTML = p_elements.innerHTML; 41 }).then(node => { 42 lazyload(node) 43 }); 44 } 45});

sourceのときにクラスが付与されないのは置いておいて、こちらのコードでできました。fetchにも順番を指定できたのですね。

投稿2021/10/20 09:28

erp

総合スコア46

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

erp

2021/10/20 10:05

then メソッドはメソッドチェーンができるのですね。大変勉強になります。 参考URLもありがとうございます。
guest

0

処理後lazyload()を実行すればよいのでは?

投稿2021/10/15 08:18

yambejp

総合スコア115010

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

erp

2021/10/15 08:51

すみません、知識不足でよく分かりませでした。 loading="lazy" に対応しているブラウザでは、lazysizes を読み込まず(lazyload を実行せず)、data-src の値を src に挿入したいのですが、それが出来ませんでした。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.46%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問