http://www.w3.org/TR/DOM-Level-2-Events/events.html
If multiple identical EventListeners are registered on the same EventTarget with the same parameters the duplicate instances are discarded. They do not cause the EventListener to be called twice and since they are discarded they do not need to be removed with the removeEventListener method.
「they are discarded 」というのがメモリに蓄積せずに廃棄するのか、単に実行されないことをdiscardといってるのがわかりません。
iPhoneのsafari自体が正しく実装してるかもわかりません。
追加されたイベントリスナー一覧を取得する関数もあればいいのですが・・・。
SafariのJavaScriptのデバッグ用でみると追加され続けているように見えますが、メモリに蓄積されてるのかが不明です。
iOSのSafari限定ならば、pageshowというイベントが使えます。
http://taiyoslab.com/event_test/
文字にアラートを出すイベントを貼り付けています。
window.addEventListener('pageshow', function(){
var element = document.getElementById("hoge");
element.addEventListener('click', function(event){
alert('event assigned');
})
});
Mobile Safariで使えるイベントのリファレンスはこちらになります。面白いものが揃ってますよ。
https://developer.apple.com/library/safari/#documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html#//apple_ref/doc/uid/TP40006511-SW5
なお、Mobile SafariではSetTimeoutやsetIntervalで回すループは一定の時間が経過すると止まる仕様だったような記憶があります。
バージョンを負うごとに緩和されていますけど、再帰的にイベントをaddする方法は使えないかもしれませんね。
> --
> このメールは Google グループのグループ「html5j.org」の登録者に送られています。
> このディスカッションをウェブ上で閲覧するには、https://groups.google.com/d/msg/html5-developers-jp/-/BHrEj8hG1e4J にアクセスしてください。
> このグループに投稿するには、html5-dev...@googlegroups.com にメールを送信してください。
> このグループから退会するには、html5-developer...@googlegroups.com にメールを送信してください。
> 詳細については、http://groups.google.com/group/html5-developers-jp?hl=ja からこのグループにアクセスしてください。
2012年3月29日18:32 shinriyo <shin...@gmail.com>:
> はじめまして。
>
> iPhoneでのloadイベントについて質問です。
> iPhoneではブラウザバックによって来たページではloadイベントを行なってくれません。
>
> 例えば以下のコードはhogeエレメントにclickイベントを追加してますが、実行されません。
> window.addEventListener('load', function(){
> var element = document.getElementById("hoge");
> element.addEventListener('click', function(event){
> }
> }
>
> 仕方ないので、ブラウザバックしても生きているsetIntervalを使用したいと思っています。
> window.addEventListener('load', function(){
> var element = document.getElementById("hoge");
> setInterval(function() {
> element.addEventListener('click', function(event){
> }
> , false) }, 1000);
> }
このコードの場合、登録されるハンドラとなるfunctionオブジェクトは、毎回生成しており、それぞれ別オブジェクトなので、インターバル毎にハンドラが登録されることになると思います。
function onClick(event) {...}
setInterval(function(){
element.addEventListener("click", onClick, false);
}, 1000)
であれば、登録されるハンドラは一回で済むと思います。(Safariが仕様どおり実装していれば)
Safariでのデバッグの仕方は分かりませんが、Firefoxの場合、登録されたリスナを得る方法がありますので、参考程度に手順を載せておきますね。
1. about:config を開く
devtools.chrome.enabled を true に変更
2. スクラッチパッドを開く
メニューの実行環境をWebページに変更
以下のコードを書いて実行
(`gBrowser.mCurrentBrowser.contentWindow` は現在開いているタブのwindowオブジェクトになります。)
var els = Cc["@mozilla.org/eventlistenerservice;1"]
.getService(Ci.nsIEventListenerService);
var infos = els.getListenerInfoFor(gBrowser.mCurrentBrowser.contentWindow, {})
infos.forEach(function(info){
Application.console.log(info.type + " => " + info.toSource())
})
参考: https://dev.mozilla.jp/2010/04/nsieventlistenerservice/
3. エラーコンソールを開く
2. の結果が出ているはず
こんな感じ
http://cache.gyazo.com/9bba655383b808fb037fbda40a473c8d.png
>
>
> この場合、1秒に一度clickイベントを追加してくれますが、追加されてメモリに蓄積されるのかが心配です。
> JavaScriptの仕様にはaddEventListenerはすでにある場合は、単に実行しないだけと書かれていましたが、
> イベントが沢山追加されてしまうかドウかまで記載してませんでした。
>
> 宜しくお願いします。
>
pageshowイベントを試してみました。
loadと同じ効果でした。ブラウザバックしても、(たまたまロードしたときに?)たまに動く程度で、最初に表示した時にしかうまく動作しませんでした。
「iOS 4.0 and later」とありますのでiOSの判別には使えそうですね。
>teramakoさん
提示した関数は簡易的なものですので、実際に行ってる処理が少し複雑ですが、その方法を試してみます。
しかし、Mobile SafariでsetIntervalのループは一定の時間が経過すると止まる仕様が本当に存在するのかどうかが気になりますが・・。
DOM level 3 Events では addEventListenerの第三引数がオプショナルに変更されてたんですね。(今更ですが)
さて、「ブラウザバック」ということで、Safari の「戻る」、「進む」アイコンをタップすることによるページ遷移を試してみました。
やったこと。
* page1.html, page2.html を作成
* それぞれ、他方のページにA要素でアンカーを張る。(BODYにA要素のみ)
* SCRIPT に、以下のコードを書く。
var pageEventsLintener = function( event ){
console.log( 'pageEvents ' + event.type );// イベントリスナの最初に書く
/* event process */
}
window.addEventListener('load', pageEventsLintener );
window.addEventListener('pageshow', pageEventsLintener );
* iPhone 4GS(iOS 最新)でテスト。
両ページともイベント処理である pageEvents は実行されているようです。
load : サーバーから読み込みが完了した直後
pageshow : ページがレンダリング完了した直後(load直後はもちろん、ブラウザバックによっても)
> 実際に行ってる処理が少し複雑
ということなので、イベント処理(上記例でいう /* event process */ 部分 )にエラーが発生していないかテストした方が近道かもしれません。
私自身の経験則になりますが、
最近の JavaScriptのエンジンは、エラーが発生すると完全に沈黙し、以降の処理を行いませんから、
イベントリスナに実装した処理にエラーがあると「イベントが発生しない!?」と誤解しがちになります。
こうした経験から、「他の処理に優先して console.log( event.type ) を書く」ようにし、
ブラウザのイベントが発生していないのか、それとも、単に自前で実装したコードにエラーが発生しているのかを判別しやすくしました。
コード補完機能の無い環境で書かなければならないときには、typo も混入しやすいですので、試してみると良いかもしれません。
Mobile Safari の
setIntervalのループは、表示中のページが非アクティブになったら止まります。非アクティブからアクティブになるとインターバルが再開します。ゲームループを作り、フレームカウンタをデバッグコンソールに表示したときに確認しました。これは、バッテリ消費を抑えるためだとか。一方、アクティブ時には、省電力モードが機能したり、エラーが無い限り動き続けます。
以下、余談ですが
iPhone 3GSを利用時、「setIntervalのループは中断しない」という判断で、JavaScriptによるGPS利用を試したことがあります。
自動車の助手席にiPhoneを放置しつつ、約20分間、ゲームループ(約16fps)で位置情報を連続取得したのですが、満タンだったバッテリ残量が半分以下に激減して慌てました(笑
2012年3月30日11:01 shinriyo <shin...@gmail.com>:
> --
> このメールは Google グループのグループ「html5j.org」の登録者に送られています。
> このディスカッションをウェブ上で閲覧するには、https://groups.google.com/d/msg/html5-developers-jp/-/HX89FEm-Ns0J
Mobile Safariでの検証はちゃんとしていないのですが、unloadイベント設定することで、
ブラウザバック時のonloadを呼び出させるテクニックがあります。
http://d.hatena.ne.jp/hiratara/20080308/1204955060
--
太田昌吾 <os0...@gmail.com>
2012年3月30日14:03 Akitoshi Manabe <akitosh...@gmail.com>:
返信ありがとうございます。
こちらの勘違いでした。しかし、発生しなかったのはエラーではありません。
ソーシャルゲームのプラットフォームの中のiframe配下に入っており、ブラウザバックしても親フレームに吸収されてしまったようです。
プラットフォームになしで単体のJSであれば、特に問題なくloadが実行されました。
長文ありがとうございました。お手数をおかけいたしました。
返信ありがとうございました。一応その方法も以前試しましたが、できませんでした。
こちらもプラットフォームに吸収されてしまってるようでした。
if(self!=top){top.location.href=self.location.href;}
gMailやYouTubeなど、Googleの関連サービスでは iframe が常用されているのですが、
HTMLのマークアップコードを提供してカンタンにシェアしてもらえるとか、
クロスドメイン間でもコンテンツ表示できるとか、その気になれば全画面表示できるとかありすが、
JavaScriptを主体に考えたときに何よりも重要な目的として、「アプリの共通ロジックと、データアセットを完全に分離する」目的があります。
Youtubeの場合、動画プレーヤーとなるプログラム(共通ロジック)と、動画データ(データアセット)という考え方で、
同じプレーヤーClassのインスタンスを生成して複数動画の同時再生をも可能にしてます。
ソーシャルゲームのサイトの場合、データアセットにゲームプログラムが含まれるので、
windowオブジェクトにゲーム関連のオブジェクトをどう構築するべきかという点も含めてゲーム開発者にルール提供しない限り、
ロジックとデータの分離を目的にした iframeの使い方ができないのが現状だと思います。
結局、簡易実装されただけの iframeでエンドユーザの操作に支障を来しているのだから、フレームを抜けるロジックは許容されるべきだと思い、1行を例示しました。
勿論、このコードを利用する場合は、ソーシャルゲームのサイトが提示する規約に従うべきです。
しかし、iframe 周辺はまだまだ奥が深いと感じますねぇ。
2012年3月30日18:24 shinriyo <shin...@gmail.com>:
> >太田さん
>
> 返信ありがとうございました。一応その方法も以前試しましたが、できませんでした。
> こちらもプラットフォームに吸収されてしまってるようでした。
>
> --
> このメールは Google グループのグループ「html5j.org」の登録者に送られています。
> このディスカッションをウェブ上で閲覧するには、https://groups.google.com/d/msg/html5-developers-jp/-/FflU5QQjtCAJ にアクセスしてください。
眞鍋さん返信ありがとうございます。こちらのコードは参考になりました。if(self!=top){top.location.href=self.location.href;}iPhoneのsafariの傾きを実験するため以下のようにしてみました。window.addEventListener('load', function(){if(self!=top){window.top.location = window.self.location;}window.onorientationchange = function () {switch (window.orientation ) {case 0:break;case 90:alert('横向きには対応していません');break;case -90:alert('横向きには対応していません');break;}}親フレームを越えて傾きが取得できるようになりましたが、弊害として、いろいろ不具合が出てしまいました。イベントリスナーだけをピンポイントで設置することは可能なのでしょうか?宜しくお願いします。
> 親フレームを越えて傾きが取得できるようになりましたが、
「ソーシャルゲームのサイトを完全に離れる」ことに注意します。
if(self!=top){window.top.location = window.self.location;}
(このコードは、全体の最初に1回実行するだけでOK)
iframe にあるコンテンツでこのコードで実行すると、親フレームの url を再読み込みさせて、自身が window フレーム になるコードです。
フレーム(window枠)の階層を考えると、
ブラウザ
+- window (topフレーム) ... ソーシャルゲームサイト。
+- iframe (selfフレーム) ... 開発するゲームアプリケーション
↓↓↓ フレームの拘束を抜けて、こう。
ブラウザ
+- window (topフレーム) ... 開発するゲームアプリケーション
このため、
* ソーシャルゲームサイトがAPIを提供する場合、APIが使えなくなる可能性もある
* 反面、デバイスの持つ様々なイベントを取得できるなど自由な開発が可能。
* 10年前のFRAMESETを使ったサイトで著作者が不明瞭になるのを防ぐ目的も。
> イベントリスナーだけをピンポイントで設置すること
必要に応じてイベントを有効化/無効化するだけで実現できます。
一般的には
addEventListener に対して、
removeEventListenerも活用するとか、window.onorientationchangeのような
DOM level-1 で設定しなければ動かないイベントではnullを代入するとかになりますが、
var enable_OriantationEvent = false;
window.onorientationchange = ( enable_OriantationEvent )
? activeOrientationHandler // 有効にする
: null; // 無効にする
ゲームの場合
addEbentListener( 'type', SysEventHandlersInGameScene );
として、SysEventHandlersInGameScene 関数が アクティブなゲームシーン内のイベント定義部分を呼び出して実行という形もあります。
ゲームは入力イベントがほとんどなので、いちいち removeEventListener() するコストを省いて、
ゲームとしてのパフォーマンスを優先させる。
SourceForge に HTML5Game Dev というプロジェクトでDLできるコードがそんな感じでした。
http://sourceforge.jp/projects/h5gamedev/
2012年4月9日16:57 shinriyo <shin...@gmail.com>:
> --
> このメールは Google グループのグループ「html5j.org」の登録者に送られています。
> このディスカッションをウェブ上で閲覧するには、https://groups.google.com/d/msg/html5-developers-jp/-/DQQ1hx3UU-8J