技術の犬小屋

Webプログラミングを餌に生きる犬のメモ帳

仕事でJavaScriptからDOMを通してHTMLを操作したことはあったけど、今回改めて勉強したのでメモしておく。
 

DOMとは

DOMとは、「Document Object Model」の略である。プログラムから「HTML 文書」や「XML 文書」を利用するための、標準化された仕様を指す。
DOMが生まれた背景として、JavaScriptのようなクライアント側のプログラムを使ってHTMLの内容を動的に変化させる「ダイナミックHTML」という概念の登場がある。登場初期の頃はブラウザごとに制御方法が違っていたので、ブラウザごとに専用のJavaScriptのコードを用意する必要があったが、それが煩わしかったので、制御方法を統一するためにDOMという仕様が生まれた。
 

DOMを構成するもの

HTML文書は、「タグ」「属性」「値」「文書」などの部品で構成されている。この部品のことをノードと呼ぶ。HTML文書がブラウザに読み込まれると、JavaScript 内でこれらの部品が「DOMオブジェクト」として自動的に生成される。
この「DOMオブジェクト」から、メソッドやプロパティを利用する事が出来、メソッドやプロパティを通してHTMLを操作する。
 

ノードとは

ノードとは、節、結節(点)、節点、交点、中心点、集合点、こぶ、膨らみ、などの意味を持つ英単語。ネットワークや木構造など、複数の要素が結びついてできた構造体において、個々の要素のことをノードという。
HTMLでは、開始タグと終了タグの間に別のタグを挟み込むと、タグをネスト(入れ子)にすることが出来るが、これを行うとタグ間で木構造の親子関係を構築することが出来る。今回の場合は「タグ」「属性」「値」「文書」といったものがノードに当たる。
 

ノードの種類について

「DOMオブジェクト」であれば、共通して利用できる基本的な機能として、Nodeというインターフェースが定められている。「Nodeインターフェース」から派生して、12種類のインターフェースが用意されているが、主に利用するインターフェースは以下の4つである。

インターフェース 解説
Document ドキュメント、階層のルートに相当
Element エレメント、要素に相当
Attr アトリビュート、属性に相当
Text テキストノード、タグ以外の文字データに相当

 

Windowオブジェクトとは

Windowオブジェクトとは、ブラウザそのものの情報を保持しているオブジェクトである。例えば、ブラウザの高さのプロパティ(outerHeight)、ブラウザの幅のプロパティ(outerWidth)、アクセスしている位置情報(Location)、閲覧しているHTMLの情報(Document)などを保持している。
 

Documentオブジェクトとは

現在閲覧しているHTML自体を保持しているオブジェクトである。DocumentオブジェクトはWindowオブジェクトに内包されている。DOM(document Object Model)では、documentオブジェクトを通して、個々の要素を取得・操作することが出来る。
 

DOMの操作方法

データの取得方法

メソッド名 解説
getElementById IDをキーにして要素を操作する。
getElementsByClassName クラス名をキーにして要素を操作する。
getElementsByTagName タグ名をキーにして要素を操作する。

 

記述例

document.getElementById("ttl");
document.getElementsByClassName("student");
document.getElementsByTagName("h1");

 

サンプル

処理ボタンをクリックすると、アラートで「成績表」と表示する。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
<script type="text/javascript">
function htmlChange() {
   var h1;
   h1 = document.getElementById("ttl").innerHTML;
   alert(h1);
}
</script>

</head>
<body>
<div id="ttl_box"><h1 id="ttl">成績表</h1></div>
<table id="tbl">
<tr><th>名前</th><th>成績</th></tr>
<tr><td class="student">田中</td><td class="score">100</td></tr>
<tr><td class="student">山本</td><td class="score">90</td></tr>
<tr><td class="student">小山</td><td class="score">70</td></tr>
<tr><td class="student">大木</td><td class="score">85</td></tr>
</table>
<div id="add"></div>
<input type="button" value="処理" onclick="htmlChange()" />
</body>
</html>

 

要素の追加・削除方法

メソッド名 解説
createElement 要素を追加する。引数として、追加するタグの名前を指定する。
createTextNode タグの中に含まれるテキスト部分を作成する。
appendChild 子要素として追加する。
removeChild 子要素を削除する。
deleteRow テーブルの要素を削除する。

 

サンプル

