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

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

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

DOMは、Document Object Modelの略で、HTML文書やXML文書をアプリケーションから利用するためのAPIです。

JavaScript

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

jQuery

jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

Ajax

Ajaxとは、Webブラウザ内で搭載されているJavaScriptのHTTP通信機能を使って非同期通信を利用し、インターフェイスの構築などを行う技術の総称です。XMLドキュメントを指定したURLから読み込み、画面描画やユーザの操作などと並行してサーバと非同期に通信するWebアプリケーションを実現することができます。

HTML

HTMLとは、ウェブ上の文書を記述・作成するためのマークアップ言語のことです。文章の中に記述することで、文書の論理構造などを設定することができます。ハイパーリンクを設定できるハイパーテキストであり、画像・リスト・表などのデータファイルをリンクする情報に結びつけて情報を整理します。現在あるネットワーク上のほとんどのウェブページはHTMLで作成されています。

Q&A

解決済

2回答

1761閲覧

promiseのメソッドチェーンの中でajaxを使い要素を追加しhtmlcollectionをArray.fromで配列に直そうとすると空の配列になる現象について

_akasa

総合スコア5

DOM

DOMは、Document Object Modelの略で、HTML文書やXML文書をアプリケーションから利用するためのAPIです。

JavaScript

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

jQuery

jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

Ajax

Ajaxとは、Webブラウザ内で搭載されているJavaScriptのHTTP通信機能を使って非同期通信を利用し、インターフェイスの構築などを行う技術の総称です。XMLドキュメントを指定したURLから読み込み、画面描画やユーザの操作などと並行してサーバと非同期に通信するWebアプリケーションを実現することができます。

HTML

HTMLとは、ウェブ上の文書を記述・作成するためのマークアップ言語のことです。文章の中に記述することで、文書の論理構造などを設定することができます。ハイパーリンクを設定できるハイパーテキストであり、画像・リスト・表などのデータファイルをリンクする情報に結びつけて情報を整理します。現在あるネットワーク上のほとんどのウェブページはHTMLで作成されています。

0グッド

0クリップ

投稿2021/06/27 09:43

前提・実現したいこと

ajaxを使って追加した要素のhtmlcollectionをきちんとした配列に直したい。

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

ajaxを使って追加した要素のhtmlcollectionを配列に直せない。なぜか空の配列になる。
javascript1の書き方をしても間違っていないと思っているのですができません。htmlcollectionは取得出来ているのでpromiseはしっかりと動いておりthenのチェーンも順番に処理できていると思うのですがArray.fromだけがうまくいきません。Array.fromも一応配列の形にはなっているので働いてるのでしょうが空の配列になります。

該当のソースコード

html

1<div id="ui">unn</div>

Array.fromをしたら空の配列になるjsコード↓

javascript1

