fc2ブログ

JavaScriptの勉強一段落

昨日と今日をかけて、JavaScriptのチートシートをhtmlで編集し直した。
6/25の日記に書いているが、項目の横の矢印画像をクリックすることで詳細表示をオンオフできる。

JavaScriptのチートシートをhtmlで編集し直すだけの作業で、昨日と今日で合計4時間半の時間がかかった。
何でこんなに時間がかかったんだろうと考えたら、jsファイルを修正して「すべての詳細を表示」機能を追加していたのを思い出した。
この機能だが、document.getElementsByNameを<div>タグに対して使っているため、Internet Explorerでは動かない。
面倒なのでIE対応はせず、「←Internet Explorerでは動きません」の文言をボタンの横に追加した。

個人的には結構実用的なチートシートになったと思うが、自分以外の人に役に立つとは思えない。

6/15から始めたJavaScriptの勉強が、とりあえず一段落した。
本当は明日からPHPの勉強に入りたいのだが、もうちょっと見栄えの勉強をしたいので、CSSの勉強を少しやりたい。
スポンサーサイト



この前作ったJavaScriptの無名関数まわりを修正

昨日の日記にまとめた、無名関数を使用する場合のメモリ消費問題を考慮して、この前作ったJavaScriptを修正した。

修正した箇所は
・イベントリスナ設定時になるべく無名関数を使わない。
→そのため、オブジェクトのメソッドをaddEventListenerに渡した場合、メソッド内のthisが定義したオブジェクトにならないことを明示する。
・クロージャを作成した場合、クロージャが参照するスコープ内の不要な変数にnullを設定し、参照を削除。
・window.onloadでセットしたイベントリスナを、window.onunloadで削除する。
このページを参考にした。削除時に対象のfunctionを指定できるように、要素に独自のプロパティを追加して、そこにfunctionオブジェクトを格納した。

修正後のソースコードは以下のようになった。
var jsCheatSheet = {
  //~中略~
  /* onload時の処理
   * <注意>ここではthisはwindowを指す
   */
  init: function(){
    //~中略~
    var listener = function(evt){jsCheatSheet.changeDisp(evt);}
    // 削除用に、オブジェクトのプロパティに該当functionオブジェクトを格納
    elm.addedmtd = listener;
    //dom.addListenerは、addEventListenerとattachEventをラップしたメソッド
    dom.addListener(elm, "click", elm.addedmtd);
    // クロージャからの参照を削除
    elm = null;
    listener = null;
  },
  /* onunload時の処理
   * <注意>ここではthisはwindowを指す
   */
  final: function(){
    //~中略~
    //dom.delListenerは、removeEventListenerとdetachEventをラップしたメソッド
    dom.delListener(elm, "click", elm.addedmtd);
    elm.addedmtd=null;
  },
//~中略~
};

dom.addListener(window, "load", jsCheatSheet.init);
dom.addListener(window, "unload", jsCheatSheet.final);

今日調べたことをまとめてみる

おとといの日記に書いた、「色々と調べたいこと」について今日も調べた。
なんとか結果をまとめてみた。

(1)currentTargetとsrcElementの違い
event.target と window.event.srcElement は同じ要素を指す。(window.eventはIEのみ)
event.target はイベントが発生した要素を、event.currentTarget はイベントリスナをセットした要素を指す。

リスナをセットした要素が子孫要素を持っていなければ、event.currentTarget と event.target は同じ要素を指す。
<event.currentTarget と event.targetが異なる例>
<body onload="init()">
<div id="DIV"><button id="BUTTON">click</button></div>
<body>
において、
function showevent(evt){
  var msg = "event.target.id=" + evt.target.id + "\n" +
    "event.currentTarget.id=" + evt.currentTarget.id;
  alert(msg);
}
function init(){
  var elm = document.getElementById("DIV");
  elm.addEventListener("click", showevent, false);
}
のようにして<div>にイベントリスナをセットした場合、
<button>をクリックすると
event.target.id=BUTTON
event.currentTarget.id=DIV
と表示される。

(2)クロージャとは?
参考資料:猿でもわかるクロージャ超入門 まとめ

JavaScriptでは、関数内で定義された内部関数は、外側の関数のローカル変数を参照できる。
そのような内部関数をクロージャと呼ぶ。
例)
function outer(){
  var x = 1;  // outerのスコープ内で変数を定義
  return function (){   //この関数が「クロージャ」
    alert(x);  // "関数内関数"の中で、outerスコープの変数を参照。
    x = x + 1;
  };
}

outer() が実行されるたびに、無名関数 function (){alert(x);x = x + 1;} および無名関数が参照する変数xが生成される。
この変数xは保持される。
var f1 = outer();  // function (){alert(x);x = x + 1;} のオブジェクトと変数xが生成&初期化される。x = 1
f1();  // "1"をalert後、x = x + 1 が実行される。x = 2
f1();  // "2"をalert後、x = x + 1 が実行される。x = 3
var f2 = outer();  // function (){alert(x);x = x + 1;} のオブジェクトと変数xが生成&初期化される。x = 1
f2();  // "1"をalert後、x = x + 1 が実行される。x = 2
f1();  // "3"をalert後、x = x + 1 が実行される。x = 4
f1とf2は異なるオブジェクトのため、変数xも別々に生成される。

