You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Web-Dev-For-Beginners/translations/ja/3-terrarium/3-intro-to-DOM-and-closures/README.md

16 KiB

テラリウムプロジェクト パート3: DOM操作とクロージャ

DOMとクロージャ

スケッチノート: Tomomi Imura

講義前クイズ

講義前クイズ

はじめに

DOM"Document Object Model")の操作は、ウェブ開発の重要な側面です。MDNによると、「Document Object Model (DOM) は、ウェブ上のドキュメントの構造と内容を構成するオブジェクトのデータ表現です。」DOM操作の課題は、しばしばJavaScriptフレームワークを使用してDOMを管理する理由となっていますが、ここではバニラJavaScriptで対応します

さらに、このレッスンではJavaScriptクロージャの概念を紹介します。クロージャとは、ある関数が別の関数に囲まれており、内側の関数が外側の関数のスコープにアクセスできるようにするものです。

JavaScriptのクロージャは非常に広範で複雑なトピックです。このレッスンでは、基本的なアイデアに触れるだけです。このテラリウムのコード内で、クロージャ内側の関数と外側の関数が構築され、内側の関数が外側のスコープにアクセスできるようになっているものを見つけることができます。詳細については、詳細なドキュメントをご覧ください。

このレッスンでは、クロージャを使用してDOMを操作します。

DOMを木構造として考えてみてください。これは、ウェブページのドキュメントを操作するためのすべての方法を表しています。さまざまなAPIアプリケーションプログラムインターフェースが作成されており、プログラマーは選択したプログラミング言語を使用してDOMにアクセスし、編集、変更、再配置、その他の管理を行うことができます。

DOMツリーの表現

DOMとそれを参照するHTMLマークアップの表現。Olfa Nasraouiより

このレッスンでは、ページ上の植物を操作できるJavaScriptを作成することで、インタラクティブなテラリウムプロジェクトを完成させます。

前提条件

テラリウムのHTMLとCSSが完成している必要があります。このレッスンの終わりまでに、植物をドラッグしてテラリウムに出し入れできるようになります。

タスク

テラリウムフォルダ内に新しいファイルscript.jsを作成します。このファイルを<head>セクションでインポートします:

	<script src="./script.js" defer></script>

注: 外部JavaScriptファイルをHTMLファイルにインポートする際には、deferを使用して、HTMLファイルが完全に読み込まれた後にJavaScriptが実行されるようにします。また、async属性を使用して、HTMLファイルの解析中にスクリプトを実行することもできますが、今回の場合、ドラッグスクリプトを実行する前にHTML要素が完全に利用可能であることが重要です。


DOM要素

最初に行うべきことは、DOM内で操作したい要素への参照を作成することです。今回の場合、サイドバーにある14個の植物が対象です。

タスク

dragElement(document.getElementById('plant1'));
dragElement(document.getElementById('plant2'));
dragElement(document.getElementById('plant3'));
dragElement(document.getElementById('plant4'));
dragElement(document.getElementById('plant5'));
dragElement(document.getElementById('plant6'));
dragElement(document.getElementById('plant7'));
dragElement(document.getElementById('plant8'));
dragElement(document.getElementById('plant9'));
dragElement(document.getElementById('plant10'));
dragElement(document.getElementById('plant11'));
dragElement(document.getElementById('plant12'));
dragElement(document.getElementById('plant13'));
dragElement(document.getElementById('plant14'));

ここで何が起きているのでしょうかドキュメントを参照し、そのDOMを検索して特定のIdを持つ要素を見つけています。HTMLの最初のレッスンで、各植物画像に個別のIdid="plant1")を付けたことを思い出してください。その作業がここで役立ちます。各要素を特定した後、そのアイテムをdragElementという関数に渡します。この関数は後で作成します。これにより、HTML内の要素がドラッグ可能になりますまたは、すぐにドラッグ可能になります

なぜ要素をIdで参照するのでしょうかCSSクラスではなくCSSに関する前回のレッスンを参考にして、この質問に答えてみてください。


