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

IE の attachEvent では this が使えない?

mixi エコーの返信をポップアップ表示する GreasemonkeyIE でも使えるようにしようと思って修正していたんですが、ちょっと attachEvent の辺りで若干ハマってしまったのでメモメモ。

とりあえず

変更前のソースはこんな感じ。

// ==UserScript==
// @name           Mixi Echo Popup
// @namespace      http://ohaco.jp/
// @include        http://mixi.jp/recent_echo.pl*
// @version        1.0.0
// ==/UserScript==

(function() {
  var allAnchor = document.getElementsByTagName("a");
  var overlay = document.getElementById('overlay').style;

  for (i = 0; i < allAnchor.length; i++ ) {
    if (allAnchor[i].getAttribute('href').match(/list_echo\.pl.*&post_time=/)) {
      allAnchor[i].addEventListener('mouseover', function(){popupDisplay(this)}, true)
    }
  }

  function popupDisplay(obj) {
    var popup, status, reply, trash, close;
    var url = obj.getAttribute('href').replace("list_echo", "view_echo");

    var httpRequest = new XMLHttpRequest();
    httpRequest.open('GET', url, true);
    httpRequest.send('');
    httpRequest.onreadystatechange = function() {
      if (httpRequest.readyState == 4) {
        var res = httpRequest.responseText;
        var start = res.indexOf('<div class="echoStatus clearfix">');
        var end = res.indexOf('<div class="echoQuestionnaire">');

        if (start != -1) {
          res = res.slice(start,end);
          res = res.replace("echo_member_id_1", "echo_member_id_100");
          res = res.replace("echo_post_time_1", "echo_post_time_100");
          res = res.replace("echo_nickname_1", "echo_nickname_100");
          res = res.replace("echo_body_1", "echo_body_100");

          popup = document.createElement("div");
          popup.innerHTML = res;
          popup.style.position = 'absolute';
          popup.style.margin = '-118px 0 0 0';
          popup.style.padding = '10px 0';
          popup.style.height = '88px';
          popup.style.overflow = 'hidden';
          popup.style.zIndex = '1';
          popup.style.background = '#fff';
          popup.style.border = 'solid 1px #999';
          popup.addEventListener('mouseover', function(){close = false;}, true);
          popup.addEventListener('mouseout', function(){close = true;}, true);

          var popupDiv = popup.getElementsByTagName('div');

          for (i = 0; i < popupDiv.length; i++ ) {
            if (popupDiv[i].className == 'status') {
              status = popupDiv[i];
              status.style.marginTop = '0';
              status.style.fontSize = '12px';
              status.getElementsByTagName('span')[0].style.fontSize = '11px';
            }
          }

          var popupAnchor = popup.getElementsByTagName('a');

          for (i = 0; i < popupAnchor.length; i++ ) {
            if (popupAnchor[i].className == 'reply') {
              reply = popupAnchor[i];
              reply.removeAttribute('onclick');
              reply.setAttribute('onclick', 'show_echo_reply_layer(event, 100)');
            }
            if (popupAnchor[i].className == 'trash') {
              trash = popupAnchor[i];
              trash.style.display = 'none';
            }
          }

          overlay.display = 'block';
          overlay.opacity = 0;
          close = true;

          obj.parentNode.insertBefore(popup, obj.parentNode.firstChild);
          document.addEventListener('click', function(){
            if(close) {
              obj.parentNode.removeChild(popup);
              overlay.display  = 'none';
            }
          }, true)
        } else {
          alert('\u8A72\u5F53\u3059\u308B\u30A8\u30B3\u30FC\u304C\u3042\u308A\u307E\u305B\u3093\uFF1E\uFF1C');
        }
      }
    }
  }
})();

冒頭の for 文の部分で addEventListener を使ってアンカーのマウスオーバーイベントを追加していますが、 IE では addEventListener が使えないので、代わりに attachEvent を使うように書きかえなくてはいけません。

IE 等の addEventListener が使えないブラウザの場合は attachEvent を使うように変更

とりあえず、特に何も考えずに下のように変更してみましたが…(一部抜粋)

  for (i = 0; i < allAnchor.length; i++ ) {
    if (allAnchor[i].href.match(/list_echo\.pl.*&post_time=/)) {
      if (document.addEventListener) {
        allAnchor[i].addEventListener('mouseover', function(){popupDisplay(this);}, true);
      } else if (document.attachEvent) {
        allAnchor[i].attachEvent('onmouseover', function(){popupDisplay(this);});
      }
    }
  }

うーん、動きません><
addEventListener の方では this で、ちゃんとマウスオーバーイベントが発生したアンカーの要素が渡されるんですが attachEvent の方だと、どうも this では上手く渡せないみたいです。

いろいろ調べてみた結果

IE 様には event.srcElement というものがあるらしいので、それを使ってみました!
以下、テストスクリプト

// ==UserScript==
// @name           Mixi Echo Popup
// @namespace      http://ohaco.jp/
// @include        http://mixi.jp/recent_echo.pl*
// ==/UserScript==

(function() {
  var allAnchor = document.getElementsByTagName("a");
  for (i = 0; i < allAnchor.length; i++ ) {
    if (allAnchor[i].href.match(/list_echo\.pl.*&post_time=/)) {
      if (document.addEventListener) {
        allAnchor[i].addEventListener('mouseover', function(){popupDisplay(this);}, true);
      } else if (document.attachEvent) {
        allAnchor[i].attachEvent('onmouseover', function(){popupDisplay(event.srcElement);});
      }
    }
  }

  function popupDisplay(obj) {
    alert(obj.getAttribute('href')); // http://mixi.jp/list_echo.pl?id=自主規制&post_time=自主規制
  }
})();

おー、ちゃんと渡せてるっぽい!

結局

IE の attachEvent 内の this が何を指しているのかはわかりませんでした><
他にも IE 用に書きかえないといけない部分がいっぱいありそうだなぁ…