🌎

VanillaJSでAjaxをしてみよう。(JSON,HTML,PHPの読み込み編)

2022/10/11に公開

introduction

はじめに

JavascriptはどんどんJQueryから離れていこうと、モダンなJSに行こうとしている。そんな時に、ネットに転がっているのはJQueryありきのものばっかりや!特に、Ajaxに関してはJQueryの書き方が簡潔すぎて、もうブラックボックス化してJQueryの構文つかえばええやないか^^となっている気もする(自分だけ?)。なので、VanillaJSを用いてきちんと理解したいな…と思いこの記事を作成した。
ってのが建前で、本音は自分の仕事に必要になったから。ただそれだけ^^
初めて書く記事だし、coder歴1年という未熟者なので、先輩方の知見をご教示いただけると嬉しい。

対象

・自分みたいに、AjaxをVanillaJSで実装したい!とかいうモダンJSの中で戦う人々。
・仕事でAjax必要なんですけど!?っていう俺みたいなやつ。
・Ajaxってどんな構造やねん?非同期通信を詳しく知りたいなぁぐらいの人
→この人は前半のソースコードが書かれていない部分だけで十分わかる。後半は読まんでいい。

前提知識

・Javascriptは使ったことがあるよ、ハンバーガーメニューとか実装できるよ、のレベル(てかそのレベルの実装出来ひん人がajaxの構造知りたいってどんなニーズやねん)

そもそもAjaxって?

Ajaxとは、Asynchronous JavaScript and XMLのこと。
"Asynchronous=非同期"という意味で、ブラウザ上で非同期通信を行うために代表的に使う技術や。
つまり非同期通信をJSとXMLを使ってやろうぜ!ってことやな。
ただ、非同期通信が何なのか全くわからない。なので非同期通信について調べてみる。

非同期(Asynchronous)通信とは?

非同期の定義について

そもそも非同期とは、2つ以上の事象が同時に発生したり、関連する複数の事象が互いの完了を待たずに発生したりする概念のことを指す。詳しくはMDNの公式ドキュメントに載っているので、概念に関してはこちらをみてほしい。

今回の文脈で使われる非同期とはつまり、下のリンク内にある”ネットワークと通信”の部分のことを指していて、
例えば、

・GoogleMapをスクロールしたらそのイベントがトリガーとなって、表示するためのデータを持ってくる
・Youtubeの動画欄をスクロールをしたら新しい動画の一覧が更に表示される
・twitterのツイートが最下層まで行ったらさらに古いツイートのデータを持って来て表示する

みたいな動作があるらしい。動画で見たい人はこれがわかりやすいと思う。
https://www.youtube.com/watch?v=V89nh3UCBbM&t=757s

さっきもいったように、非同期は、

  1. 2つ以上の事象が同時に発生したり、
  2. 互いの完了を待たずに発生したりする概念

なわけで、今回の場合は

A:サーバーからデータを受信すること  
B:ユーザーがwebサイトを閲覧するためにスクロールをすること

とすると、AとBが同時に起こることを受け入れているというところに非同期らしさがある。

これが同期(Synchronous)通信になるとどうなるのか?

では、非同期の対となる概念である同期通信に置き換わったとしたらどうやろうか。まずは同期通信の定義について、MDNの公式ドキュメントから確認してほしい。大雑把に言えば、各当事者が同じ通信の状況に立っているということ。お互いの通信者が足並みを揃えた状況なわけやな。

プログラミングとかはほんとにそう。処理を完了してやっと結果が返ってくるのであって、その間はターミナルやシェルスクリプトの操作はできないよね。これは、ターミナルの表示とパソコンが同じ通信状況にあるということ。
あとはラインとかそうじゃない?彼女とラインをするとき(君らがいるのかはしらないけど)、彼女の携帯のライン画面と、僕の携帯のライン画面は同じ表示になっている。もし非同期なら、僕が1時間前に送ったラインが今届いたり、話がずれにずれる状況ができる可能性があるというのは伝わるかな(彼女と僕でラインの画面が全然違うようになっているよね)。

じゃあ、さっきの、AとBが同期通信になったらどうなるのか?

