JavaScript の覚え書き

初版作成  2018.1.27
最終更新  2018.02.01


◆◆ そもそも何のためにあるのか? ◆◆

html ファイルの内容を動的に書き換えるためにある。

◆◆ スクリプトの埋めこみ方 ◆◆

下記のように head の中にスクリプトを書くのが基本らしい。
body の中に書くとロードされた直後に実行される。

<html>
<head>
<title>タイトル</title>
<script>
ここにスクリプトを書く。
</script>
</head>
<body>

</body>
</html>

◆◆ デバッグ ◆◆

ブラウザにはエラーが表示されないので、デバッグがしずらい。

Chrome が使いやすい。

F12 を押すとデバッグ領域が表示される。
console タブにエラーが表示される。

console.log("a = %d",a)

で console に出力する。

alert("文字列")  でメッセージボックスを作成して出力


◆◆ 最初に覚えるべきこと ◆◆

・関数名と変数名は同じ名前を使えない!!!

これでハマることが多いので注意!

・大文字小文字の区別がある

innerHTML を間違えて innerHtml と書いてしまうと、
何も起こらない。


◆◆ 発見しづらいバグ ◆◆

プロパティを間違えてもエラーは起こらない。
例えば

obj.style.backgroundColor = "red" を
obj.style.backgroundColo  = "red"

と間違えると何も起こらない。
デバッガにもエラーが表示されないので、
この手の誤りは発見しづらい。


◆◆ 定番的な使い方 ◆◆

head に書くと、html の残りの部分を読み込みながら、実行が開始される。
ページを読み込み終えた後、実行するために、以下のように書く。

<script>
window.onload = init; // html ファイル読込終了直後に実行   init() ではエラー

function init(){
   初期化処理
}
</script>


◆◆ 基本構文 ◆◆

if, for, while は C と全く同じ
ループからの脱出も C と同じく break, continue

ラベル付きの break により多重ループから一気に脱出可能。
ラベルの位置に注意!
脱出するループの for 文の手前に書かないとエラーになる

label1:
for(i = 0; i < 4; i++){
    for(j = 0; j < 4; j++){
        document.write("i j = " + i + " " + j + "  ");
        if ( i == 2 && j == 1 ) {
            break label1;
        }
    }
}

◆◆ 変数 ◆◆