処理ボタンをクリックすると、「<strong>平均は86.25点です</strong>」を「<div id=”add”></div>」の子要素として追加し、「<div id=”ttl_box”></div>」の子要素の「<h1 id=”ttl”>成績表</h1>」を削除する。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
<script type="text/javascript">
function htmlChange() {
    var heikin;
    heikin = document.createElement('strong');
	var text;
    text = document.createTextNode('平均は86.25点です');
	heikin.appendChild(text);
	var add;
    add = document.getElementById("add");
	add.appendChild(heikin);
	
	ttlBox = document.getElementById("ttl_box");
	ttlBox.removeChild(ttlBox.lastChild);
}
</script>
</head>
<body>
<div id="ttl_box"><h1 id="ttl">成績表</h1></div>
<table id="tbl">
<tr><th>名前</th><th>成績</th></tr>
<tr><td class="student">田中</td><td class="score">100</td></tr>
<tr><td class="student">山本</td><td class="score">90</td></tr>
<tr><td class="student">小山</td><td class="score">70</td></tr>
<tr><td class="student">大木</td><td class="score">85</td></tr>
</table>
<div id="add"></div>
<input type="button"  value="処理" onclick="htmlChange()" />
</body>
</html>

 

CSSを操作する

CSSを変更するには、styleプロパティを利用する。
 

記述例

document.getElementById("ttl").style.color = "red";
document.getElementById("ttl").style.borderBottom = "solid 3px #ccc";
document.getElementById("ttl").style.backgroundColor = "#FFFFCC";
document.getElementById("ttl").style.fontSize = "3em";

 
 
以上
 
 
参考
JavaScriptプログラミング講座【ドキュメントオブジェクトモデル(DOM)について】
JavaScriptでDOMを操作する方法【初心者向け】
ノードとは|node – 意味/定義 : IT用語辞典
学校の教科書

各プログラミング言語には、1度しか使わない様な用途で作成するクラスやメソッドには、名前を付けずにそれを使用する方法が用意されていることに気付いた。今回はJavaScriptの関数に名前を付けずに使用する方法を勉強したのでメモしておく。
 

関数とは

関数とは、同じ処理を何度も使い回したいときに使用するものである。デフォルトの関数定義は以下のような書式となる。

<script>
function 関数名( ) {
    処理
}
</script>

 

無名関数とは

無名関数とは、名前の無い関数のことである。1度しか使わない様な用途の関数を作りたいときに使用する。逆に言うと、1度しか使わないのでわざわざ名前を付ける必要が無い。

<script>
//関数定義
var 変数名A = function (引数){
    処理
}

//関数呼び出し
var 変数名B = 変数名A(引数);
console.log(変数名B);
</script>

 

<script>
//関数定義
var sum = function (a,b){
    var result = a + b;
    return result;
}

//関数呼び出し
var answer = sum(1,2);
console.log(ancer);
</script>

 

即時関数とは

即時関数とは、無名関数の1種である。無名関数と同じで関数に名前を付けずに定義することが出来、加えて関数の呼び出しも省略することが出来る。

<script>
//関数定義
var 変数名 = (function (引数){
    処理
})(引数);

console.log(変数名);
</script>

 
関数自体を()で囲み、関数に与えたい引数を関数の後ろに記述し、それを変数に代入する。
 

<script>
//関数定義
var sum = (function (a,b){
    var result = a + b;
    return result;
})(1,2);

console.log(sum);
</script>

 
 
以上
 
 
参考
JavaScriptで即時関数を使う方法【初心者向け】

先日、仕事でJavaのSwingを書いていたところ、匿名内部クラスを利用しているコードに遭遇した。覚えておくと便利そうなので、とりあえずメモしておく。
 

匿名内部クラスとは

匿名内部クラスとは、「その場限り」のサブクラスもしくは実装クラスを作る方法である。new スーパークラス名( コンストラクタの引数 ) { /* 実装 */ }という書式で、「名前のない」クラスを作ることができる。
匿名クラスは必ず、スーパークラスのサブクラスか、インターフェイスの実装クラスとして作らなければならない。単独のクラスとして作る場合には、Objectクラスのサブクラスとして作ることになる。スーパークラスの代わりにインターフェイスを使用し、その実装クラスを作ることもできる。
また、匿名クラスは「名前のないクラス」のため、参照型変数を作ることができない。そのため、基本的に「オーバーライドするメソッド」もしくは「実装するメソッド」以外にpublicメソッドを作ることはない。新規に追加したpublicメソッドを外から呼ぶ方法がないためである。
つまり、匿名クラスは、サブクラスもしくは実装クラスを手っ取り早く作る方法であり、それ以外の目的に使うことは難しいということである。
 

どういった場合に使うか

匿名内部クラスは、「ある1箇所でしか使わないような、わざわざクラスを作るほどでもないようなコード」を書く場合において、とても短く書けて便利である。
 
例えば、JavaのSwingでJTableのセルの見た目を変更するには、新しくTableCellRendererクラスを継承したクラスを定義しなければならないが、今回限りしか使わない場合、以下のように匿名内部クラスを用いて定義することが出来る。