A:サーバーからデータを受信すること  
B:ユーザーがwebサイトを閲覧するためにスクロールをすること

結果はわかるんじゃないかな。例えば、Twitterで最下層までスクロールしたら、スマホは固まってそこからスクロールができなくなる。サーバーとユーザーが同じ立場になるので、サーバーが「おい!おれがデータ受信するまでうごくなよ!」なんていうジャイアンになっちまうわけだな。

非同期がなぜ必要なのか?

ここまでのことをまとめると、非同期は同期と違って"通信中であっても、別の操作ができる"ということが大きな利点としてあるということがわかるだろう。
では、Webサイトにおいて、なぜこんなに非同期通信が使われているのか?理由は明白だ。
サーバーからどんなデータ量を受信しても、ユーザーを縛ることなく通信ができるということ。
これが一番なんだな。べつに非同期と同期でどっちがいいなんてなくて、ただ今回のようにwebサイトでサーバーの受信する話になったときは非同期が有利ってだけだ。

Ajaxを用いて実際に非同期通信をやっていこう

まずはAjaxの基本的な構造を以下に記述する。今回は、以下の動画で用いられたソースコードを利用する。そのため、feed.jsonに関してはその動画内で使われたものを使用することになる。feed.jsonの中身はあんまり関係ないので、別に動画を見なきゃいけないわけでもない。

https://www.youtube.com/watch?v=dyBtH9NolIQ&t=437s

では、以下の基本構造について、1つずつ解説していこう。

Ajax-基本構造
<body>
    <div id="news"></div>    
    <script>
        const ajax = new XMLHttpRequest();
        ajax.open('GET','feed.json',true);
        ajax.onload = function(e){
            if(ajax.readyState === 4){
                if(ajax.status === 200){
                    const json = JSON.parse(ajax.responseText);
                    const html = '<ul>';
                    for(const i=0;i<json.items.length;i++){
                        html += '<li>' +json.items[i].title + '</li>';
                    }
                    html += '</ul>';

                    document.getElementById('news').innerHTML = html;
                }
            }
        }
        ajax.send(null);
    </script>
</body>

XMLHttpRequest

では、まずscript内の一行目から見ていこう。どうやらXMLHttpRequestというオブジェクトを生成しているようだ。こんなの聞いたこと無いので、MDNの公式ドキュメントを見てみよう。すると、

XMLHttpRequest(XHR)オブジェクトは、サーバーと対話するために使用されます。ページ全体を更新する必要なしに、データを受け取ることが出来ます。これでユーザーの作業を中断させることなく、ウェブページの一部を更新することが出来ます。

と書いている。これ、非同期通信まんまのこと書いてるやん!ってなるわけ。まぁ、つまり非同期通信をするためにはこのXHRオブジェクトが必須なわけだな。ほんで、こうとも書いている。

XMLHttpRequestという名前ではあるものの、XMLだけではなく、あらゆる種類のデータを受け取るために使用することが出来ます。

つまり、XMLだけじゃなくて、jsonとか、htmlとかいろんなデータから引っ張ってこれるみたいね。csvとかもいけんのかな…。
まぁ、ここからちょっと先を見てみると、このオブジェクト内のメソッドをうまく活用してajaxが成立するみたい。じゃあ、メソッドを理解すればええやないか^^ってなるので、それぞれ解説していこう。

openメソッド

もうわかってきたか?そう、今回も、基本的にはMDN公式ドキュメントを見て勉強するで。
まず構文は以下のようになる。構文に関しては英語版MDNと日本版MDNで異なる表現になっているので、どちらも見ておくと理解が捗るだろう(英語版の方が実用的な表記だと思われる)。

open syntax-ja
XMLHttpRequest.open(method, url[, async[, user[, password]]])
open syntax-en
open(method, url)
open(method, url, async)
open(method, url, async, user)
open(method, url, async, user, password)

第1引数はHTTPリクエストメソッドをどれにするか?を選ぶこと。まだHTTPリクエストメソッドを知らない人は、以下の動画がすごくわかりやすい。GETとPOSTの違いがわかれば十分だが、もう少し知りたいって人はMDN公式ドキュメントをみるといい。だ(しつこい?そんなことないだろ^^)。