宣言不要。グローバル変数になる。
var a; のように宣言すると、ローカル変数になる。
for(var i = 0; i < n; i++){  とするとスコープは for 内

グローバル変数はバグの元となるので、
var を使うのが望ましい。

整数と実数の区別はない。

変数に型はないが、Excel のセルように、中に入っている
データは型を持つ。例えば「数値」「文字列」「オブジェクト」

a = 123;
b = "123";
document.write("型は " +(typeof a));

id と同一の変数名を使用するとエラーになるので注意!!!
プログラムは実行可能なので、バグの原因となる。


◆◆ 定数 ◆◆

a = 0x1f     // 16進
b = 1.2e-4   // 1.2 × 10^(-4)


◆◆ コメント ◆◆

C++ と同じ
//  コメント
/*  コメント */

◆◆ print ◆◆

i = 10;
x = 3.4;

print "i = " + i + "   x = " + x;

JavaScript には C の printf に対応する書式付きの
print 機能はない。自分で実装する必要がある。

github に sprintf.js というプログラムがある。

https://github.com/alexei/sprintf.js

◆◆ 関数 ◆◆

function rect_area(height, width){
    var area;               // 関数内だけで使う変数はローカルにする
    area = height * width;
    return area;
}

a = rect_area(2,5);

返り値なしの return 文を使うこともできる
 (関数の途中で実行を打ち切る場合)。


◆◆ 文字型オブジェクト ◆◆

str = new String("文字です");
document.write("<p>「" + str + "」の長さは " + str.length + " です。");

◆◆ 一般のオブジェクト ◆◆

obj1 = new Object();
obj1.name = "yabu";
obj1.age = "45";

プロパティを付け加えることができる。

obj2 = obj1;
obj2.name = "mitukuri";

代入ではなく参照なので obj1.name の結果は "mitukuri" になっている。


◆◆ 配列 ◆◆

添字が整数の配列

a = new Array(3);   3 を省略してもよい

a[0] = "a";
a[1] = "b";

のように宣言なしで書いてもよい。

a[] = "c";

とすると自動的に a[2] に割り当てられる。
いきなり

a[3] = "c";

とすると、a[3] のみが生成される。次に

a[] = "d";

とすると a[4] に割り当てられる。

b = new Array("yabu", "mitukuri", "sera");
c = ["yabu", "mitukuri", "sera"];

連想配列

name = new Array();
name["yabu"] = "tetsuro";
name["mitukuri"] = "kazuhiro";
name["sera"] = "keita";


◆◆ html 要素の動的な変更 サンプルプログラム ◆◆

ボタンを押すと、動的に要素が変化する。

<html>
<head>
<title>JavaScript の練習</title>
<script>
window.onload = init;

function init(){
    p1 = document.getElementById("id-p1");
    canvas1 = document.getElementById("id-canvas1");
    dc = canvas1.getContext('2d');
}

function b1_click(){
    p1.innerHTML = "かきくけこ";
}

function b2_click(){
    canvas1.style.backgroundColor = "rgb(0,255,255)";
    dc.beginPath()
    dc.rect(50,10,100,150);
    dc.fillStyle = "rgb(255,255,0)";
    dc.fill();
}
</script>
</head>
<body>
<p>JavaScriptの練習です。</p>
<canvas id="id-canvas1" width="300px" height="300px" style="background-color: beige"></canvas>
<p>
<input type="button" value="ボタン1" onclick="b1_click()">
<input type="button" value="ボタン2" onclick="b2_click()">
</p>
<p id="id-p1">表示エリア1</p>
</body>
</html>

◆◆ name を使う方法 ◆◆

要素を識別するのに id ではなく name を使う方法もある。
name を使うと、複数の要素に同じ name をつけることができ、
getElementsByName() を使うと、配列として得られるらしい。

ただし、<div> には name 属性がないなど、ややこしいので、
name はいざ使うときに調べることにする。


◆◆ イベント関数の定義法 ◆◆

(1) タグ内に直接書き込む方法

<span onclick="func(arg)">

引数が無い場合は func() と書く。func はダメ。

複数の要素が同じ関数を呼び、呼び元によって
異なった値を関数の引数に加えたいとき、
(1) の形式が良いと思われる。

(2) を使う場合、引数は固定 (event) なので、
要素ごとに別の関数を定義し、
その関数が共通関数を呼ぶ必要がある。

(2) id を使う方法

<span id="span1">この領域</span>

obj = document.getElementById("span1");

この方法で呼ばれる関数の引数は固定でイベントの引数が入る。
ユーザーが定義する引数を使うことはできない。

(2a) 無名関数を使う方法

obj.onclick = func1(e){
   処理内容
}

(2b) 関数を別の場所で定義する

obj.onclick = func2;

func2(e){

}

(2c) イベントリスナを定義する(この方法が最も分かりやすい)

obj.addEventListener("click"    ,click);
obj.addEventListener("mousemove",move);

window.addEventListener("keydown",key); // window はデフォルトで存在する
                                        // keydown は window しか受けられない
function click(e){
    var x = e.offsetX;
    var y = e.offsetY;
}

function move(e){
    var x = e.offsetX;
    var y = e.offsetY;
}

function key(e){
    var code = e.keyCode;
}


◆◆ イベントの種類 ◆◆

<input type="text" onkeyup="func(arg)" id="text1">
<div onclick="func(arg)" style="background:beige;" id="div1">

obj = document.getElementById("text1");
obj.onmouseover = func1;
obj.onmouseout = func2;
obj.value = "str"          // テキストボックスの内容

obj = document.getElementById("div1");
obj.style.backgroundColor =
obj.style.left =            // position:absolute が必要
obj.style.top =
obj.style.display = "block" or "none"
obj.innerHTML = "moji";    // <div> <p> <span> など

タグの中に書く時は on が付く。
関数の定義のときもイベントは on がつく。


◆◆ オートコンプリート ◆◆

デフォルトで on になっているので、ヒント機能を
テキストボックスに付加するとき

<input type="text" id="myid" name="myname" size="5" autocomplete="off">


◆◆ canvas の使い方 ◆◆

作成
<canvas id="canvas1" width="300px" height="300px"></canvas>

オブジェクトとデバイスコンテキストの取得
canvas = document.getElementById("canvas1");
d = canvas.getContext('2d');

線を描く
d.beginPath();
d.moveTo(x,y);
d.lineTo(x2,y2);
d.lineTo(x3,y3);
d.lineWidth = 5;
d.strokeStyle = "rgb(255,0,0)";
d.stroke();

長方形
d.beginPath();
d.rect(x, y, xlen, ylen);
d.fillStyle = "rgb(255,255,0)";
d.fill();
d.lineWidth = 1;
d.strokeStyle = "rgb(0,0,0)";
d.stroke();

円(扇形)
d.beginPath();
d.arc(x,y,radius,start_angle,end_angle,true);
d.fillStyle = "rgb(255,255,0)";
d.fill()
d.lineWidth = 10
d.strokeStyle = "rgb(0,255,0)";
d.stroke();

背景色で塗りつぶす
d.clearRect(0,0,300,300);

キャンバス上のマウスの位置を取得

canvas.addEventListener("click"    ,click);
canvas.addEventListener("mousemove",move);

function click(e){
    var x = e.offsetX;
    var y = e.offsetY;
}

function move(e){
    var x = e.offsetX;
    var y = e.offsetY;
}

キャンバスの左上位置の取得(使い道はなさそうだが・・・)
canvas_location = canvas.getBoundingClientRect();
canvas_location.left   x 座標
canvas_location.top    y 座標


◆◆ 再読み込み ◆◆

location.reload();


◆◆ 指定された url へジャンプ ◆◆

location.href=(url)

url を http:// から始めない場合は、
現在の位置からの相対位置

◆◆ タイマー関係 ◆◆

setInterval と setTimeout の 2 つの方法がある。

setInterval(func1,5000);   // 5000 ms ごとに func1 を実行


timer = setTimeout(func2,5000);   // 5000 ms 後に func2 を実行

clearInterval(timer);             // それを取り消す


◆◆ Web サーバと通信をするサンプルプログラム(非同期) ◆◆

このサンプルでは web サーバは Raspbery Pi
GPIO を使ってハードウェアにアクセスして、
その返答を返してくる。

---------------- html ファイル -----------------

------------ head ---------

<style>
body {
  font-size: xx-large;
  margin: 20px;
  background-color: #ffffd0;
}
p {
  line-height: 2.0;
}
p.button {
  padding: 0em 0em 1.3em 0em; /* 上 右 下 左*/
}
span.blue {
  background-color: #a0e0ff;
  padding: 1em 1.5em;           /* 上下 左右 */
}
</style>

----------- body ------------

<p>
<span id="button1" class="blue" onclick="ondo_sokutei()">温度</span>
</p>
<div style="background-color: beige;">
<p id="ondo_disp">
温度 =
</p>
</div>
<script type="text/javascript">
button1     = document.getElementById("button1");
ondo_disp   = document.getElementById("ondo_disp");

var xhr;

function ondo_sokutei(){
    xhr = new XMLHttpRequest();
    file = "cgi-bin/ondo.py";
    xhr.open("GET", file, true);
    xhr.onreadystatechange = updatePage;
    xhr.send(null);
}

function updatePage(){
    if ( xhr.status == 200 ){
        text = xhr.responseText;
    } else {
        text = "status = " + xhr.status;
    }
    ondo_disp.innerHTML = text;
}
</script>

----------------- cgi-bin/ondo.py ----------
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
#   apache2:       www-data に visudo で権限を与えておくこと
#

import sys
import os
import cgi
import commands
import serial

#-------------- 温度測定 -------------
#
# root の権限が必要なため ondo_sub.py を使う

cmd = "sudo ./ondo_sub.py"
ret = commands.getoutput(cmd)

#-------------- 結果の送信 ------------

# 最初の 2 行を忘れると apache2 がエラーを出す

print "Content-type: text/html"
print ""
print "温度 = " + ret + " 度"

------------ cgi-bin/ondo_sub.py ------------
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# シリアル端子の向こう側に Arduino が接続されており、
# Arduino に LM35 が接続されている。
# Arduino から 1 行返答が来る。
# その返答を受けて 1 行標準出力に出力する。
#
# GPIO を制御するプログラムは root の権限が必要なので、
# cgi (apache2 の場合 www-data の権限で動作) から sudo で
# 本プログラムを呼び出す
#
import serial

vcc = 4.95

rs = serial.Serial('/dev/ttyAMA0', 9600, timeout=10)

rs.write("t")
line = rs.readline()  # 行末は 13 10
rs.close()

line = line.rstrip()
val = int(line)
ondo = val * ( vcc * 1000 / 1024.0 ) / 10

ondo_str = "%5.1f" % (ondo)

print ondo_str

-----------------------------------------------

arduino のプログラムは
これ


◆◆ 日付と時刻の取得 ◆◆

d = new Date();
year    = d.getFullYear();
month   = d.getMonth() + 1;
day     = d.getDate();
hours   = d.getHours();
minutes = d.getMinutes();
seconds = d.getSeconds();