table.getColumnModel().getColumn(0).setCellRenderer(new TableCellRenderer() {
    JTableHeader header = adaptee.tableHeader;

    @override
    public Component getTableCellRendererComponent(JTable _table, Object _value, boolean _isSelected, boolean _hasFocus, int _row, int _column) {
        return header.getDefaultRenderer().getTableCellRendererComponent(_table, _value, _isSelected, _hasFocus, _row, _column);
    }
});

 
 
以上
 
 
参考
匿名クラスとは : JavaA2Z

JavaのコレクションAPIでよく使うんだけど、忘れちゃっていちいち検索していたメソッドをまとめてくれている記事を発見した。大変有難いので引用させて頂いてメモしておく。
 

JavaのコレクションAPIでよく使うメソッド

操作名前 Array(配列) List Map
要素の参照 a[i] get(int index) get(Object key)
要素の変更 a[i]に代入 set(int index, E element) put(K key, V value)
要素の追加 add(E e) put(K key, V value)
要素の削除(インデックス指定) remove(int index) remove(Object key)
要素の削除(要素指定) remove(Object o) 実装必要
要素の全削除 clear() clear()
要素数取得 a.length size() size()
要素の検索 Arrays.binarySearch(Object a, Object key) indexOf(Object o) 実装必要
整列 Arrays.sort Collections.sort(List list) (TreeMapの使用)
新規作成(空) new T[length] Collections.emptyList() Collections.emptyMap()
新規作成(全て同じ要素) 実装必要(Collections.fill()を使用) Collections.nCopies(int n, T o) 実装必要
結合 実装必要 addAll(Collection<? extends E> c) 実装必要
ディープコピー Arrays.copyOf(T original, int newLength) new ArrayList(list)等 new HashMap<K, V>(map)等

 
 
以上
 
 
参考
javaのコレクション系クラスでよく使うメソッドの目的別まとめ – プログラミングのメモ

相変わらずEffective Javaを読んでいる。自分でジェネリクスなクラスを作る場合の書式と利点が分からなかったので調べた。後から見返して分かり辛いと思ったところは加筆する予定。
 
ちなみに基本的なジェネリクスの使用方法である、リスト型 + ジェネリクスの使い方は過去の記事でまとめたので、そちらを参照してほしい。
Javaでは配列よりリスト(ジェネリクス)を選ぶ
 

型引数とは

型引数はクラス宣言・インタフェース宣言で、クラス名・インタフェース名に続いて指定する。型引数は<>で囲み、その中に1つ以上の型変数を定義する。複数個指定する場合には、コンマ(,)で区切る。

public interface Map<K, V> {
  //...
}

 
型変数の命名規則は変数の命名規則と同じだが、英大文字で1字が推奨されている。
定義した型変数は、implements句やextends句、メソッドの引数、返り値だけでなく、インスタンス変数やメソッド内部で用いることもできる。

public class Value<V> {
    private V value=null;
     
    public Value(V value){
        this.value=value;
    }
     
    public V getValue(){
      return this.value;
    }
     
    public String toString(){
        return value.toString();
    }
}

 
この例ではインスタンス変数として、型引数で指定された型のオブジェクトvalueを指定している。またtoString()メソッド内で、valueのメソッドを呼び出している。valueの型はこのクラス作成時には一意には決まっていないが、必ずjava.lang.Objectのインスタンスであるので、java.lang.Objectのメソッドだけは呼び出すことが可能である。もし次のように型引数で指定できるクラスを限定した場合には、呼び出せるメソッドも増える。

import java.util.Date;
 
public class Term<S extends Date, E extends Date> {
    private S date1 = null;
    private E date2 = null;
     
    public Term(S date1, E date2){
        this.date1 = date1;
        this.date2 = date2;
    }
     
    public long calculateSpan(){
        return date1.getTime() - date2.getTime();
    }
}

 
extends句を用いて、型引数としてjava.util.Dateのサブクラスしか認めないようにしている。この場合には、インスタンス変数date1もdate2もjava.util.Dateのインスタンスになる。従ってjava.util.Dateのメソッドも呼び出すことができる。
 
次のように指定することで、上記のジェネリクスなクラスを使用することが出来る。

Term<Time, Time> period = new Term<Time, Time>(new Time(), new Time(5000));

 
このようにすると、Termクラスの型引数S, EにDate型のサブクラスであるTime型を渡すことができ、渡されたTime型はTermクラスの内部で使用される。
 

ワイルドカードで柔軟な型引数を作る