(3)無名関数を使用する場合のメモリ消費問題
参考資料:element.addEventListener - MDC -

イベントリスナをセットする際に無名関数を使用する。
例)
var listener = function(){jsObj.init();}
window.addEventListener("load", listener, false);
この場合、jsObjだけでなく、無名関数 function(){jsObj.init();} がアクセス可能な全ての変数がメモリに保持される。
removeEventListener を呼び出すか無名関数への参照を全て削除すれば、変数の消費するメモリが解放されるようになる。

(4)無名関数使用とIE循環参照の関係
参考資料:Internet Explorer リーク パターンを理解して解決する

無名関数等でクロージャを作成し、クロージャをイベントとして要素にセットする
例)
function AttachEvents(element){
  // クロージャ作成
  var listener = function(){ClickEventHandler.call(element)}
  element.attachEvent("onclick", listener);
}
function ClickEventHandler(){
  // thisでイベント発生元要素を取得
}
function SetupLeak(){
  AttachEvents(document.getElementById("LeakedDiv"));
}
イベントセットにより、id="LeakedDiv" の要素 → クロージャ function(){ClickEventHandler.call(element)} の参照が発生。
クロージャは定義元の関数への参照を保持するので、 クロージャ → element → id="LeakedDiv" の要素
という参照も発生し、循環参照となってメモリリークが発生する。


自分が理解できる程度にはまとめられたが、分かり易いかどうか客観的に判定できない。
間違って理解しているところもあるかもしれない。

ちょっとだけJavaScriptのイベントまわりの勉強

昨日の日記に書いた、「色々と調べたいこと」について調べた。

調べたことをまとめてここに書こうと思っていたが、時間が無いので明日書く。
クロージャの理解に曖昧なところがあるので、明日もうちょっとクロージャについて調べよう。

それにしても、プログラミングの勉強時間が全然取れない。遊んでる時間が多い。
明日は一日の行動履歴をつけて、何に何時間かけているかちゃんと調べてみる。

DOMを使ってイベント処理

昨日の日記に書いたとおり、自分用に書いたチートシートをスッキリと見やすくするJavaScriptを作った。

チートシートを以下のようなHTMLにして書き直した。
項目の横に下矢印アイコンをつける。
項目の詳細を<div>タグで囲む。
この下矢印アイコンをクリックすることで、詳細の表示/非表示を切り替えられるようにした。

実装の概要は以下のとおり
<img>タグにイベントリスナをセット
イベントが発生したら、リスナから渡されたイベントから.currentTargetで発生元要素取得
.parentNode経由で.getElementsByTagName("div")により表示切替<div>要素取得
.classNameを判定し、表示用クラスなら非表示用クラスに、非表示用クラスなら表示用クラスに変更
思ったよりも短いスクリプトで実装することができた。
ただ、イベント発生元の取得方法が、W3C準拠ではないIEでは違うところで手こずった。

とりあえずIEでも動くものができたが、色々と調べたいことが残った。
・currentTargetとsrcElementの違いがよくわかっていない。
・イベントリスナ登録に無名関数を使用する場合のメモリ消費問題
・無名関数使用とIE循環参照の関係
・無名関数とクロージャの関係。そもそもクロージャって何?
明日ここらへんをきっちりと調べよう。

ざっくりとCSSの勉強

ほとんどスタイルシートを使っていないので、CSSの入門サイトを見て基本中の基本を勉強。
CSSの主要なプロパティを一通り眺めた。実際使うのはあまり多くなさそう。

<div>タグの .style.display を変更するJavaScriptを作って、
<div>タグ内の表示/非表示を切り替えるJavaScriptを作ることにした。
それを使って、以前作ったチートシートをスッキリと見やすくしよう。

とりあえず今日は仕様と簡単な設計を行った。実装は明日する。
イベント発生元情報取得あたりでハマりそう。やり方は一応調べたけど、自分がちゃんと理解していない気がする。

JavaScriptのthisにハマる

昨日作ったJavaScriptにちょっと機能追加した。
ソース内に直接記述していたパラメータを、Webページのテキストボックスから入力するようにしただけ。
ついでに共通処理をメソッドにしたので、結構時間がかかった。

デバッグしようとしたら、Firebugがjsファイルを読み込まない。
ちょっと悩んだが、ブラウザでJavaScriptを無効にしていただけだった。
こんなので悩みたくないので、<noscript>タグを追加して、赤太字でJavaScript無効を表示するようにした。

再利用を考慮するほどのコードでは無いが、一応JavaScriptのfunctionをすべてオブジェクト内に記述している。
共通定数をプロパティとしてまとめたところ、window.onload で実行されるfunctionの動きがおかしくなった。
共通定数を this.定数名 と記述していたが、thisがfunctionを記述したオブジェクトを指していないのだ。