1 function delayHello(){ 2 var d = new $.Deferred; 3 d.resolve(); 4 return d.promise(); 5 } 6 let promise=delayHello(); 7 promise.then(function(){ 8 $.ajax() 9 .done(function(){ 10 $('#ui').append('<div class="yur">こんんにちは</div>'); 11 }) 12 13 }).then(function(){ 14 console.log(document.getElementsByClassName('yur'));//htmlcollectionは取得出来ている。 15 console.log(Array.from(document.getElementsByClassName('yur')));//空の配列になる。 16 17 })

Array.fromをしてもなぜかきちんと配列に値が入っているjsのコード↓

javascript2

1 function ajaxWithAppendToBody(ajaxOptions){ 2 var deferred = new $.Deferred; 3 $.ajax(ajaxOptions).done(function(data){ 4 $('#ui').append('<div class="yur">こんにちは</div>)'); 5 deferred.resolve(data); // resolve()でdoneメソッドを呼び出す 6 }).fail(function(data){ 7 deferred.reject(data); // reject()でfailメソッドを呼び出す 8 }); 9 return deferred.promise(); // 戻り値はpromiseにする 10 } 11 12 13 ajaxWithAppendToBody({ 14 }).done(function(data){ 15 // 非同期処理が成功時の処理 16 console.log(document.getElementsByClassName('yur'));//htmlcollectionは取得出来ている。 17 console.log(Array.from(document.getElementsByClassName('yur')));//きちんと配列になっている。 18 19 }).fail(function(data){ 20 // 非同期処理が失敗時の処理 21 }); 22

試したこと

javscript2のコードの真似をしてjavascript1のコードでresolveの前で要素を追加してみました。

js

1function delayHello(){ 2 var d = new $.Deferred; 3 $.ajax() 4 .done(function(){ 5 $('#ui').append('<div class="yur">こんんにちは</div>');//resolveの前で要素を追加する 6 d.resolve(); 7 }) 8 9 return d.promise(); 10 } 11 let promise=delayHello(); 12 promise.then(function(){ 13 console.log(document.getElementsByClassName('yur'));//きちんとhtmlcollectionを取得出来ていた。 14 console.log(Array.from(document.getElementsByClassName('yur')));//配列の形になっていた 15 16 })

そしたらきちんとした配列の形で出力されました。resolveの前でするとうまくいくようです。なぜresolveの後だと成功しないのでしょうか。promiseは非同期処理の順番を指定するものだと考えているのでできればthenの中でajaxなどの非同期処理を書けるようになりたいのです。

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

xampp
jquery 3系

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

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

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

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

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

guest

回答2

0

ベストアンサー

1番目のコードは2つの問題があります。

  • 外側のpromiseが $ajax のpromise解決を待っていない
  • console.log の使い方(中に入っている情報に対する認識)を誤っている

まず1つ目ですが、簡単に確認するにはタグをappendする前にconsole.logで何らかのログを出すと分かります。

js

1// ajaxの部分だけ抜粋 2 .then(function () { 3 $.ajax().done(function () { 4 $("#ui").append('<div class="yur">こんんにちは</div>'); 5 console.log('div append done'); 6 });

HTMLCollectionのログの前に div append done が表示されるはずです。

MDNでPromiseの挙動を確認すると、返り値の項にこう書いてあります。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise/then

・何も返さなかった場合、 then によって返される Promise は undefined の値で解決します。

外側のpromiseのthenは、確かにreturnがないので、何も返していません。なので、中の処理に関わらず即刻解決されて次のthenが処理されてしまっています。
これを防ぐには、$ajaxの結果をちゃんとreturnで返してあげれば良いです。

js

1 .then(function () { 2 return $.ajax().done(function () { 3 $("#ui").append('<div class="yur">こんんにちは</div>'); 4 console.log('div append done'); 5 });

これだけでHTMLCollectionのログの前に'div append done'が出てきます。
一応、これでこの問題は解決です。この状態でArray.fromを使用すればちゃんとdiv.yurの配列が取得できます。

2つ目のconsole.logについてです。
他のブラウザで正確な検証はしていないのでchromeの場合に限っての説明としますが、console.logの引数にDOM要素を設定した場合、DOMへの参照になります。
コンソール上では ▶ HTMLCollection []と右向きの三角が表示されいます。ここをクリックすると中身が見れるわけですが、これは「今現在の」参照(document.getElementsByClassName('yur')の結果)になります。
そもそも、console.logの実行時点でちゃんと要素がappendされていれば、コンソール上の表示はHTMLCollection [div.yur]となるはずです。なので、元のコードではconsole.logの実行時点でHTMLCollectionは空だったのです。しかし、このHTMLCollectionはMDNの表現で言うと「生きて」おり、DOMの状況を監視してターゲット(この場合は"yur"クラスを持つ要素)に変更があった場合自動的に中身が更新されていくものです。最初のコードもコンソールで確認する段階には要素がappendし終わっているので、コンソールのHTMLCollectionをクリックするとdiv.yurが1つ入っているように見えます。
しかし、ログが出力された時点ではまだdiv.yurはappendされておらず、appendを検知して中身が更新されたHTMLCollectionを見ていただけなのです。

このように、console.logでDOMやオブジェクトをそのまま見ると「今いまの情報」を見ることができない場合が発生します。これを防ぐため、MDNのドキュメントは以下のような方法を推奨しています。

js

1console.log(JSON.parse(JSON.stringify(obj)))

https://developer.mozilla.org/ja/docs/Web/API/Console/log#logging_objects

投稿2021/06/27 12:25

hope_mucci

総合スコア4447

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

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

_akasa

2021/06/28 11:56

MDNまで引用していただき丁寧な回答どうもありがとうございます。 console.log('div append done')を入れてreturnで$.ajaxを返すのとreturnを付けない書き方をしてみたらdiv append doneの出力のタイミングに違いがありました。 thenの中に書くハンドラ関数はどんな処理でも関数の処理を終えてから、次のthenの処理を始めるものだと考えていました。then()の中に書くハンドラ関数がpromiseを返す関数(非同期関数)だった場合はreturnを付けてreturn $.ajaxがあるから終わらせることができない($.ajax()の処理結果が必要である)状態にしているという理解でいいのでしょうか。 ログでhtmlcollectionを確認している時点ではappendされてなかったのですね。全然気づきませんでした。非同期関数の処理が丸ごと外側のpromiseの処理が終わってから実行されていたんですね。正しいコンソールログの使い方も教えてくださりありがとうございます。 $.ajax()はpromiseを返すということは実際はalwaysが省略されて$.ajax().always(function(){return new $.Deffered.promise()})のようになってるのでしょうか。 まだまだ本当に理解してない出来てないですが、調べていきます。教えてくださりありがとうございました。
hope_mucci

2021/06/28 12:20

promise関連の動作原理にいろいろと理解不足があるように見受けられます。promiseは本当に複雑で難しい概念で、このようなコメント上でとても解説しきれません。回答に上げたMDNのドキュメントを読み、実際にいろいろなパターンを書いてみて勉強してください。 then()はpromiseをreturnすると、その後の動作がreturnしたpromiseの解決状況に依存します(MDNドキュメントの「返値」の一番下に書かれているパターン)。よって$.ajax関数の戻り値であるpromiseの解決とthen()の解決が同値になり、結果的に$.ajaxの処理完了を待つことができる、ということです。
_akasa

2021/06/28 13:11

理解できてないことが多いので聞きたいことはありますが、まずはMDNのドキュメントをしっかり読んで書いてみます。それでも分からなければまた質問させていただきたいです。 確かにMDNのドキュメントに書いてありました。MDNのドキュメントと合わせてpromiseを基礎から勉強します。ありがとうございました。
guest

0

$.whenかfetchで処理すると良いかと思います

投稿2021/06/28 00:19

yambejp

総合スコア114810

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

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

_akasa

2021/06/28 18:16

回答ありがとうございます。 MDNのドキュメントを見てみました。$.whenはこの場合の使い方が分からないので並列処理でないと解決できない問題の時に使ってみたいと思います。fetchを書いてみましたがエラーになったのでとりあえずpromiseと$.ajaxを使って書きたいと思います。でもfetchなどは知らなかったので助かりました。$.ajaxと二点違いがあるそうなので必要になった時にエラー含め解決して使いたいと思います。ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問