クロージャ

次に、dragElementクロージャを作成します。これは、内側の関数今回の場合は3つを囲む外側の関数です。

クロージャは、1つ以上の関数が外側の関数のスコープにアクセスする必要がある場合に便利です。以下はその例です

function displayCandy(){
	let candy = ['jellybeans'];
	function addCandy(candyType) {
		candy.push(candyType)
	}
	addCandy('gumdrops');
}
displayCandy();
console.log(candy)

この例では、displayCandy関数が、既存の配列に新しいキャンディタイプを追加する関数を囲んでいます。このコードを実行すると、candy配列は未定義になります。これは、candyがローカル変数(クロージャにローカル)であるためです。

candy配列をアクセス可能にするにはどうすればよいでしょうか?配列をクロージャの外側に移動してみてください。これにより、配列はグローバルになり、クロージャのローカルスコープに限定されなくなります。

タスク

script.js内の要素宣言の下に、次の関数を作成します:

function dragElement(terrariumElement) {
	//set 4 positions for positioning on the screen
	let pos1 = 0,
		pos2 = 0,
		pos3 = 0,
		pos4 = 0;
	terrariumElement.onpointerdown = pointerDrag;
}

dragElementは、スクリプトの冒頭で宣言されたterrariumElementオブジェクトを取得します。そして、関数に渡されたオブジェクトのローカル位置を0に設定します。これらは、ドラッグ&ドロップ機能を各要素に追加する際に操作されるローカル変数です。テラリウムはこれらのドラッグされた要素によって埋められるため、配置場所を追跡する必要があります。

さらに、この関数に渡されたterrariumElementは、pointerdownイベントを割り当てられます。これは、ウェブAPIの一部で、DOM管理を支援するために設計されています。onpointerdownはボタンが押されたとき、または今回の場合はドラッグ可能な要素がタッチされたときに発火します。このイベントハンドラーは、いくつかの例外を除き、ウェブおよびモバイルブラウザの両方で動作します。

イベントハンドラーonclickは、クロスブラウザでのサポートがはるかに広範です。なぜここで使用しないのでしょうか?作成しようとしている画面インタラクションの正確なタイプを考えてみてください。


Pointerdrag関数

terrariumElementはドラッグ可能になりました。onpointerdownイベントが発火すると、pointerDrag関数が呼び出されます。この関数を次の行の下に追加します:terrariumElement.onpointerdown = pointerDrag;

タスク

function pointerDrag(e) {
	e.preventDefault();
	console.log(e);
	pos3 = e.clientX;
	pos4 = e.clientY;
}

いくつかのことが起きています。まず、e.preventDefault();を使用して、pointerdown時に通常発生するデフォルトイベントを防ぎます。これにより、インターフェースの動作をより細かく制御できます。

スクリプトファイルを完全に構築した後、この行を削除してみてください。何が起きるでしょうか?

次に、ブラウザウィンドウでindex.htmlを開き、インターフェースを検査します。植物をクリックすると、'e'イベントがどのようにキャプチャされるかがわかります。このイベントを掘り下げて、1回のpointerdownイベントでどれだけの情報が収集されるかを確認してください。

次に、ローカル変数pos3pos4e.clientXに設定されていることに注目してください。これらの値は、クリックまたはタッチした瞬間の植物のx座標とy座標をキャプチャします。クリックしてドラッグする際の植物の動作を細かく制御する必要があるため、その座標を追跡します。

なぜこのアプリ全体が1つの大きなクロージャで構築されているのか、少しずつ明確になってきましたかもしそうでなければ、14個のドラッグ可能な植物それぞれのスコープをどのように維持するかを考えてみてください。

この初期関数を完成させるために、pos4 = e.clientYの下に2つのpointerイベント操作を追加します

document.onpointermove = elementDrag;
document.onpointerup = stopElementDrag;

