クロスブラウザチックなonbeforeunload。

Filed Under (Uncategorized) by on 15-10-2008

どうも。ひろきのです。

Prototype.jsといえばクロスブラウザでDOMの足回りを提供してくれているすばらしいフレームワークですが、ちょっとばかり不満があります。

Event.observeで、あたえたeventHandlerの戻り値を完全に無視しているところです。
具体的には

JAVASCRIPT:
  1. function createWrapper(element, eventName, handler) {
  2.     var id = getEventID(element);
  3.     var c = getWrappersForEventName(id, eventName);
  4.     if (c.pluck("handler").include(handler)) return false;
  5.  
  6.     var wrapper = function(event) {
  7.       if (!Event || !Event.extend ||
  8.         (event.eventName && event.eventName != eventName))
  9.           return false;
  10.       Event.extend(event);
  11.       handler.call(element, event) //ここでハンドラの戻り値を返してない。
  12.     };
  13.  
  14.     wrapper.handler = handler;
  15.     c.push(wrapper);
  16.     return wrapper;
  17.   }

このぐらい書き換えろとか思うかもしれませんがこれらの重要なライブラリについて元自体をいじってしまうのが難しい環境もあるかもしれません。

そもそもEventObjectのevt.returnValueを使えばいいじゃんとかありますが、WebKitのevt.returnValueの挙動が他のブラウザとちがい、実際にハンドラからreturnしないと受け付けないというものがあるため、コレ自体を完全にクロスブラウザにするのは難しかったりします。(evtオブジェクトのreturnValueを上書きできないのかも。。)

そもそもOperaにはonbeforeunloadが存在しませんし・・・

このハンドラからの戻り値をダイレクトに使用しなければならない場面はそう多くはありませんが、最近のWebアプリケーションでは一般的になっている編集中にページ遷移しようとするとでるメッセージ(Gmailなどで書きかけで移動するとでてくるやつ)を実現するためにはこの機能が必須になります。

ちなみにOperaの場合はステートが保存されているので、
戻るボタンを押せば編集中の内容が復帰します。

JAVASCRIPT:
  1. Event.observe(window,'beforeunload',function(e){
  2.     return e.returnValue = 'このままページを移動すると編集中の内容は保存されませんがよろしいですか?';
  3. });

のようにしたい場合、現行のPrototype.jsでは上手くいきません。

そこで、Webkitでbeforeunloadの場合に限り、Event.observeをフックすることを考えました。

以下コードです。

実用上は、Webkitの場合にはaddEventListenerを用いればよいのですが、透過的に行えたほうが自然といえば自然なので。

同様のテクニックで、Prototype.jsのカスタムイベントをよりリッチにすることができますが、それについてはまた今度。

JAVASCRIPT:
  1. if(Prototype.Browser.WebKit)(function(){
  2.    
  3.     Event._observe = Event.observe;
  4.     Event.observe=function(element,name,func){
  5.         if(/beforeunload/.test(name)){
  6.             element.addEventListener('beforeunload',function(evt){
  7.                 return func.bind(element)(evt);
  8.             });
  9.         }else{
  10.             Event._observe(element,name,func);
  11.         }
  12.     }
  13.     Object.extend(window, {
  14.         observe: Event.observe.methodize()
  15.     });
  16. })();

Post a comment