第 12 回 ブラウザでのHTML, DOM, Javascript

本日の内容


このドキュメントは http://edu.net.c.dendai.ac.jp/ 上で公開されています。

12-1. ブラウザ内のJavaScript

ブラウザでJavaScriptのプログラムを動かすには、HTMLファイルに埋め込 むか、HTMLファイルよりプログラムファイルを読み込みます。 これには、HTMLの script 要素を使用します。

  1. head内でscript要素内にプログラムを埋め込んだり、ファイルを指定 した場合、body内の要素が表示される前に、プログラムが実行されます。 但し、body内のDOMにはアクセスできません。
  2. body内でscript 要素が指定されたら、その場でプログラムが実行さ れます。DOMについては、すでに定義されたものだけにアクセスできます。
  3. body 内でscript要素が defer 属性付きで指定された場合、bodyが すべて読み込まれた後に、プログラムが実行されます。
  4. body 内でscript要素が async 属性付きで指定された場合、bodyの 読み込みとは独立にプログラムが読み込まれ、実行されます。

そのため、DOMに対するプログラムを動作させるには、次のような方法が あります。

  1. HTMLのbody の最後にscript要素を記述する
  2. body 内のscript要素にdefer 属性を指定する
  3. イベントハンドラコンテンツ属性である onload をbodyに指定し、必 要なプログラムをコールバック関数で与える。

さらに、表示したHTML文書に対して、ユーザからのインタラクティブな操 作に対応するには、 ユーザ操作などで生じるイベントに対応した動作を記述する必要がありま す。 特定の要素に対するイベントを処理するプログラムを書くには、 その要素にイベントハンドラコンテンツ属性を指定し、 必要な処理をイベントハンドラとして渡す必要があります。

HTML内の JavaScriptの実行

HTML からJavaScript を動かすには次の方法があります。

  1. head要素内の script 要素
  2. body要素内の script 要素
  3. イベントによる駆動

HTML内のJavaScriptの埋め込み

ブラウザで HTML の読み込みにより、プログラムを動かしたい場合、 以下の方法がある。

  1. JavaScript のプログラムを script 要素内に記述
  2. script の src 属性にURLを書き、読み込みを指定
  3. イベントハンドラ属性に直接プログラムを書く

属性にプログラムを直接書く場合、ダブルクォーテーションマーク(")を " でエスケープする必要があります。

script 要素の属性 src で URL でファイルを指定する場合、読み込み方 を指定することができる。 デフォルトでは、指定された場合、そのファイルを読み込んでプログラム を実行します。 そのため、プログラムの内容が単なる関数定義で、ユーザ操作に連動する ような場合、ファイル読み込みの時間にHTMLの表示などが止まることがあ ります。

  1. script 要素に defer 属性が指定されると、文書をすべて読み込んでから、 プログラムが実行されるようになります。
  2. さらに、 async 属性が指定されると、指定したファイルのプログラムは 実行可能になったときに実行されます。

head要素内の script 要素

head 内に置かれた script は body 文書が読み込まれる前に実行されま す。 そのため、文書に関する DOM はすべて参照できません。 プログラムと文書を独立させるために、プログラムを置くのには有効です が、文書を直接操作するプログラムは書けません。

body要素内の script 要素

body 要素内の script 要素でプログラムが置かれた場合、デフォルトの 動作では、そこまで文書が解釈された状態で実行されます。 そのため、script 以降のDOM要素は参照できません。 DOM全体に作用させるには defer 属性を指定させるか、 body に onload で関数を呼び出すかする必要がある。

12-2. イベント

イベントによる駆動

ブラウザでは、クリックされるなどの様々なイベントが発生します。 イベントはオブジェクトで、 DOMのオブジェクトに登録されたイベントハンドラと言う、イベントを引 数とした関数オブジェクトが呼び出されます。

イベントに関しては、 HTML standard の Eventsの章 にまとめてあります。 但し、ここにあるほかにも、メディア要素イベントやドラッグアンドドロップ イベントが定義されています。 8.1.8.2 章のEvent handlers on elements, Document objects, and Window objectsにも載ってます。 主要なものを下記に示します。

load
ドキュメントを読み終わったとき
click
マウスクリックしたとき
submit
Submitボタンを押したとき
reset
resetボタンを押したとき
change
変更があったとき
select
選択されたとき
mouseenter
領域にポインタが入ったとき
mouseleave
領域からポインタが外れたとき

イベントハンドラコンテンツ属性

HTMLの要素には共通のイベントハンドラコンテンツ属性がたくさん定義さ れています。 "on"+イベント名がそれぞれのHTML要素で許されていて、イベントハンドラを 指定できます。

例12-1

古くは、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 できます。

例12-2

バブリングの例 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

例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(&quot;id2&quot;).setAttribute(&quot;class&quot;,&quot;redmark&quot;);">
        <!-- 〇 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>

12-3. template, slot, カスタム要素

template

template 要素を使うと、HTML 文書内に、レンダリングはされずに、プロ グラムから参照可能な文書片を埋め込めます。 すると、その文書片をコピーすることにより、文書片の構造をそのまま文 書を構成する構造として使用することができます。

例12-4

template 使用例


<!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>

12-4. Camvas

Canvas

camvas 要素は width と height 属性を指定することで、画面上に描画領 域を作り、JavaScript の API によりグラフィックを描画することができ ます。

canvas 要素に対して、 getContext でコンテキストを取得した後、 そのコンテキストに対して描画に関する API を指定して描画します。

strokeStyle
線種を決める
fillStyle
塗りつぶしのパターンを書く
fillRect
塗りつぶしの長方形
strokeRect
長方形の枠を描く

一方、線を書くには beginPathを宣言した後、 moveToで始点を決めた後 lineTo で通過点を指定していき、最後に stroke で描画します。

一方、文字を書く場合は、font でフォントを指定した後、 fillText や strokeText で文字を書きます。

例12-5

Canvas使用例


<!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>

12-5. Cookie

12-6. データ、ストレージ

12-7. 演習

演習12-2

与えたデータの配列に関して、折れ線グラフを書くプログラムを作れ


<script>
    const data=[3,4,2,6,10,3];
</script>

演習12-3

アナログの針を表示する時計を作成せよ。但し、 setInterval 関数は、第一引数の関数を第二引数で指定したミリ秒ごとに実行するものとする。

12-8. 演習の解答

演習12-2

与えたデータの配列に関して、折れ線グラフを書くプログラムを作れ

解答例


<!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>

演習12-3

アナログの針を表示する時計を作成せよ。但し、 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>

坂本直志 <sakamoto@c.dendai.ac.jp>
東京電機大学工学部情報通信工学科