これで、ポインタを動かすと植物が一緒にドラッグされ、植物の選択を解除するとドラッグジェスチャが停止するように指定しています。onpointermoveonpointerupは、onpointerdownと同じAPIの一部です。インターフェースは現在エラーをスローしますが、これはまだelementDragstopElementDrag関数を定義していないためです。次にそれらを構築します。

elementDragとstopElementDrag関数

クロージャを完成させるために、植物をドラッグしたときとドラッグを停止したときに何が起きるかを処理する2つの内部関数を追加します。目指す動作は、いつでもどの植物でもドラッグでき、画面上のどこにでも配置できることです。このインターフェースは非常に自由度が高くドロップゾーンなどはありません、植物を追加、削除、再配置することでテラリウムを自由にデザインできるようにしています。

タスク

pointerDragの閉じ中括弧の直後にelementDrag関数を追加します:

function elementDrag(e) {
	pos1 = pos3 - e.clientX;
	pos2 = pos4 - e.clientY;
	pos3 = e.clientX;
	pos4 = e.clientY;
	console.log(pos1, pos2, pos3, pos4);
	terrariumElement.style.top = terrariumElement.offsetTop - pos2 + 'px';
	terrariumElement.style.left = terrariumElement.offsetLeft - pos1 + 'px';
}

この関数では、冒頭で設定したローカル変数14を大幅に編集しています。ここで何が起きているのでしょうか

ドラッグ中に、pos1pos3(以前にe.clientXとして設定)から現在のe.clientX値を引いたものに再割り当てします。同様の操作をpos2にも行います。その後、pos3pos4を要素の新しいX座標とY座標にリセットします。これらの変更をドラッグ中にコンソールで確認できます。その後、植物のCSSスタイルを操作して、新しい位置に基づいて植物の位置を設定します。これは、pos1pos2の新しい位置を比較して植物の上部と左側のX座標とY座標を計算することで行います。

offsetTopoffsetLeftは、親要素の位置に基づいて要素の位置を設定するCSSプロパティです。親要素はstatic以外の位置指定がされている必要があります。

これらの位置の再計算により、テラリウムとその植物の動作を細かく調整できます。

タスク

インターフェースを完成させる最後のタスクは、elementDragの閉じ中括弧の後にstopElementDrag関数を追加することです:

function stopElementDrag() {
	document.onpointerup = null;
	document.onpointermove = null;
}

この小さな関数は、onpointeruponpointermoveイベントをリセットします。これにより、植物のドラッグを再開したり、新しい植物のドラッグを開始したりできます。

これらのイベントをnullに設定しないとどうなるでしょうか

これでプロジェクトが完成しました!

🥇おめでとうございます!美しいテラリウムが完成しました。
完成したテラリウム


🚀チャレンジ

クロージャに新しいイベントハンドラーを追加して、植物にさらに何かをさせてみましょう。たとえば、植物をダブルクリックして最前面に移動させるなどです。創造力を発揮してください!

講義後クイズ

講義後クイズ

復習と自己学習

画面上の要素をドラッグすることは一見簡単そうに見えますが、目的とする効果によってはさまざまな方法や落とし穴があります。実際、ドラッグドロップAPIというものがあり、試してみることができます。このモジュールでは、求める効果が少し異なるため使用しませんでしたが、自分のプロジェクトでこのAPIを試してみてください。

ポインターイベントに関する詳細は、W3CドキュメントMDNウェブドキュメントで確認できます。

常にブラウザの対応状況をCanIUse.comで確認してください。

課題

DOMを使ったさらなる作業

免責事項:
この文書は、AI翻訳サービス Co-op Translator を使用して翻訳されています。正確性を追求しておりますが、自動翻訳には誤りや不正確な部分が含まれる可能性があることをご承知おきください。元の言語で記載された文書が公式な情報源とみなされるべきです。重要な情報については、専門の人間による翻訳を推奨します。この翻訳の使用に起因する誤解や誤認について、当方は一切の責任を負いません。