ジェネリクスのワイルドカードは?記号で表し、メソッドの呼び出し時に型引数として利用することができる。ワイルドカードを指定することで、型変数を特定しない操作が出来るようになる。
ジェネリクスのワイルドカードは次のように利用する。

class Value<T> {
    private T value;
    public Value(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }
}

class Test {
    public void printValue(Value<?> obj) {
        System.out.println(obj.getValue());
    }

    public static void main(String args[]) {
        printValue(new Value<String>("dog house"));
        printValue(new Value<Integer>(new Integer(10)));
    }
}

 
printValueメソッドの引数に注目してほしい。引数objはワイルドカードを用いたValue型として宣言されている。この場合、Valueクラスの型引数に指定する型は問わないという意味になる。
 
printValue()メソッドでは、T型の実装に依存するような振る舞いがないということにも注目してほしい。T型の実体は少なくともObject型を継承していることは保障されているため、obj.getValue()メソッドを呼び出す操作は認められている。
 
しかし、一方でワイルドカードの型変数に対して代入する行為はコンパイラによって禁止されている。例えば、次のコードはコンパイルエラーになる。

public static void main(String args[]) {
    Value<?> obj = new Value<String>("dog house");
    obj.setValue("cat house");
}

 

上限と下限を設定する

しかし、単にワイルドカードを使用するだけでは本来ジェネリクスが持っている不変という性質の利点を捨ててしまうことになる。そこで、ワイルドカードに「上限」と「下限」を設定し、一定の範囲をワイルドカードに与えることで、特定のクラスのサブクラスであることを強要、またはスーパークラスであることを強要することができる。この「上限」と「下限」が与えられたワイルドカードを「境界ワイルドカード」と呼ぶ。
 

上限境界ワイルドカードを設定する

境界ワイルドカードのうち、指定した型と同じかその型を継承するサブクラス(サブインタフェース)であることを強要するものを「上限境界ワイルドカード」と呼ぶ。
ワイルドカードに対して上限を設定するには、extendsキーワードを使用する。例えば、Number型を上限として設定したい場合は次のようになる。
 

Value <? extends Number>

 
実際にコードに当てはめると次のようになる。

static void printValue(Value<? extends Number> obj) {
    System.out.println("type = " + obj.getValue().getClass());
    System.out.println("int value = " + obj.getValue().intValue());
    System.out.println("double value = " + obj.getValue().doubleValue());
}

public static void main(String args[]) {
    printValue(new Value<Integer>(100));
    printValue(new Value<Double>(1.23456789));
    //以下はエラーになる
    //printValue(new Value<String>("Kitty"));
}

 
この場合、Valueクラスの型変数Tは不明な型であるが、NumberクラスまたはNumberクラスのサブクラスでなければならないという境界が設定される。そのため、型変数TがString型である場合はコンパイルエラーとなる。
 

下限境界ワイルドカードを設定する

境界ワイルドカードのうち、そのクラス、またはそのクラスのスーパークラスであることを強要するものを「下限境界ワイルドカード」と呼ぶ。
ワイルドカードに対して下限を設定するには、superキーワードを使用する。例えば、String型を下限として設定したい場合は次のようになる。
 

Value <? super String>

 
実際にコードに当てはめると次のようになる。

static void printValue(Value<? super String> obj) {
    System.out.println("type = " + obj.getValue().getClass());
    System.out.println("int value = " + obj.getValue() + "\n");
}

public static void main(String args[]) {
    printValue(new Value<String>("dog"));
    printValue(new Value<Object>(new Integer(10)));
    //以下はエラーになる
    //printValue(new Value<Integer>(new Integer(10)));
}

 
この場合、Valueクラスの型変数Tは不明な型であるが、ValueクラスのT型変数の下限がString型でなければならないという境界が設定される。そのためTにString型のサブクラスや、他の関係のないクラスを指定することはできない。
 

上限と下限どちらを使うか

Java言語は、上限境界ワイルドカードと下限境界ワイルドカードの両方をサポートしているため、どちらを使うのか、そしてそれをいつ使うのかを、どのようにして知ればよいだろうか。これに関してはgetとputの原則(get-put principle)という単純なルールがあり、このルールによって、どちらの種類のワイルドカードを使うのかを判断することができる。
構造から値を取得する (get) だけの場合には 上限境界(extends)ワイルドカードを使い、構造の中に値を格納する(put)だけの場合には下限境界(super)ワイルドカードを使い、そして両方を行う場合にはワイルドカードを使ってはならない。
 
 
以上
 
 
参考
書籍 Effective Java
1. ジェネリクス (3) | TECHSCORE(テックスコア)
ワイルドカード
Java の理論と実践: Generics のワイルドカードを使いこなす、第 2 回