https://www.youtube.com/watch?v=2tBbC1rZo3Q

第2引数はurlの値。今回の場合はfeed.jsonから取得するので、これを記述する。

第3引数で、非同期するかどうかをえらべる。Asynchronousのasyncだ。Trueにすることによって、非同期通信をすることができる。

第4、5引数はuser,passwordだけど、これはセキュリティ上ロックがかかっているサイトにリクエストを送るときにのみ利用するので今回は気にしない。

んで、肝心な何をするか?についてだけど、これも公式ドキュメントを見ると、

open()メソッドは、新しく作成されたリクエストを初期化したり、既存のリクエストを再初期化したりします。

と記述されている。つまり、このメソッドを用いることによって、XHRオブジェクトに必要な情報(サーバーから何を受け取るかとか)を記述するわけだな。これはXHRオブジェクト生成とセットでしなければならない。

onloadメソッド

これに関してはwindowでメソッドとしてあるので、js使いまくっている人は基本原理からわかっているだろうが、知らないって人もいるのですこしだけ解説しておこう。まず、onloadメソッドはoneventプロパティの一つであり、イベントハンドラープロパティ(event handler property)の一つでもある。まぁ、要はイベントが発火したことに応答して処理を行えるようにするプロパティのことだ。XHRオブジェクトのonloadってのはつまり、非同期通信が必要となった時に再ロードされるというイベントに対する応答なわけだ。

英語版のMDN公式ドキュメントでは、

The load event is fired when an XMLHttpRequest transaction completes successfully.

って書いてあるが、transaction=取引のことで、さっき言ったことと同じことを述べていることが伝わるといいな。(英語のわかる上級戦闘民族だけでいい。)

function(e) に関してはこのサイトを見てくれ。このサイト以上に詳しく解説できる自信はない。

また、addEventListenerメソッドでも同じことができるが、詳しくはこのサイトをみるんだ。

readyStateメソッド

まぁ、もうわかっただろうから、次からは最初にMDN公式ドキュメントをおいておくことにしよう(しつこすぎて読むのやめてもらっても困るので)。
MDN公式ドキュメントーreadyState

まずこのドキュメントに書いてあることは、
XMLHttpRequest.readyState プロパティは XMLHttpRequest (XHR) クライアントの状態を返します。

ってだけだ。これは、非同期通信行った際に操作が完了してから初めて動作を行いたいっていう願いから記述するわけ。だからStateをreadyするメソッド。詳しい引数の解説はあまりしないが、open()をしているか?send()をしているか?の確認に使えるという点で、非常に便利だと思う。

statusメソッド

MDN公式ドキュメント-status

どんなことをするメソッドか?MDN公式ドキュメントには、

XMLHttpRequest のレスポンスにおける数値の HTTP ステータスコードを返します。