例によって、ネットで調べたところすぐに解決。
obj2.func = obj1.func;
のように、オブジェクト内のメソッドは他のオブジェクトにコピーできる。
addEventListener/attachEventの引数に設定した場合も、同様にコピーされる。
window.addEventListener("load", obj1.func, false);
とした場合、onload時に実行されるのは obj1.func では無く window.func になり、function内のthisはwindowオブジェクトを指す。

そこで、addEventListener/attachEventの引数に直接functionを設定せずに、ワンクッション置くことにした。
var listner = function(){ obj1.func(); };
window.addEventListener("load", listner, false);
こうすればobj1のメソッドだけがコピーされることは無い。
イベントリスナ周りを記述するときは注意しよう。すぐ忘れそうな気がするが。

作ったJavaScriptコードはちゃんと動いたが、実用的では無いので明日続きをやろう。
早くスタイルシートの勉強に入りたいのだが。

<2010-06-29 追記>
var listner = function(){ obj1.func(); };
のような無名関数を使用する場合の注意点と、それを考慮したソースをこのページに書いた。

DOMを使ってHTMLを組んでみた

昨日ざっとDOMの基礎を眺めたところ、
document.getElementById で親ノード取ってきて、
document.createElement とプロパティ追加で子ノード作って、
親ノード.appendChild で子ノード追加
でHTMLを組めることがわかった。
簡単そうなので、ボタンのonclickで<body>の一部を組み換えるスクリプトを作ってみた。

とりあえず作ってみたが、動かない。
何が悪いのかわからない。
Firebugでデバッグしようとしたが、jsファイルをスクリプトとして認識しない。
JavaScriptの構文チェック方法を調べたところ、「jsファイルをダブルクリックすればいい」とあった。
エラーメッセージが一つしか出ないことを除けば、これは便利な構文チェック方法だ。
構文修正後、Firebugがjsファイルを認識したので、ブレークポイントを設定しつつデバッグ。
直感的にわかり易いインタフェースや、行き届いた説明文のおかげで、操作方法を一切調べずにデバッグできた。
実行とデバッグを繰り返して、何とか思った通りの動作をするようになった。

今日ちょっとハマったのは、addEventListener/attachEventメソッドに渡すfunctionに引数を設定する方法。
最初 addEventListener(type, func(arg), false) としたら、func(arg)のreturn値がaddEventListenerメソッドに渡されてしまった。
ネットで調べたら、var listener = function(){func(arg);} と新たなfunctionオブジェクトを作成して、
addEventListener(type, listener , false) とすれば良いとあった。
調べるとすぐに解決方法が出てくるなんて、便利になったものだなと改めて実感した。

明日は更に機能追加しよう。

<2010-06-29 追記>
var listener = function(){func(arg);};
のような無名関数を使用する場合の注意点と、それを考慮したソースをこのページに書いた。

ちょっとだけDOMの勉強

JavaScriptを使って動的にHTMLを組み換えたいと思い、DOMを使えばできそうなんで、DOMの勉強を始める。
とりあえずネットで検索したページをざっと見た。
簡単なものを作ってみたいのだが、時間が無い。
遊ぶ時間が多すぎてなかなか時間を割けない。
その遊びがとても楽しいかと言うと、そんなわけでもないのに。
時間の使い方がとても下手なのだ。
明日は本腰を入れてDOMをやる。遊びは時間が余ったらやる。始めからそうすりゃいいんだよな。

JavaScriptを使って簡単な物を作ってみた

JavaScript入門サイトをざっと見た。
構文などの基礎を自分用チートシートにまとめ、操作できるブラウザオブジェクトを一通りチェックした。
functionを使って定義するクラスの、メソッドの使い方がよくわからない。
コンストラクタとして実行されるとか、メンバ変数やメソッドを記述しないと暗黙的にメソッドが実行されるとかよく理解できない。
実際に使って覚えていくしかないかな。

JavaScriptを使って動的に<body>の中身を組み替えられそうだけど、どうやるんだろう。DOMを使うのかな。

試しに何か作ってみようと、
「テキストボックスに数字を入れて、ボタンを押すと計算結果を別のテキストボックスに出力するページ」
を作ってみた。
これは特に問題無く簡単にできた。

更に、テキストボックスの内容を一括クリアするボタンを追加した。
テキストボックスを一つずつ指定せずに、ループ処理でeval()使って実装してみたのでちょっと戸惑った。
Firebugを使わずに、alert()で地道にデバッグした。

今日はここまで。
テキストボックスの追加や、計算式の変更をブラウザ上で行えるように機能追加したい。
プロフィール

himax64

Author: 南西
30代後半の無職です。
就活もせずダラダラ生きてます。
作ったもの

最新記事
人気記事
検索フォーム
カテゴリ
月別アーカイブ
最新コメント
最新トラックバック
RSSリンクの表示
QRコード
QRコード
カウンター