|
||||||||||||||||
『起動』のページでも書いていますがJavaScriptは読み込みと同時に起動するか何らかのイベントから起動する事がほとんどです。 DOM操作として最も多いのはビジターによる何らかのアクションでイベント(動作)を発生させてJavaScriptを動かす方法だと思います。 また最近はXMLやXHTMLの利用や考え方からでしょうが、 本文であるHTML文書のペーパーの部位とJavaScriptやCSSなどの修飾部位を分離して、 ページを『ペーパー部分と付属効果部分』に分ける事も主流になりつつあります。
個人ページでは必要がない人は『分離』をそれほど意識する必要はありませんが、
整理と言う点では良い事ですので、この辺も踏まえてイベントハンドラの使い方を書いておきます。
使い方は色々あるのであとで書きます。 Microsoft(IE)はDOMを先駆けて実装したのと、拡張範囲が物凄く広いのでかなり多いです。 全部網羅できませんのでMicrosoftやMozillaのリファレンスなどもチェックしてください。 一部だけ載せます。 [ マウス系 ] ■ onclick
■ ondblclick
■ onmouseover
■ onmouseout
■ onmousedown
■ onmouseup
■ onmousemove
■ oncontextmenu
[ キーボード系 ] ■ onkeydown
■ onkeyup
■ onkeypress
[ ウィンドウ系 ] ■ onload
■ onunload
■ onerror
[ フォーム系 ] ■ onchange
■ onsubmit
■ onreset
この他にもいっぱいあります。
○○系と言うのは、一般的には・・・と言う事です。
■ イベントハンドラの使い方 ■ インラインのイベントハンドラ イベントハンドラは基本的にHTMLエレメントにインラインで記述する事で、 そのエレメントでイベントが発生した時にJavaScriptを起動していました。 もちろん今使ってもまだそれほど問題は無い方法です。
<img onclick="" onmouseover="" onmouseout="" onload="">
この様に対応するタグに直接書き込んで使います。
eventhandler = "実効命令"
<img src="gazo.jpg" width=200 height=150 onclick="
こんな書き方さえも許されます。function test(a){ if(document.getElementById('aaa').value == ''){ a.src = 'aaa.jpg'; }else{ a.src = 'bbb.jpg' } }; test(this);" onmouseover="" onmouseout="" onload=""> 一般的には eventhandler = "this.src = 'aaa.gif'" の様に単純な実行文を書いたり eventhandler = "関数()" の様に、関数に渡して処理をさせるのが普通です。 ただしHTMLの流れとしては、インラインで書き込む事は非推奨になりつつあります。 ま、便利ですしブラウザが理解してくれるうちは、個人レベルであれば使っても良いかな?と思います。 ■ JavaScriptによるイベントハンドラのセット ページの一番最初の説明でも書いてありますが、 最近はBODY内のドキュメントエリアを単純なリストとブロックの文章とし、 位置関係や修飾関係のCSS、JavaScriptなどは別にすると言った文書の構造を分けるのが標準となってきてます。 こうなってくるとドキュメント内にインラインでCSSやJavaScriptのイベントハンドラなどを書き辛くなります。 また別の考え方として、例えばJavaScriptを外部ソースファイル化した場合、 この外部ファイルがprototype.jsの様に巨大な場合は読み込みが完全に完了する前にイベントハンドラが存在してしまい実行されると、 タイミングによってはイベントハンドラで呼ばれる関数等が存在しないケースもありえます。(基本は待ってくれるので大丈夫です) また、JavaScriptが読み込まれてイベントハンドラ部分が読み込まれていても、まだドキュメント自体が完全に読み込まれる前であればJavaScriptのDOM操作でエラーが出る可能性も十分あります。 このケースはマウスオーバーイベントなどで起きてるケースを見かけます。 こういった不確実な要素を排除する為には、全ての読み込みが完了するまではJavaScriptプログラムを起動させないと言うのが一つの手です。 と言う事で、ページのロードをきっかけにJavaScriptを埋め込んでいく方法です。 またプログラム中で新たに作ったオブジェクトにイベントハンドラをつける時にも使います。 初期セッティングを行うような感じです。
<script type="text/javascript">
HEAD内のJavaScript記述でwindow.onload と言うイベントを無名関数につなげてしまいココでロード後のセットを行います。window.onload = function(){ } </script> ま、これ自体が従来のJavaScriptでのwindowへのイベントのセットでもあります。 この無名関数が実行される時は既にドキュメントが全て読み込まれていますので、getElementById()などで対象オブジェクトを指定できます。 window.onloadは BODYタグに onload ="" を書くのと同じだと思ってください。 ただし document.onload では無く window.onload です。 また、この方法を使う場合は、BODYタグに onload イベントハンドラがあると同じ指示がぶつかってしまいます。 BODYタグにあるonload = "処理や関数" を、
window.onload = function(){
の様に移動させてBODYタグの onloadイベントは削除してください。処理や関数 } その他もし window.onload = func; のような記述が他のスクリプトである場合は
window.onload = function(){
の様に () をつけてこのwindow.onload 用関数内に書き込んでしまいます。
こうやって、既存の window.onloadイベントで始動させる処理命令や関数を全てこの1箇所にまとめます。
複数のプログラムを共存させる場合にはとても重要な事です。func() } さて、ここから自分の目的のオブジェクトにイベントと処理をつける方法。
window.onload = function(){
対象オブジェクト.eventhandler = 無名関数(){ }/* 無名関数へ */ document.getElementById('aaa').onclick = function(){ idがaaaのエレメントのクリック後の実効命令 } /* ハンドラー(関数オブジェクト名) */ document.getElementById('bbb').onclick = cFunc; } /* ほぼ普通の関数 オブジェクト */ function cFunc(){ idがbbbのエレメントのクリック後の実効命令 } 対象オブジェクト.eventhandler = 関数名; このような書き方で、イベントハンドラをセットできます。 インラインで書くのと難しさは何も変りません。 対象オブジェクト.eventhandler = 関数名; と書く使い方をする時は関数名に () を付けないようにしてください。 この意味は簡単なルールで、() は実行と思って良いです。 変数 = 関数名(); とすると関数が実行された処理後の戻り値が変数に入ります。 変数 = 関数名; とすると、変数 = 関数名 = 関数の処理内容という形で、変数に関数名の関数オブジェクトを割当てた事になります。 この方法であれば、例えばテーブル(table)に20個のセルがあって、 その全てにイベントつける面倒な時にも楽に使えます。
window.onload の無名関数やその他関数の中などで
こういう書き方をしてしまえば、インラインで全ての要素にonclick="" イベントを付けるよりずっと楽です。
沢山のサムネイル画像などがある場合も、DIVなどで囲ってIMGタグ配列を抽出してこんな感じで付けちゃいます。
/* id table1 の中のTD(セル)タグ名から抽出して配列化 */ var TDs = document.getElementById('table1').getElementsByTagName('td'); /* TDの入ってる配列の全ての要素(TD)にonclickを付ける*/ for(var i=0; i<TDs.length; i++){ TDs[i].onclick = function(){ 各セルのクリック処理 } } これらのイベントハンドラは、そのイベントが発生すると無名関数内、または 関数名で指定された関数内の処理を実行します。 こういう書き方をすれば、スクリプトパート部分で全てをコントロールし、 HTMLドキュメントエリアはドキュメントとタグのみのすっきりした分離構造になります。 ただしこの方法は一つ大きな問題が出る場合があります。 『オンロードをきっかけに全てが始まる』と言う事は、 ロードが完了するまではスクリプトの起動命令が存在しないと言う事にもなります。 『 window.onloadとはdocumentの要素が全て読み込まれたら 』を意味します。 例えばアフィリエイト(画像つき)などで画像のURLがアフィリエイト会社のサーバーの場合、 時間帯によっては非常に重くて画像がなかなかロードされないケースなどがたまにあります。 こういった場合その画像がロードするまで、またはロード失敗の結果が出るまでは、window.onload のイベントは発生しないと言う事です。 またiframe等を内包していると、iframe内のドキュメントのロードが完了しないと window.onload のイベントが発生しません。 つまり『 何も始まらない・・・・ 』と言う事になります。 これらを避ける為には別サーバーのデータを読む必要があるようなコンテンツは、 基本コンテンツが読み込まれたあとwindow.onload でJavaScript側から入れ込むとか、画像のURLを提供するとか、一旦自サーバー内のベース画像を読ませてから 本画像を入れ込むなどの工夫をした方が良い場合もありえます。 自サーバーが重いのであれば、自ドキュメントも重いですし『仕方ない』と言えますが、 自サーバーが軽いのに、他サーバーのデータの遅れで不便になってしまっては無駄ですからね。 もう一つ注意:window.onload = function(){ }の中で 変数にvar宣言をすると当然ローカル変数になります。 なんとなく初期起動だからと言う事でグローバルな印象を持ちがちなので注意です。 JavaScirptによるイベントハンドラの設定については、 こういった従来の方法で十分間に合う場合はこれで問題ありません。 が、Mozillaのリファレンスなどでは『古い方法(従来の方法)』と言う事になっています(笑) IEはattachEvent()、Netscape系はaddEventListener()を使って、 オブジェクトにイベントを付加する方法などが、今後の標準になっていくと思います。 『また処理分岐?』っていう迷惑な話もありますが、それは自作関数やミニライブラリなどで吸収させちゃいましょう。 自分のページのスクリプトをしっかり把握して自分で書いてる人は従来の書き方でも特に問題ないです。 でも 将来的にはメソッドを使う方法に切り替えていく事も薦めます。 私としては『絶対』と言う考え方が無いので、必要に応じてリニューアル時等に徐々に変更していきましょうと言うスタンスです。 従来の方法もスクリプトの把握度によって絶対駄目とは言いがたいですからね。 大きなプログラムを組む場合やライブラリ開発、汎用スクリプト、これらの配布者は、 メソッドの方法またはメソッドと同等のプログラムを付加する必要があります。 これはなるべくと言うよりそうすべきという少し強めです。 理由は簡単でIEはattachEvent()、Netscape系はaddEventListener()が用意されており、 これらのメソッドは同じオブジェクトに同一イベントを追加していける点が従来の方法より優れているからです。 また、複数の同一イベントを同一オブジェクトに追加した後で、その中の特定のイベントだけメソッドdetachEvent()、removeEventListener()で削除する事ができる事も、 プログラムを組む上で自由度、応用度が広がります。 従来のobject.eventhandler = '' やタグへのイベントハンドラをつける方法だと、 もし同じオブジェクトに同一イベントが別の目的で付加された場合、後から設定された方だけが有効になり、 先に書いてある方は上書きされて消されてしまうので、ライブラリなどで両方に window.onload 記述があると、何処をいじって良いのかわからないレベルの人にとっては困り物でした。 先に書いてある同じオブジェクトの同じイベントがメソッドによる物であれば上書きされません。 現在は非常に良くできたJavaScriptライブラリなどが色々あって、あれこれ色んな物をつける人が増えています。 こういった場合、ライブラリ製作者はwindow.onload の様に広範囲で頻繁に使われがちなオブジェクトのイベントに関しては、 『上書きをせずに追加をする』という作り方をする必要があり、同時にグローバル変数などもなるべく少なくして1個のオブジェクトにしてしまうか、無名関数化する努力が必要です。 が、イベントが複数追加できてしまうと困るケースもあります。 例えば異なる画像のスライドショーを2個付けられても困ります。 こういう場合はユニークイベントである必要性をきちんと注意書きにして、イベントは追加ではなく上書きして独占してしまった方が良い場合もありえます。 が、他のスクリプトが何になるか?はそのページの管理者しか知りえないので独占できるかどうかも微妙ですが・・・。 window.onload や document.onclick、mouseover、mouseout、など広範囲に使われそうなイベントは、 同一イベントで他の処理と共存できる場合は『追加型』にする事を考えましょう。 未だに超有名な秀作のライブラリでも、イベントを独占してグローバル変数を多数使用してるケースがありますが、 作られた時期の下位互換などのせいもありますし、今後修正されていくだろうと思います(期待しています)。。 と言う事でメソッドです。(私も現在修正中です) ■ メソッドによるイベントハンドラのセット ◆attachEvent() (IE、Opera) (Object).attachEvent('eventhandler',funchandle) の様に、オブジェクトにイベントと処理関数をセットします。 eventhander の部分は onclick、onmouseover などの様にイベントハンドラ名を文字列として指定します。 funchandle は処理を渡す関数名です。 無名関数を直に書き込んで (Object).attachEvent('eventhandler',function(){ 処理 }) と言う書き方をする事も可能です。 ◆addEventListener() (Netscape、FireFox、Safari、Opera) (Object).addEventListener('eventtype', funchandle, false) の様に、オブジェクトにイベントと処理関数、と通常はfalseを渡します。 eventtype は イベントハンドラ名ではなく、event.type プロパティで参照できる値です。 基本はイベントハンドラの『 on 』を外したものと同じで、click, mouseover などになります。 funchandleは処理を委ねる関数名です。 無名関数を直に書き込んで (Object).addEventListener('eventtype', function(){ 処理 }, false) と言う書き方をする事も可能です。 falseに関しては、同じ位置で発生するイベントの優先度変更と思って良いと思います。 あるエレメントを別のエレメントで囲んであって両方に対して同じきっかけで発生するイベントがある場合、 通常は最も内側(クリックならクリックされたエレメント)の発生要素から順にイベントが発生します。 外側のイベントを優先する場合外側の方を true にする事で、そのtrueのイベントを優先させる事ができます。 このメソッドを使う時に両方で利用する為の分岐は
if(el.addEventListener){ el.addEventListener('click', func, false); }
Mozillaでアナウンスされている物です。(ちょっと変更)else{ if(el.attachEvent) el.attachEvent('onclick', func); } この中で el はイベントを付けるオブジェクトです。 で、このメソッドを使う時に毎回こんな記述をするのは非常に面倒です。 そこで、document.getElementById()を簡略化するのと同じように、 ブラウザの違いを吸収させてしまう関数を作って常用すれば逆に使いやすくなります。 例:ミニライブラリ
function addEv(obj, type, func, cap){
こんな感じでしょうか。if(obj.addEventListener){ if(!cap){ cap = false; obj.addEventListener(type, func, cap); } }else{ if(obj.attachEvent){ obj.attachEvent('on'+ type, func); } else{ var p_ev = obj['on'+ type]; obj['on'+ type] = function(e){ if(p_ev){ p_ev(e) }; func(e || window.event); }; } } } 本来は attachEvent()とaddEventListener()の分岐だけで良いのでしょうが、一つ不安な事があってもう一つ余計な物もつけています。 余計な物 = 従来の記述方法を使って、これらのメソッドと同じ役目をする物です。 私はこの数年は最新の情報を追ってなかったので、どのブラウザレベルでどこまで解釈できるのか?どのメソッドがどの時点で提供されてきたのか?がはっきりわかりません。 なので、attachEvent()やaddEventLister()が使えないが object.eventhandler = func が使えるケースがあると言う想定で、一応下位互換としてつけてあります。 このミニ関数 addEv() を使ってイベントとその関数を設定する時は addEv(オブジェクト, イベントタイプ, 処理関数 , キャプチャー ) の様に関数として呼んで使います。 ■addEv()の 引数について オブジェクトはイベントを付けるオブジェクトなのでwindowやdocument、getElementById(id)で取ったオブジェクトなどをそのまま渡します。 イベントタイプはevent.typeでとれる値。『on』が付いてないイベント名(click, load, mouseover)です。 処理関数は別にある関数の名前(ハンドラー)でも、無名関数function(){}をそのまま渡しても大丈夫です。 capは Netscape系のaddEventListener()で使う、3つ目の引数(arguments[2])です。 trueにする必要がある場合だけ、true と書き込みます。(省略でfalse) これでブラウザ間の違いの処理を気にせず addEv()でイベントを追加可能です。 が、、、 まだ問題があります。しつこい(^^ゞ 同じオブジェクトに同じイベントが追加される場合は、同じイベントで複数の処理が実行される事になります。 addEventListener()は実行されてイベントが追加された順に正しく順序良く処理を行います。 が、attachEvent()はイベントが追加された順ではなく、別の何らかのルールで順番が決められるようです。 従って、例えばwindowに複数のonloadイベント処理を付加する場合などに微妙な順序が必要なケースならば、 IEではattachEvent()を使わない、else else の方で処理させた方が良い事になります。 (IEで順番を決める方法があるかもしれません。^^;) でも・・・この自作関数の方で行うと、別問題がまた生じます(^^ゞ メソッドを使う場合はそのあとでどういう形でイベントを追加されても、イベントは上書きされずに残ります。 自作関数は従来の方法なので、同じ方法(Object.eventhandler= '')やタグに書き込む方法を使われると上書きされて消されちゃいます(^^; どんなスクリプトと共存させられるかわからないような配布スクリプトの場合は、実行順序よりも上書きされてしまう危険性のほうに重点を置くべきだと思います。。 例:一応ミニライブラリ (IEに順序を守らせる事が最優先?)
function addEv(obj, type, func, cap){
前述のミリライブラリからattachEvent()を削除しました。if(obj.addEventListener){ if(!cap){ cap = false; obj.addEventListener(type, func, cap); } }else{ var p_ev = obj['on'+ type]; obj['on'+ type] = function(e){ if(p_ev){ p_ev(e) }; func(e || window.event); }; } } elseの部分は、既に定義されている同一イベントを取り出して、新たに追加するイベントの前に実行するように組んでいます。 同一のイベントがかなり多くなってくると、ただでさえIEは実行速度が一番遅いので、この入れ子構造の関数は微妙に実行時差ができやすくなります。 ま、これを使うよりは、Mozillaでアナウンスされてる普通に分岐させる方法が良さそうです^^;。 更にしつこく・・・(-_-; インラインのイベントハンドラの場合は、onclick ="関数(); return false;"ではなく、 onclick ="return 関数()" とする事で、関数側から返される return falseを使ってタグの元の動きをキャンセルできます。 Aタグでわざわざ関数から返さずにonclick ="関数(); return false;"と書くのは、hrefを無効にする事が既に確定してる場合だけです。 それ以外の場合はフォームのonsubmit="return 関数()" の使い方と同じように関数から falseかtrueを返すように使う物です。 Object.eventhandler = function(){ }のケースは関数内で return falseをすれば同じくタグの元の動きをキャンセルできます。 Netscape系はメソッド(addEventListener)を使ってつけたイベントの場合、渡された関数内でreturn false;をしてもタグの元の動きをキャンセルできません。 従ってアンカーやフォームにaddEventListener()を使ってイベントをつけて、戻り値をfalseにする必要がある場合は、処理関数内で return falseの場所に if(e.preventDefault){ e.preventDefault();} return false; (eはイベントオブジェクト)の様に書いて、キャンセルさせて、IEにはreturn falseを返すようにしないと実行されてしまうので注意です。 preventDefault() ま、他にも回避策はあるかもしれませんが。。 ■ イベントハンドラ その他あれこれ onclickを アンカー(Aタグ)につける人が何故か?未だに多いのですが、 アンカーにインラインでイベントハンドラをつける場合で、href属性へのクリックジャンプを実行させたくない場合は <a href="" onclick="関数(); return false;> の様に、関数や処理の後にreturn false;をつけます。 あるいは、<a href="" onclick="return 関数();>の様に書いて、関数の方でreturn falseをしてやります。 retrun false を明確に受け渡す事でタグやイベント本来の動きをキャンセルできます。 これでアンカーのhref属性で指定した先にジャンプしなくなります。 が、一部のIE7ではreturn false;が効かずに hrefが有効になってしまうと言う報告があります。 これはIE6からIE7にバージョンアップしたケース、サードパーティ製のIEを使う場合などに見られるようです。 IEの設定をいじると治りますが・・・。そのままの人も多いですからね。 また、既述ですが アンカーオブジェクト.onclick = function(){ }の様にJavaScriptでイベントハンドラをつけた場合は、 この関数内で return false を返せばタグ本来の動きをキャンセルできます。 IEの場合はメソッド(attachEvent)を使っても同じく渡された関数内の最後でreturn false;をすれば普通にアンカーやフォームなど本来の動きキャンセルできます。 が、Netscape系はメソッド(addEventListener)を使ってつけたイベントの場合、渡された関数内でreturn false;をしてもタグの元の動きをキャンセルできません。 従ってアンカーやフォームにaddEventListener()を使ってイベントをつけて、戻り値をfalseにする必要がある場合は、処理関数内で return falseの場所に if(e.preventDefault){ e.preventDefault();} return false; と書かないと実行されてしまうので注意です。 preventDefault() ◆習慣的な href="#" について
何故わざわざアンカーを使ってしかも href を無理につけるのか?
用が無いのに href="#" 等をつける習慣がありますね。私もhrefはまだ付けてます(笑) アンカーを使う場合はhref属性を最初からつけなければ良いです。 ま、それならSPANを使えば良いのですけどね。 hrefをつける人は元々 CSSの hover と cursor、アンダーラインなどの為につけていると思います。 が、今は(IEは7から)どのタグにも hover が有効になっています。 ですから Aタグも href をつけなくても hoverは有効です。 ところがIEは7でもバグ?なのか href が無いと a:hover { } ではhoverが効かないんですね(^^ゞ なのでclassを付けるかIDを付けて、そのセレクタで指定する必要があります。 /*スタイル設定*/ .Atag:hover { color:red; } /*タグ*/ <a class="Atag" onclick="" > の様にしてやらないと hoverが有効になってくれません。 classを付ける手間を考えたら href を付けた方が楽で慣れてるって人もいると思いますが、 前述の一部IEで return falseが効かないケース&この設定を直してない人の数を考えると、 hrefを付けるよりは class指定で hoverさせてやった方が良いと言う事になります。 hrefが無いとカーソルも下線もなくなりますが、それはCSSの方で指定しておけば一括ですし。 他のコーディングに関しては下位互換は切り捨てているのに、 JavaScriptの onclick の為にわざとアンカーを使う事は未だに多いのがちょっと不思議。 ま、href="javascript:コード"の様に使うのであれば便利と言えば便利ですが、、、。 getElementById()などを標準として使ってるページであれば、CSSが効かない事は無いので return false;が正しく動かないケースが存在している以上は、 SPANタグかAタグにしてCSS設定を併用して行った方が良いかと思います。 もう一つIEでも、href の無い Aタグの hoverを有効にする方法があります。 Aタグの外側を囲んでいるタグか、もっと外側のBODYタグなどに対して body:hover {} のように空の hoverを付けてやります。 その上で A:hover { color:red } の様に普通にAタグにhoverを付けてやると、 空のhoverの内側に位置するAタグは href も Class も無くても hoverが普通に有効になります。 こうすれば hrefが無くてもクラス指定も使わずに A:hover { } が普通に使えます。 hrefが無いと下線やカーソルの変化がなくなりますが、A {} と A:hover {} あたりを使えば良いかと。 active や visited などをJavaScriptの起動のために何処まで必要なのか?を考えると扶養の部類だと思います氏。 ま、基本的にはまだユーザーの多いIE6では hover が他のタグに効かないので、 その辺を考えてAタグを使って href まで付けていると言う人もいるでしょう。 この場合はhref="#"よりも、 href="javascript:void(0)"や href="javascript:;" の様に href値に何も実行しないJavaScriptを入れるのが良いでしょう。 また、SPANタグとCSS設定などに変更していくのも良いかと。 onclick後のJavaScriptの処理の中でthisやeventオブジェクトを使わないケースであれば、 onclickは使わずにhref="javascript:関数()"にできるケースなら問題は解決です。 ちょっと特別な例として href を有効利用してる場合 <a href="a.html" onclick="関数(処理と別の場所にジャンプ); return false; "> 例えばこの例は JavaScriptが無効、OFFの人の場合はa.htmlに飛び、 JavaScriptが有効な人は関数で処理して別のアクションをするとします。 こういう使い方をしてる人は href を使わざるを得ないでしょう。 この状態で return false;が効かない一部のIEの場合は面倒です。 でも、こういう場合は ドキュメントのオンロードが済んだら、ID指定などで、目的のアンカーのhref属性の値を 'javascript:void(0)' または 'javascript:;' の様に書き換えるか、アトリビュートをデリートしてしまえば良いです。 JavaScriptが有効な人にはhrefが不要なのですから、オンロード時点で削除と言う事ですね。 或いはインラインで <a href="a.html" onmousedown="this.href='javascript:void(0)' " ;onclick="関数(); return false; "> の様に、onclickより先に動作するイベント、onmouseoverやonmousedownを使ってクリックされる直前にhref値を書き換えてしまえば済みます。 onclickイベントが起きてからhrefを書き換えるのは手遅れです。 もうかなり昔から href="javascript:void(0)" でhrefを無効にする手が使われていましたが、 いつの間にか href="#"と言うのを使う人が増えてしまったようです。 どこかの真似なのかな? Aを使っちゃいけないと言う話ではなく、return false; が効かない変な動きのブラウザを考えると、 今の時点で href="#" は薦められません。 ◆Imageオブジェクトの complete と onload
Imageオブジェクトには画像の読み込みが完了しているか?を確認する為に
complete プロパティを調べる方法があります。
Image.complete が true か false か。 IEではこれは正しく動作するのですが、Netscape系ではキャッシュの関係なのか? HTMLオブジェクトの場合も、new Image()で作ったイメージオブジェクトの場合も 必ずしも正確に(こちらのオンロードの意図にそって)動作してくれない事があります。 そこで明確にロード済を知る手段として有効なのが onload です。 例: var img1 = new Image(); img1.loaded = false; img1.onload = loadcheck; img1.src = 'aaa.jpg'; function loadcheck(){ this.loaded = true;} 画像が1個の場合は無名関数で良いです。 completeプロパティはリードオンリーなので使いまわしできない為、勝手にloaded というプロパティを作りfalseを入れておきます。 読み込みが完了するとonloadイベントで loaded が true になります。こういうロードフラグを作る事でNetscape系でもしっかりと確認ができます。 ちょっと困るのがHTML画像オブジェクトのロード状況。 window.onload のタイミングで 画像タグにonloadを埋め込むのは全く無意味です。 従ってインラインでonloadを書き込むか、画像タグ(オブジェクトを)をwindow.onload後に入れ込む、と言った形になります。 が、ロード確認が重要なケースなら遠回りでもこういう方法をとるしかありません。 ◆frame、iframe に あとづけの onload
frameタグ、iframeタグに インラインでonloadを書き込む場合は問題ないのですが、
後からJavaScriptで
これらの他にもブラウザや使い方による微妙な違いが発生するケースもあります。
プログラムを書いてると『あれ?』って事が時々起こるので、
それから『どちらが正しい動きか?間違ってるか?』なんて責任を他に押し付ける前に、
最低でもIEとFireFoxの2大ブラウザで確認して原因をつきとめて両方で動くようにしてください。frameオブジェクト.onload = function(){ } の様にonloadをあと付けしてもIEでは この onload は有効になってくれません。 この場合はIEでは、 frameオブジェクト.attachEvent('イベント名',関数名); の様に記述する必要があるようです。 attachEventを使わなくてもIEではフレームのドキュメントに対して、 readyState や onreadystatechangeが使えますので、この辺を使ってロード確認をする手もあります。 Netscape系では、frameオブジェクト.onload = function(){ } でも インラインで書くのと同じように普通に有効になってくれます。 frame や iframe に読み込まれたドキュメントのDOM操作をする場合は、 読み込ませてすぐに操作しようとするとエラーが出る可能性は非常に高いです。 必ず onloadなどを利用して『 読み込みが完了したか? 』をチェックする習慣をつけて、 読み込みが完了したらフレームドキュメントのDOM操作をするように用心深くワンクッション置くようにしてください。 iframe.src = 'a.html';と指定しただけでは読込みスタートの合図でしかありません。 製作者は回線の影響を受けないローカルのテストであったり、 何度も表示してキャッシュがあったりしてエラーに気づきにくくなるケースがあります。 初めてページを訪れる人にはエラーでお出迎えと言うパターンも良く見かけますので気をつけてください。
イベントハンドラを設定して関数に渡す場合に、 この関数が複数箇所のイベント共通関数だった場合や windowの様に全体や広範囲をターゲットとしてる場合に、 『 どのオブジェクトのイベントなのか? 』『 どこでイベントが起きたのか?』を知る必要がある場合があります。 イベントは発生するとイベントオブジェクトと言うものができますので、 このイベントオブジェクトのプロパティを参照する事で色々情報をとる事ができます。 よく使われる物をピックアップして紹介しておきます。 ■ インラインのthis イベントが埋め込まれているオブジェクトを指します。
<img src="aaa.gif" onclick="関数(this)">
this は元々状況に応じて指す物が変ります。この様にインラインでイベントハンドラを書いた場合に、 このイベントハンドラの実行内容に書くthisは、このイベントが付けられているオブジェクトを指します。 (この例の場合は このimgタグエレメント )。 関数側で引数としてこのオブジェクトを受ける事で、対象オブジェクトの特定をわざわざする必要がなくなります。 一部thisについて修正しました thisがイベントが発生したオブジェクトと書いてしまっていました。 thisはimg等の内包するタグが存在しないエレメントにつけられた場合は確かにそうですが、 例えばDIVにonclickがつけられて、そのDIVの中のAタグがクリックされた場合でも、thisはイベントがつけられているDIVを指します。 イベントが発生したエレメントは、Aタグのエレメントです。これは (event).target || event.srcElement で取る必要があります。 インラインで書いた場合は、処理を渡された関数側でthisを使っても、そのthisはオブジェクトを指しませんので、 イベントハンドラから関数を呼ぶ時に引数として渡す必要があります。 フォーム部品の場合はフォームelementには、element.form で属しているフォームを指すプロパティがあるので 例えばボタンにイベントハンドラをつけた場合は、this が ボタンのエレメントを指し、this.form は属しているフォームを指します。 先ほど書いたように関数に渡してから関数内で this を使うと意味が違ってしまうので、 フォームをオブジェクトとして渡したい場合は
<input type="button" onclick="関数(this.form)">
の様に引数として渡すか
<input type="button" onclick="関数(this)">
の様に引数をオブジェクトとして扱えば良いです。関数側で function 関数(obj){ obj.form //これがフォームオブジェクトを指す } 普通のHTMLエレメントの場合も
<img src="aaa.gif" onclick="関数(this.parentNode)">
thisをオブジェクトの代名詞として使って、自分のすぐ外側のブロックのオブジェクトを指したり、
this.src とか this.href などの様にそのタグオブジェクトのプロパティ(アトリビュート)を指定したりします。■ 無名関数やハンドラで埋め込んだ関数オブジェクトでの this イベントの発生したオブジェクトではなく、イベントを埋め込んだオブジェクトを指します。 内包するタグが存在しない場合はこれらは両方同じと言えます。
document.getElementById('aaa').onclick = function(){ alert(this.tagName); }
これらの書き方の場合は、イベントハンドラに関数オブジェクトが埋め込まれてると思ってください。
なので無名関数やハンドラで設定されてる関数内では、thisがそのままonclickイベントが埋め込まれてるオブジェクトを指します。
この例のcFuncにハンドラで指定するのも無名関数と同じく、thisはcFuncが埋め込まれたオブジェクトを指します。document.getElementById('aaa').onclick = cFunc; function cFunc(){ alert(this.tagName) } もし、このIDがaaaがDIVやSPANなど他のタグを内包できるタグだった場合、 イベントの発生エレメントはは実際にクリックされたタグになりますので、thisがそのままイベント発生オブジェクトを指すケースとは違ってしまいます。 発生エレメントに関してはイベントオブジェクトを取って target(Netscape系) か srcElement(IE)で取る必要があります。 あまり外側のタグにつけて一括クリック監視などを使わないので、this = イベント発生エレメント と間違えて書いていました。m(__)m ■ attachEvent() の this attachEvent('eventhandler',関数名)で設定された関数内の this は、 イベントが埋め込まれたオブジェクトではなく、グローバルなthis つまりwindowを指します。 ■ addEventListener() の this addEventListener()で設定された関数内のthis は、イベントが埋め込まれたオブジェクトを指します。
イベントが発生するとイベントオブジェクトが作られます。 このイベントオブジェクトは、発生位置、発生オブジェクト、マウスボタンの特定や、押されたキーの特定など 色々と使える情報を持っています。 IEでもNetscape系でも同じ event という文字で書きますが、 オブジェクトの定義が微妙に違って、 eventオブジェクトの指定?表記?方法なども微妙に違います。 また eventオブジェクトの持つプロパティも違いますので例を出しながら紹介します。 ■インライン でのイベントの取り出し 全共通
タグ
イベントハンドラの実行命令箇所にevent と言う名前でeventオブジェクトが存在しるので、
インラインの場合はイベントオブジェクトを取り出して関数などに渡す時は、ここで『例』の様に明確にevent を引数として関数に渡す必要があります。
受け取る関数側では、そのイベントオブジェクトを受け取る引数を用意して(eventと言う引数名にはしない)、それを受け取る事でイベントオブジェクトを利用できるようになります。<img src="aaa.gif" onclick="関数(event)"> /*JavaScript記述内*/ function 関数(e){ } ■インライン でのイベントの取り出し MS InernetExplorer
タグ
IEの場合はイベントハンドラの実行箇所から渡されなくても、window.event としてオブジェクトを参照できます。
後の方で書きますが直接event として参照するのは控えてください。<img src="aaa.gif" onclick="関数()"> /*JavaScript記述内*/ function 関数(){ evt = window.event } IEでもNetscape系でも通用する書き方としては、1つ目の例の引数として渡す方ですので、それを定番としてしまう方が良いです。 ■関数オブジェクトを埋め込む場合 Netscape、FireFox、Safari、Opera
document.getElementById('aaa').onclick = function(e){ }
このような書き方の場合は、Netscape系ではイベントハンドラに直結してる関数の引数[0] / arguments[0]に勝手にイベントオブジェクトが入ります。
従って取り出す時は受け取る為の引数を用意しておいて、その引数にイベントオブジェクトを入れます。document.getElementById('aaa').onclick = cFunc; function cFunc(e){ } 引数0に勝手に入るとなると、引数の受け取りの順番などで困りそうですが、この手の方法の場合に引数を受け取る事はまずありません。 他の何らかの処理のデータを受け取るのであれば、変数として受け取っておけば良いと思います。 ■関数オブジェクトを埋め込む場合 MS InternetExplorer
document.getElementById('aaa').onclick = function(){
evt = window.event
}
IEの場合は、ごく普通に window.event を参照するだけです。document.getElementById('aaa').onclick = cFunc; function cFunc(){ evt = window.event } ◇Netscape系、IE 両方に通用させるには
document.getElementById('aaa').onclick = function(e){
この様にvar evt = window.event || e; と書く事で、
IEではwindow.eventが存在していてeがundefinedになるので window.eventの値(イベントオブジェクト)が変数evtに入り、
Netscape系では window.eventがUndefinedでeが存在するのでeの値(イベントオブジェクト)が変数evtに入ります。
var evt = window.event || e; } document.getElementById('aaa').onclick = cFunc; function cFunc(e){ var evt = window.event || e; } これでどちらも変数evtがイベントオブジェクトとして利用できます。 この時 var evt = event || e; と書くとNetscape系でeventが理解できずにエラーが出てしまいます。 初心者の『変数』か『オブジェクト』の解説で書いたと思いますが、 未定義の変数など理解できない物がいきなり出てくるとエラーになりますが、 既に存在しているオブジェクトのプロパティとして調べる場合は、未定義でもUndefinedとなりエラーにはなりません。 その為、IEだけで使えるwindow直下のeventオブジェクトも window.eventと書く事で、 Netscape系では理解できないがエラーは出ないと言う形になります。 一方IEでは引数eに関しては、引数として設定があるが何も受け取ってないのでundefinedとして処理できます。 onerrorなどの特殊なイベントの場合は、関数に引数として渡される値がイベントの発生したオブジェクトではなく、 エラーメッセージとファイル名、ライン名などになるケースもあります。 この場合IEではwindow.eventでイベント発生オブジェクトを特定できますが、 Netscape系ではできなくなります。 エラーはオブジェクトよりも、エラーを起こしたJavaScriptのコードに問題があるわけで、それがわかれば良いので特に問題はないとは思いますが。。 ■ attachEvent() の場合 attachEvent('eventhandler',関数名)で設定された関数では、引数[0] ( arguments[0] )にイベントオブジェクトが入ります。 また、通常通りwindow.eventでも当然取れます。 ■ addEventListener() の場合 addEventListener()で設定された関数では、引数[0] ( arguments[0] )にイベントオブジェクトが入ります。 従ってIEとNetscape系で別々に分岐させて attachEvent()とaddEventListener()で同じ関数を指定してる場合は、 例:function cFunc(e){ } とする事で、両方ともイベントオブジェクトを引数eとして取れます。 ちょっと前に書いてある、ブラウザの違いを吸収させる関数ミニライブラリを使う場合 addEv(window,'click',function(e){ 処理中 eがイベント }) と言うように共通で使えます。
プロパティについては、とほほさんの所や、Microsoft、Mozillaのリファレンスを見てください。 よく使われる物をちょっと書きます。 ■ イベントが発生したオブジェクトの特定 IEは (event).srcElement Netscape系は (event).target 全部読んでる人は『あれ?』と思うかもしれません。 イベントが発生したら、そのイベントハンドラが埋め込まれてるオブジェクトを this としてすぐ特定できるはずですから。 でも、これは最下層の個々のオブジェクトにイベントハンドラを付けた(埋め込んだ)場合です。 タグを内包できるエレメント(DIVなど)にイベントをつけた場合は、 必ずしも埋め込んだオブジェクト=発生オブジェクトではありません。 このイベントオブジェクトのプロパティからの発生オブジェクト特定は、広域のタグなどにイベントハンドラを付けて、 その範囲の中でどのエレメント(HTMLオブジェクト)から発生したのかを突き止める時に使います。 例えば、ページ中にボタンが10個あるとします。 onclickイベントを付けたいのですが面倒なので個々のボタンに埋め込まずに、 document.onclick = function(){ } と言う形でdocument(ボディと同じ)にイベントハンドラ を付けてしまいます。 こうなるとボディのどこをクリックしてもイベントが発生します。 thisは documentの事を指しますので、どこでクリックされたのかもわかりません。 こういう時に、この eventオブジェクトのこのプロパティを参照します。
document.onclick = function(e){
これで el にイベントが発生したオブジェクトが入ります。var evt = window.event || e; var el = evt.srcElement || eve.target; } 一つ前の解説通りIEでもNetscapeでも evtにイベントオブジェクトが入っています。 存在するオブジェクトのプロパティを調べるのですからお互い理解できないプロパティ名があってもUndefinedで無視してエラーも出ないです。 先ほどの例の話で、ボタン10個に onclickイベントをインラインで書き込んだりJavaScript側で埋め込んだりするよりも、 これらのボタンのIDを工夫したりクラス名などをこれら専用にして、
document.onclick = function(e){
の様に、『イベントが発生したエレメントのクラス名が btns なら・・・』とすれば、
ボタンが押された場合の実効命令を簡単に記述する事ができてしまいます。var evt = window.event || e; var el = evt.srcElement || eve.target; if(el.className == 'btns'){ 実行文 } } 或いはエレメントのIDが○○系ならAを実行とか、タグ名が○○なら・・など、 HTMLオブジェクトとそのIDやクラス名、場合によっては自作グループ名などが整理されていれば クリックイベント(例の場合)を一つの関数の中で色々分岐させたりもできて、管理しやすくなるケースもあります。 極端に言えば、メニューのような『クリックされる事が仕事』のブロックなどでは、 この方法でクリックされたエレメントとそのIDを利用したり、switch〜caseの様な方法で分岐させてしまう事も プログラムの作り方としては『あり』と言う事です。(JavaScriptがON前提のサイトなら) 例えば、DIV内に20個のサムネイル画像があって、クリックしたら拡大画像を別所に表示するというケースは、 サムネイルのIMGタグ1個1個にonclickを埋め込むよりも外側のDIVに埋め込んでしまって、 先ほどの書き方なら if(el.tagName == 'IMG') とすればサムネイル画像がクリックされた事がわかります。 でも、こういう書き方をする時は 何番目(どの)の画像がクリックされたか、何番目(どの)のボタンがクリックされたか?などがすぐ把握できませんね。 これはID名を工夫したり、自作アトリビュートをつけたり連想配列を作っておくなどして、 クリックされたエレメントから情報を引き出せるようにして利用します。 例えば、サムネイルが <img id="th1" src="aaa1.gif" > <img id="th2" src="aaa2.gif" > <img id="th3" src="aaa3.gif" > <img id="th4" src="aaa4.gif" > でクリックされたら、photo1.jpgの様に photo + 同番号の画像を表示する場合
document.onclick = function(e){
el.idでクリックが発生したサムネイルのIDを取得。var evt = window.event || e; var el = evt.srcElement || eve.target; if(el.tagName == 'IMG'){ 表示用画像オブジェクト.src ='photo' + el.id.replace('th','') + '.jgp'; } } replaceでthと言う文字を消して数字だけにしてしまう。 photo と .jpgと言う文字列を前後に付けて、呼び出す画像名に加工。 これはあまり良い例ではないですが、こうやってIDなどを利用しやすいように番号付にしたりして、 JavaScriptで使いまわすのがベタですが単純な方法です。 ■ イベントが発生した座標 IE、Netscape系共通 (古いブラウザは除く) (event).clientX、(event).clientY、(event).screenX、(event).screenY clientX、clientY は ブラウザの現在のドキュメント表示枠の中での左からの位置(X)、 上からの位置(Y) です。スクロールしていても、コンテンツの長さや幅とは関係なく、 『表示枠』つまりブラウザで見える部分内での位置です。 左上の角の隅が clientX =0、clientY = 0 の位置。 screenX、screenY は ビジターのモニターでの 左からの位置(X)、上からの位置(Y) です。 モニターの左上の角の隅が screenX =0、screen = 0 の位置。 これらのプロパティの値は数値型(number)の数字となります。 単位は値にはありませんがpx(ピクセル)です。 他にも別用途の座標プロパティがIE、Netscape系両方にそれぞれあります。 マウスカーソルの位置を常に追いかけて行く場合などに onmousemove イベントでイベントを発生させて 現在のカーソル位置を把握する方法として使ったりします。 或いは、補助説明の表示や、サブツールの表示など レイヤーをマウスカーソルの近くに表示させる時などですね。 サンプルソース:
document.onclick = function(e){
var evt = window.event || e; alert(evt.clientX); alert(evt.clientY); } IEは連続イベントは重くなりがちなので、クリックサンプル
clientX = 0
clisentY = 0 screenX = 0 screenY = 0 実際に実用的に考えると『ドキュメント上で』という値を利用するケースがほとんど。 それに関してはイベント座標 ■ 押されたマウスボタン IE、Netscape系共通 (古いブラウザは除く) (event).button イベントを発生させたマウスボタンがどれかを知る為の物。 今はあまり使われません。ブラウザによって動作、状況値が違いますし。 左ボタンは IE 1、Netscape系、Safari、Operaは 0 右クリックは IE、Netscape系 共通で 2 が、ちょっと注意しなければいけない事があります。 onclickイベントでの button IEはonclick(クリックイベント)は左クリックのみに有効で、尚且つ button プロパティはonclickの時は0が入ります。 この0は左ボタンを意味する0ではなく、『ボタンなし』の0つまりデフォルト値です。 ま、結果的には onclickイベントが左クリックにしか反応しない事でonclickイベントの場合のbutton値は 0=左クリックとほぼ同じ事になります。 従って、onclickイベント発生直後のイベントのbutton値は 左クリックなら 全てのブラウザで 0 と言えます。 Opera、Safariもonclickイベントの場合は左ボタンだけに反応します。 従って、buttonプロパティで右ボタンが押されたかどうか?を判定する必要性がありません。 Netscape、FireFoxはonclickイベントの場合でも右ボタンにも反応します。 その為、onclickイベントから左ボタンかどうかを知る必要がある場合は buttonプロパティを調べて判定する必要がでてきてしまいます。 この様にonclickイベントで buttonプロパティを調べる場合は、0なら左クリックだが、イベントは右クリックでも発生するFireFoxとNetscapeがある。 等のイベントの発生条件にも注意しないといけなくなります。 通常はonmousedownで button を調べるが・・・ ボタンをわざわざ調べるのですから、全てのブラウザで右クリックでも反応するイベントを使わないと本来は意味がありません。 ですのでボタンを調べる必要がある時は onmousedown を使います。 が、この場合はIEはIEとして正しいボタン番号の左クリックは 1 を返します。 でもNetscape系では 1 ホーイルボタンなどのセンターボタンのクリックを意味します。 従って、この違いをきちんとブラウザによる番号の違いとして処理させないといけなくなってしまいます。 と言う事で『左クリックなら』という条件に絞り込む時は onclickを使った方が良い事にもなります(^^ゞ 右クリックの場合を調べるなら、onmousedown で event.button の値が2 で共通なので、 この場合はonmousedownを使います。 Operaの右クリック Operaはデフォルトでは右クリックはコンテキストメニューが出て終りで、 onmousedown のイベントが発生しません。 こういった不確定な要素が多く、あまり使われる事がありません。 右クリックの様にサブボタンでコンテキストメニューが出る物に関しては、 今は oncontextmenu というイベントハンドラがあるのでそちらを使います。 サンプルソース:
document.onclick = function(e){
var evt = window.event || e; alert(evt.button); } サンプル(onmousedown) ブラウザによる違い。面倒ですね(^^; ■ 押されたキー (キーボード) IE、Netscape系共通 (event).keyCode イベントを発生させたキーが何のキーかを知るための物。 onkeydownやonkeypressなどのキーが押されて発生するイベントや、 このイベントハンドラを広域タグやdocumentにつけておいて取ります。 サンプルソース:
document.onkeydown = function(e){
これで キーコードが 数値型(number)の数字が取れます。var evt = window.event || e; alert(evt.keyCode); } どのキーコードがどのキーに対応してるかは、サンプルソースを実際に自分のHTMLファイルに書いて動かして、 自分でキーを調べて把握してください。 実際の動きサンプル このkeyCodeで注意したいのが『日本語入力モード』です。 例えば document.onkeydown をつけたケース。(windowsでの検証) 半角/全角キーが押されて日本語入力モードになっていると IE、Safari 日本語入力(文字入力)ができる場所(input)などでは、押されたキーではなく日本語モードとして229がでます。 が、日本語入力のできない場所にフォーカスがあたると押されたキーコードを正しく返します。 Netscape、FireFox 日本語入力(文字入力)可能場所以外でも、どの文字キーでも229が戻ります。 Opera IEと同じですが、日本語入力可能なINPUTなどにフォーカスが当たるとキーコードが返ってきません。 また、日本語のキーボード特有のキーはキーコードが必ず同じ扱いされているかどうかも怪しい節があります。 IEではCSSでime-modeをdisabledにしたりできますので予防策もありますが、 ま、こういったちょっと変な状況においてはキーコードが正しく取れない場合があると言う事です。 この件は文字キーの話ですので、Enterキーなどは普通に取れるので大きな障害ではないと思います。 本来はonkeypress、onkeydownに関する事ですが、 キーを特定してJavaScriptで利用する場合に注意したいのが 『キー本来の動きをどうするか?』です。 IE、FireFox、Safari は イベントを受け取った関数内で処理後に return falseとする事で、キー本来の動きをキャンセル事が可能です。 従って、何の処理もせずに特定のキーの場合だけreturn falseを返せば、 キーボードの一部のキーを無効化する事もでき事になります。 が、 Netscpae、Operaでは、キー本来の動きが return falseで打ち消す事ができない為に、JavaScriptだけに利用する事が難しく、 特定キーの無効化もブラウザによって違う以上は完全にできないと言う事にもなります。 今後、この2つのブラウザでもキーの動きをキャンセル事ができる方法を見つけたらここに書きます。 キーを利用してる例: 文字コード表 このリンクページの下部に、文字コード表があり矢印キーでコントロールできるようにしてるのが、 onkeydownイベントと、event.keycode によるキーの特定です。 documentに付けているので、ボタンでキー操作を有効化、無効化できるように分けています。 まだまだ イベントオブジェクトのプロパティはあります。 リファレンスなどを参考にして、どういう利用価値があるのか?を考えてください。 |
専用ページから申し込むと So-netより高い3万円CB
案ずるより産むが易し
使ってみれば疑問も解決 XREA+ (plus) 206円/月 ( お試し7日間 ) CORE SERVER 428円/月 ( お試し15日間 ) ロリポップ 270円/月 ( お試し期間10日間 ) ヘテムル 1620円/月 ( お試し期間15日間 ) さくら 129円/月 ( お試し期間2週間 ) |
|||||||||||||||
お客さんに振込手数料を払わせていませんか?イーバンク銀行同士なら振込手数料無料。
オフィスで簡単振込み
イーバンク銀行 ビジネス口座
|