と記述されている。ステータスコードに関してわからない人もいるだろうから、以下の動画(さっき載せたけど、ステータスコードは後半で解説してる。クソわかり易い)をみるといい。もちろん、もっと詳しく知りたい人は(ry

このstatusメソッドによって、通信が正常に行われたか?がわかるわけだ。かんたんだな。

responseTextメソッド

MDN公式ドキュメント

これはMDN公式ドキュメントにも解説されている通り、送信されたリクエストに続いてサーバーから受け取ったテキストを返すメソッドだ。
今回はparseを活用して、受け取ったドキュメントをjsonの構造として認識をしている。

sendメソッド

MDN公式ドキュメント

これは一言でいうと、リクエストをサーバーに送信するために必須のメソッドだ。
この公式ドキュメントに書いてあることは重要で、
リクエストが非同期の場合 (これが既定)、このメソッドはリクエストが送信されるとすぐに戻り、結果はイベントを用いて配信されます。リクエストが同期の場合、このメソッドはレスポンスが到着するまで戻りません。
っていうのが、非同期と同期の差異を作っている。

JSONとXMLについて

メソッドの方はだいたい網羅出来たが、そもそもjsonファイルとxmlファイルという、普段webコーディングだけをやっているとあまり使わないファイルを活用することになる。そのため、ざっとそれぞれのファイルの内容を解説する。この理解に関してはこのサイトが役に立った。

JSON

JSONの正式名称は”Javascript Object Notion”だ。んで、この言語はデータ記述言語の一つ。
そもそもデータ記述言語とはなにか、という話も触れておくと、一言でいうと配列や連想配列的な概念を使ったデータの表記方法という理解でいい。連想配列はJSでいうところのオブジェクトだな。

なんでデータ記述言語がいるの?

webコーディングをやっている人は、phpなんかをさわるときにforeachで連想配列内をloopで回すということをやっていると思うけど、phpでの連想配列と、jsのオブジェクトの記述はぜんぜん違う(下を見てもらいたい)。

phpで連想配列
$map = [
	'name' => '個人アカウント',
	'category' => 'VOCALOID'
];
jsで連想配列
var map = { 
	"name": "初音ミク",
	"category": "VOCALOID"
};

これ、同じ連想配列だけど記述方法とか違うし、そもそもphpとjsでは処理の仕方とかもぜんぜん違う。んで、例えばphpファイルで作ったデータをjsで読み込もうとするとけっこう大変なわけだな。ほな、配列とか連想配列的な概念で構築されたデータを色々な言語で扱うことが出来るようにその部分だけ共通化した規格を用意してればええやないか^^ってことで作られたのがデータ記述言語なわけ。まぁつまりデータを使うならこの言語使っときゃjsでもpythonでもphpでも同じように取得できるよねってこと。

XML

じゃあXMLってなんやねん?ってことなんだけど、これはJSONが普及する前によく使われていたデータ記述言語なんだけど、下のようにHTMLと同じマークアップ構造をしているわけ。まぁこれでもいいんだけど、データ記述する時にわざわざタグとか書くのめんどくね?もっと簡潔にかけねぇの?って考えられたJSONが下記安すぎるので、最近はあんま使われなくなったな。まぁ今回はこいつの説明はこれくらいにしておこう(今は必要じゃないからな)。

Ajax実践

サーバー起動

Ajax初心者の人はよく間違えやすいのだが、ajaxはサーバーと通ずる通信のため、ローカルサーバーを立てる必要がある。一番簡単なのはvscodeのlive serverによる設計なのだが、別にローカルサーバーさえ立てられればなんでもいい。

jsonでの読み込み

jsonでの読み込みは、以下のように取り込むと良い。というか先述した基本構造がそのとおりになっている。feed.jsonはこのリンクから入手することができる。

jsonでの読み込み
<body>
    <div id="news"></div>    
    <script>
        const ajax = new XMLHttpRequest();
        ajax.open('GET','feed.json',true);
        ajax.onload = function(e){
            if(ajax.readyState === 4){
                if(ajax.status === 200){
                    const json = JSON.parse(ajax.responseText);
                    const html = '<ul>';
                    for(var i=0;i<json.items.length;i++){
                        html += '<li>' +json.items[i].title + '</li>';
                    }
                    html += '</ul>';

                    document.getElementById('news').innerHTML = html;
                }
            }
        }
        ajax.send(null);
    </script>
</body>

HTMLでの読み込み

他のhtmlファイルから読み込もうとしたが、なかなかvanillaJSを用いてhtmlの読み込みをしているところがなかった。そのため、海外のドキュメントや動画を色々漁ったら、以下の動画が見つかった。まじでわかりやすいわありがとうdcode。
https://www.youtube.com/watch?v=cva5NQTnbu4
ココで述べている通り、例えばあるcontainerの内部に別のhtmlに書かれている内容を持って来たいって時にはこれをresponseTextをつかう。
ただ、responseTextは先程述べたとおり文章として読み込むことしか出来ないのでは?responseXML(これは出てきてないが、名前を見た感じこっちのが適切のように見えるだろ^^)のほうが良いのでは?と思ったので少し調べた。

htmlからの読み込み
<body>
    <div id="container"></div>
<script>
const ajax = new XMLHttpRequest();
const container = document.getElementById('container');
ajax.open("get", "./external.html");
ajax.addEventListener("load", function(){
    if(this.status === 200){
        container.innerHTML = ajax.responseText;
    }else{
        console.log('Not Found')
    }
});
ajax.send();
</script>
</body>
external.html
<ul class="fruits">
    <li>Apples</li>
    <li>Bananas</li>
    <li>Mangos</li>
</ul>

responseTextとresponseXMLの違いは?

これに関してはこのサイトが理解の助けになった。このサイトが言うには、responseTextとresponseXMLではデータ構造が違うと。ほんで、responseXMLが返してくれるデータ型は”XMLdocument”であると。実際に、responseXMLで表示すると[object XMLDocument]という表示になる。
対して、responseTextは文字列で返してくれる。htmlファイルは、基本単なる文字列を解析して作られるテキストなのだから、responseTextのほうが良かったのだろう。

phpでの読み込み

さて、phpでの読み込みということだが、webコーディングではwordpressを使うことがほとんどだろうから、これに関しても扱っておこう。 違いは、リンクの貼り方。固定ページを作成して、そのリンクをつかわなくてはならない。wordpress経験者ならすぐに分かるだろう。あとは非同期通信を行ったかどうか。かな?まぁこれに関しては今回はどっちでもいい気がする。

index.php
    <div id="container"></div>
    <script>
    const ajax = new XMLHttpRequest();
    const container = document.getElementById('container');
    ajax.open("get", "sample",true);
    ajax.addEventListener("load", function(){ 
    if(this.status === 200 && this.readyState === 4){
	    container.innerHTML = ajax.responseText;
    }else{
        console.log('Not Found')
    }
});
   ajax.send(); 
    </script>
  </body>

もっと深く知りたい場合は?

英語が読んだり聞いたりできる、あるいは字幕だけでも動画見るのは十分ってできるバラモン階級の人類には以下のdcode氏の動画を勧める。なんなら自分もこれでもっと勉強したいと思ったほど。
https://youtu.be/cva5NQTnbu4

公式ドキュメントはしっかり読んだほうがいい。
https://developer.mozilla.org/ja/docs/Web/API/XMLHttpRequest/XMLHttpRequest

実はmdnのドキュメントは公式ドキュメントではなく、以下のものが公式らしい。構文とかの正確性、内部構造を見たいという時は使うべきだと思う。
https://xhr.spec.whatwg.org/

ほかにも公式ではないが、documentsっぽいものを挙げておこう。簡潔に情報を手に入れられる。

https://lab.syncer.jp/Web/API_Interface/Reference/IDL/XMLHttpRequest/
https://phpjavascriptroom.com/?t=ajax&p=xmlhttp

このサイトに関してはだいぶ詳しいイメージ。より実践系に持っていっているので、おすすめ。有料の部分もあるが、ajaxの基本に関しては無料でみれる。
https://wp-p.info/tpl_rep.php?cat=js-practice&fl=r18

最後に

現代はネット社会と言われ、検索すれば何でも自由に出てくると言われている。しかし、ことプログラミングに関しては、初学者にわかりやすいようにしようとどんどんハードルを下げ、いろんな関数の内部構造をブラックボックス化したりする事によって、あたかも「プログラミングは勉強しなくてもつかえるよ」と言っているように思う。しかし、プログラミングをするなら、ドキュメントは読んだほうがいいし、深く理解することはそのまま知識の汎用性につながる。深い知識は自由度が高いのだ。

簡単な知識のみでも、コピペだけでも、使えることは使える。使えたという経験を作るのはたしかにいいことだ。しかし、そこから一歩踏み込んで、自分で内部構造を理解し自由に扱えるなら、プログラミングがもっと楽しくなると思う。そのためにネットの海に潜り込んで、公式ドキュメントはもちろん、色んな人が書いた記事から深い知識を得ることが大切だと思う。

くっっっっっっっそどうでもいいことをつらつらと述べたけど、この記事を作りながら改めて思ったのでつい書いてしまったわけで、若者の戯言として流してくれると嬉しい。

Discussion