読者です 読者をやめる 読者になる 読者になる

Greasemonkeyで要素固有のIDを取得する方法

ニコニコ動画 Greasemonkey

上の記事とあまり関連していないけど。「nicovideo Thumbinfo popup」はリンク先の情報をポップアップ表示するだけのスクリプトなのに,なぜリンクにクラス属性を付与するのか,というお話。
まあぶっちゃけクラス属性を付与しなくても同じような動作は実行できます。ただ,いろいろ面倒なのと,クラス属性を与えないと同じリンクでもポップアップ表示する毎にdivを生成し直したりとか,いろいろ非効率的になりそうだったからです。
実際の実装を簡略化して書くとこんな感じになります。

document.addEventListener('mouseover', function(e) {
    var elem = e.target;
    if(elem.nodeName == 'A' &&
       !hasClassName(elem, created_classname) &&
       url_regex.test(elem.href)) {
        addClassName(elem, created_classname);
        video_id = RegExp/$1;
        var popup = new Popup(elem, video_id);
        popup.showDelay();
    }
}, false);

var Popup = function(elem, video_id) {
    elem.addEventListener('mouseover', this.showDelay.bind(this, false), false);
    elem.addEventListener('blur', this.hideDelay.bind(this, true), false);
    this.thumbinfo = ThumbInfo.get(video_id);
}

ページ内のa要素上にマウスカーソルが初めて載ったときは,documentに追加したイベントリスナでポップアップの生成処理を行う。2回目以降はdocumentのイベントリスナは何もせず,個別のa要素に対応付けられたイベントリスナがポップアップを表示させる,というような感じの処理になっています。
ここで,a要素にクラス属性を付与しているのですが,それはポップアップを生成した/していないを判別するためだけに付与しています。クラス属性を付与する以外の方法で同じような処理ができればいいのですが,ちょっと方法が見つかりませんでした。
以下,やってみて失敗した方法たち。

オブジェクトのプロパティ名としてa要素を使う

こんな感じの処理

var created_links = {};
document.addEventListener('mouseover', function(e) {
    var elem = e.target;
    if(typeof created_links[e.target] == 'undefined') {
       created_links[elem] = new Popup(elem, video_id);
       ...
    }
}, false);

だめだった。オブジェクトのプロパティ名として参照する際にelemが文字列化してしまうようだ。a要素を文字列化すると,href属性の値になってしまい,同じhref属性を持つa要素がページ中に複数存在するとおかしなことになってしまう。

要素にプロパティを追加する

document.addEventListener('mouseover', function(e) {
    var elem = e.target;
    if(!created_links[elem['_GM_popup_created']]) {
       elem['_GM_popup_created'] = true;
       var popup = new Popup(elem, video_id);
       ...
    }
}, false);

うまくいかなかった…はずなのに今試したらうまくいってしまった。なぜだ…。
前はHTMLElement.prototypeにゲッターを追加していたけど,それはできなかった。要素のprototypeではなく,個別の要素そのものにプロパティを追加するのはGreasemonkeyでもできるらしい。ラッパーオブジェクトに包まれてるからできないと思ってた…。

結論

ブログのエントリは実験しながら書くものじゃないね。きちんと最後まで実験してから書きましょう。