このドキュメントは http://edu.net.c.dendai.ac.jp/ 上で公開されています。
ブラウザでJavaScriptのプログラムを動かすには、HTMLファイルに埋め込 むか、HTMLファイルよりプログラムファイルを読み込みます。 これには、HTMLの script 要素を使用します。
そのため、DOMに対するプログラムを動作させるには、次のような方法が あります。
さらに、表示したHTML文書に対して、ユーザからのインタラクティブな操 作に対応するには、 ユーザ操作などで生じるイベントに対応した動作を記述する必要がありま す。 特定の要素に対するイベントを処理するプログラムを書くには、 その要素にイベントハンドラコンテンツ属性を指定し、 必要な処理をイベントハンドラとして渡す必要があります。
HTML からJavaScript を動かすには次の方法があります。
ブラウザで HTML の読み込みにより、プログラムを動かしたい場合、 以下の方法がある。
属性にプログラムを直接書く場合、ダブルクォーテーションマーク(")を " でエスケープする必要があります。
script 要素の属性 src で URL でファイルを指定する場合、読み込み方 を指定することができる。 デフォルトでは、指定された場合、そのファイルを読み込んでプログラム を実行します。 そのため、プログラムの内容が単なる関数定義で、ユーザ操作に連動する ような場合、ファイル読み込みの時間にHTMLの表示などが止まることがあ ります。
head 内に置かれた script は body 文書が読み込まれる前に実行されま す。 そのため、文書に関する DOM はすべて参照できません。 プログラムと文書を独立させるために、プログラムを置くのには有効です が、文書を直接操作するプログラムは書けません。
body 要素内の script 要素でプログラムが置かれた場合、デフォルトの 動作では、そこまで文書が解釈された状態で実行されます。 そのため、script 以降のDOM要素は参照できません。 DOM全体に作用させるには defer 属性を指定させるか、 body に onload で関数を呼び出すかする必要がある。
ブラウザでは、クリックされるなどの様々なイベントが発生します。 イベントはオブジェクトで、 DOMのオブジェクトに登録されたイベントハンドラと言う、イベントを引 数とした関数オブジェクトが呼び出されます。
イベントに関しては、 HTML standard の Eventsの章 にまとめてあります。 但し、ここにあるほかにも、メディア要素イベントやドラッグアンドドロップ イベントが定義されています。 8.1.8.2 章のEvent handlers on elements, Document objects, and Window objectsにも載ってます。 主要なものを下記に示します。
HTMLの要素には共通のイベントハンドラコンテンツ属性がたくさん定義さ れています。 "on"+イベント名がそれぞれのHTML要素で許されていて、イベントハンドラを 指定できます。
古くは、input のボタンなどに onclick を指定して文字列の中にプログ ラムを埋め込んでいました。 新しい DOM では、イベントを定義していて、 イベントリスナにイベントと関数の対を追加するようになっています。 例12-1
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>例12-1</title>
<style>
.white{background:white;}
.red { background: red;}
.blue {background: blue;}
.green {background: green;}
</style>
</head>
<body>
<p id="target">
Hello world
</p>
<p>
<input type="button" value="red" id="inputelement">
<input type="button" value="blue" onclick="change('blue')">
<a id="aelement">green</a>
<a onclick="change('blue');">blue</a>
<select id="selector">
<option>white</option>
<option>red</option>
<option>blue</option>
<option>green</option>
</select>
</p>
<p id="pelement">
hover
</p>
<script>
const target=document.getElementById("target");
function change(x){
target.setAttribute('class',x);
}
const inputElement = document.getElementById('inputelement');
inputElement.addEventListener('click',(event)=>{
change('red');});
const aElement = document.getElementById('aelement');
aElement.addEventListener('click',(event)=>{
change('green');});
const selectElement = document.getElementById('selector');
selectElement.addEventListener('change', (event) => {
change(event.target.value);});
const pElement = document.getElementById('pelement');
pElement.addEventListener('mouseenter',(event)=>{
change('red');});
pElement.addEventListener('click',(event)=>{
change('blue');});
pElement.addEventListener('mouseleave',(event)=>{
change('white');});
</script>
</body>
</html>
なお、イベントは、受け取ったオブジェクトからDOMの木構造 の Document オブジェクトまでのオブジェクトのパスを辿るように、イベ ントが伝搬されます。これはバブリングと呼ばれます。
また、イベント自体は予め定義されていたものだけではなく、自由に文字 列で定義して、プログラムから dispatch できます。
バブリングの例 ex1.html
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>バブリングの例</title>
<style>
.red {background-color: red;}
.blue {background-color: blue;}
.white {background-color: white;}
</style>
</head>
<body>
<h1 class="white">バブリングの例</h1>
<ol id="www">
<li id="rrr">red</li>
<li id="bbb">blue</li>
</ol>
<script>
function dispatchred(e) {
this.dispatchEvent(new Event("r", { bubbles: true }));
}
function dispatchblue(e) {
this.dispatchEvent(new Event("b", { bubbles: true }));
}
function dispatchwhite(e) {
this.dispatchEvent(new Event("w", { bubbles: true }));
}
function setblue(e) {
this.setAttribute("class", "blue");
}
function setred(e) {
this.setAttribute("class", "red");
}
function setwhite(e) {
this.setAttribute("class", "white");
}
function setEventListeners(elem, disp) {
elem.addEventListener("click", disp, { capture: true });
elem.addEventListener("w", setwhite, { capture: true });
elem.addEventListener("b", setblue, { capture: true });
elem.addEventListener("r", setred, { capture: true });
}
setEventListeners(document.body, dispatchwhite);
setEventListeners(document.getElementById("www"), dispatchwhite);
setEventListeners(document.getElementById("rrr"), dispatchred);
setEventListeners(document.getElementById("bbb"), dispatchblue);
</script>
</body>
</html>
onload など、ブラウザの状態などにより、イベントが生成された場合、 それに対応させたプログラムを実行させることができます。
例12-3はJavaScriptの読む位置やオプショ ンにより、参照できる範囲が異なることを示しています。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>Defer テスト</title>
<style>
.redmark {background:red;}
</style>
<script>
// × head では DOM は見えない
document.getElementById("id1").setAttribute("class","redmark");
</script>
</head>
<body onload="document.getElementById("id2").setAttribute("class","redmark");">
<!-- 〇 onload は全てを読んでから実行される -->
<p id="id1">1</p>
<p id="id2">2</p>
<p id="id3">3</p>
<script>
// 〇 前のDOMは読める
document.getElementById("id3").setAttribute("class","redmark");
</script>
<script>
// × 後ろのDOMは読めない
document.getElementById("id4").setAttribute("class","redmark");
</script>
<p id="id4">4</p>
<script defer>
// × defer は src を指定するときだけ
document.getElementById("id5").setAttribute("class","redmark");
</script>
<p id="id5">5</p>
</body>
</html>
template 要素を使うと、HTML 文書内に、レンダリングはされずに、プロ グラムから参照可能な文書片を埋め込めます。 すると、その文書片をコピーすることにより、文書片の構造をそのまま文 書を構成する構造として使用することができます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>template テスト</title>
<style>
th,td {border: 1px solid black;}
</style>
</head>
<body>
<h1>テンプレートテスト</h1>
<table>
<thead>
<tr><th>商品番号</th><th>商品名</th><th>単価</th></tr>
</thead>
<tbody>
<template id="shohin">
<tr><td id="no"></td><td id="name"></td><td id="price"></td></tr>
</template>
</tbody>
</table>
<script>
const data =[
{"no": 999001, "name": "リンゴ", "price": 200},
{"no": 999002, "name": "みかん", "price": 100},
{"no": 999003, "name": "もも", "price": 500}
];
</script>
<script>
const temp = document.getElementById("shohin");
for(let i=0; i< data.length; i++){
const d = data[i];
const c = temp.content.cloneNode(true);
for(let key in d){
c.querySelector("#"+key).innerText=d[key];
}
temp.parentNode.appendChild(c);
}
</script>
</body>
</html>
camvas 要素は width と height 属性を指定することで、画面上に描画領 域を作り、JavaScript の API によりグラフィックを描画することができ ます。
canvas 要素に対して、 getContext でコンテキストを取得した後、 そのコンテキストに対して描画に関する API を指定して描画します。
一方、線を書くには beginPathを宣言した後、 moveToで始点を決めた後 lineTo で通過点を指定していき、最後に stroke で描画します。
一方、文字を書く場合は、font でフォントを指定した後、 fillText や strokeText で文字を書きます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>cambus test</title>
</head>
<body>
<p>aaa</p>
<canvas id="imgcanvas" width="600" height="500">
<p>ccc</p>
</canvas>
<p>bbb</p>
<script>
const imgcanvas=document.getElementById("imgcanvas");
if(imgcanvas.getContext) {
const ctx=imgcanvas.getContext('2d');
ctx.fillStyle="rgba(255,0,0,1)";
ctx.strokeStyle="#000000";
ctx.fillRect(0, 0, 100, 100);
ctx.strokeRect(0, 0, 100, 100);
ctx.fillRect(50, 50, 100, 200);
ctx.strokeRect(80, 130, 200, 100);
ctx.beginPath();
ctx.moveTo(0,100);
ctx.lineTo(200,200);
ctx.stroke();
ctx.font = "16pt 'Arial'";
ctx.fillText("abc",250,50);
ctx.strokeText("xyz",250,150);
}
</script>
</body>
</html>
与えたデータの配列に関して、折れ線グラフを書くプログラムを作れ
<script>
const data=[3,4,2,6,10,3];
</script>
アナログの針を表示する時計を作成せよ。但し、 setInterval 関数は、第一引数の関数を第二引数で指定したミリ秒ごとに実行するものとする。
与えたデータの配列に関して、折れ線グラフを書くプログラムを作れ
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>折れ線グラフ</title>
</head>
<body>
<canvas id="graph" width="600" height="400"></canvas>
<script>
const data = [3, 4, 2, 6, 10, 3];
</script>
<script>
const graph = document.getElementById("graph");
if (graph.getContext) {
const ctx = graph.getContext('2d');
ctx.fillStyle = "rgba(255,0,0,1)";
ctx.strokeStyle = "#000000";
ctx.beginPath();
ctx.moveTo(0, 100);
for (let i = 0; i < data.length; i++) {
ctx.lineTo(i * 100+100, 200 - data[i] * 10);
}
ctx.stroke();
}
</script>
</body>
</html>
アナログの針を表示する時計を作成せよ。但し、 setInterval 関数は、第一引数の関数を第二引数で指定したミリ秒ごとに実行するものとする。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>時計</title>
</head>
<body>
<p id="debug"></p>
<canvas id="tokei" width="600" height="400"></canvas>
<script>
function plothari(ctx, deg, radius) {
deg -= Math.PI / 2;
ctx.fillStyle = "rgba(255,0,0,1)";
ctx.strokeStyle = "#000000";
ctx.beginPath();
ctx.moveTo(300, 200);
ctx.lineTo(Math.cos(deg) * radius + 300,
Math.sin(deg) * radius + 200);
ctx.stroke();
}
var tokei = document.getElementById("tokei");
var debug = document.getElementById("debug");
debug.textContent = "aaa" + tokei.getContext;
if (tokei.getContext) {
var ctx = tokei.getContext('2d');
function tick() {
var date = new Date();
debug.textContent = "" + date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds();
ctx.clearRect(0, 0, 600, 400);
plothari(ctx, (date.getHours() % 12) * Math.PI / 6, 100);
plothari(ctx, date.getMinutes() * Math.PI / 30, 150);
plothari(ctx, date.getSeconds() * Math.PI / 30, 200);
}
setInterval(tick, 500);
}
</script>
</body>
</html>