opera:hige このページをアンテナに追加

 | 

2008-05-14

del.icio.us のポスト画面でタグが表示されなくなる問題 13:45  del.icio.us のポスト画面でタグが表示されなくなる問題 - opera:hige を含むブックマーク はてなブックマーク -  del.icio.us のポスト画面でタグが表示されなくなる問題 - opera:hige

この前 Kuruman が悩んでた問題*1.私の方でもその現象に遭遇したので検証してみる.

Opera でのみ起こった現象. Firefox では問題なし.

原因はどうもなんか新しいことがあったらページ上部に表示される


f:id:higeorange:20080514123158p:image


これのようだ.

これを消したければ もちろん hide this をクリックすればいいわけだけど,クリックしたときにどういうことが行われているかというと

    function hideNotifyBar() {
        Cookies.set('notifications_seen', Cookies.get('notifications_seen') + ',17');
        (new fx.Height(
            $id('notifybar'), { 
                duration:20, 
                onComplete: function(){ remove($id('notifybar')) } 
            }
        )).toggle('height');
        return false;
    }
    addLoadEvent(function() {
        if ($id('notifyclose')) $id('notifyclose').onclick = hideNotifyBar;
    });

この スクリプトでわかるように hideNotifyBar() が呼ばれている.

一度 hide this が呼ばれるとクッキーに保存されて以降このコード自体がソースに含まれなくなる.

ちなみに オブジェクト fx は定義されている javascript ファイルがロードされていないのでエラーとなる.これは今回とはまた別の問題.


今回問題なのは hideNotifyBar をイベント追加している addLoadEvent 関数.

これを定義しているのが http://del.icio.us/ui/static/delicious.old.js?v=61E-123 のスクリプト

どういう風に定義しているかというと

function addLoadEvent(f) { var old = window.onload
	if (typeof old != 'function') window.onload = f
	else { window.onload = function() { old(); f() }}
}

となっている.

つまり addLoadEvent で追加された関数は window.onload に追加していくという関数である.


ロードされたときに実行されるべきのは

  • init()*2 ( <body onload="init()"> )
  • rmPostAddEvent() ( addLoadEvent で追加 )
  • hide this にイベントを付加する関数 ( addLoadEvent で追加 )

となる.


どういう風に実行されるかをわかりやすくするためにテストページを用意した.

http://higeorange.com/tmp/testLoad.html

注意: alert が出る.

このページのソースは

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
    "http://www.w3.org/TR/html4/strict.dtd">
<html lang="ja">
    <head>
        <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
        <title></title>
        <script type="text/javascript">
            addLoadEvent(function() {
                log("first is loaded");
            });
            function init() {
                log("init is loaded");
            }
            function addLoadEvent(f) {
                var old = window.onload;
                if (typeof old != 'function') {
                    log("old is not function, " + 'add: ' +f);
                    window.onload = f;
                } else {
                    log('old: ' + old + ', add: ' + f);
                    window.onload = function() {
                        old();
                        f();
                    }
                }
            }
            function log(s) {
                alert(s);
            }
        </script>
    </head>
    <body onload="init()">
        <script type="text/javascript">
            addLoadEvent(function() {
                log("second is loaded");
            });
        </script>
    </body>
</html>

となっており,

del.icio.us と同じように

body onload で init,

head 内の script で addLoadEvent で追加 (rmPostAddEvent にあたる) 実際には外部ファイルで呼び出しているけど同じはず 以下 first,

body 内の script で addLoadEvent で追加 (hide this にイベントを付加する関数にあたる) 以下 second,

としている.


でこれを OperaFirefox 実行してみると違いが出た.

以下 alert の内容を列挙.

Opera

old is not function, add: function() {
                log("first is loaded");
            }
old: function() {
                log("first is loaded");
            }, add: function() {
                log("second is loaded");
            }
first is loaded
second is loaded

Firefox

old is not function, add: function () {
    log("first is loaded");
}
old: function onload(event) {
    init();
}, add: function () {
    log("second is loaded");
}
init is loaded
second is loaded

これから分かることは

window.onload の内容は

Opera: なし -> first (head内) -> first & second (body 内) あれ init はどこで追加されたのよ?

Firefox: なし -> first -> init -> init & second

と変化している.


実際に実行されるのが

Opera: first & second

Firefox: init & second

となる.


この結果から Opera では タグが表示されないのである ( init が呼ばれないため ).

また Firefox でも意図通り動いていない気がする.


このバグを直すためにスクリプトを書いた.

// ==UserScript==
// @include http://del.icio.us/*?*url=*
// ==/UserScript==
//

(function() {
    opera.defineMagicFunction(
        'addLoadEvent',
        function(func, w, f) {
            var old = window.onload;
            if (typeof old != 'function') {
                window.addEventListener('load', f, false);;
            } else {
                window.onload = function() {
                    old();
                    f();
                }
            }
        }
    );
})();

最初に追加される rmPostAddEvent を window.addEventListener で追加. window.onload が undefined であるとき

これで不思議なのが 2回目に addLoadEvent が呼ばれたときには window.onload に init が入っている.入っていないとこのスクリプトは動かないわけだが.

だったら直す前でもこの時点で init が入っててもおかしくないのになぁ.

ということで追加の順番に関してもう少し検証が必要.


とりあえず 上のスクリプトでタグが表示されないバグは直る.

お知らせが出たら hide this を押したらいいんだけども.


追記

Konqueror は Firefox と同じ挙動.

お願い

IE & Safari での動作を知りたいので

http://higeorange.com/tmp/testLoad.html

において誰かテストお願いします.


再追記

Thx. os0x.


os0x さんのコメントどおりの挙動だとさらに疑問が.

second がなかった場合を考える.


window.onload の内容.

Opera 以外のブラウザ: first -> init

Opera : first -> init は追加されず.


実行結果

Opera 以外のブラウザ: init() 予想通り

Opera : init() あれ? first が実行されるのでは?


テストページ

http://higeorange.com/tmp/testLoad2.html


再々追記

どうも混乱してきたのでこれで打ち切り.

<body onload="init()"> ってしてるのがまずいだろうな.

てことで del.icio.us に報告しておきました.

再々々追記

再追記で書いた second がなかった場合の挙動について多分分かった.

【javascript】body.onload=””とwindow.onloadについて|WEB制作のメモ

window.onload と document.body.onload が両方あった場合あとから追加されたほうが実行されるということ.

*1:なんか別の問題っぽい

*2:タグ表示などの全般

os0xos0x2008/05/14 14:09IE7とSafari3(Win/Mac共に)はFirefoxと同じ挙動でした。
で、Operaさんは document.body.onload だけにinitが入っているみたいです。
この挙動から推察するには、bodyタグにonloadがあったらとりあえずdocument.body.onloadに入れて、window.onloadがnullだったらそっちにも入れる。けど、window.onloadがnullでなかったら上書きはしないようになってるぽいですね。

トラックバック - http://orera.g.hatena.ne.jp/higeorange/20080514
 |