IT戦記 このページをアンテナに追加 RSSフィード Twitter

2008-05-17

onclick 属性問題について

本気でやるならonclick属性は避けてライブラリを活用すべき - id:HolyGrailとid:HoryGrailの区別がつかない日記 に関して

この先、 HTML に onclick と書いても石を投げられないように書いておく><

僕も onclick 属性がダメだと思っていた時代もありました。でも、今は時々使うなあ。

(あ、でも、 HTMLJS で分業している場合は、使わないほうがいいよね^^;そこだけは言っておく。)

JavaScript 入門の記事を書くときに onclick 属性がダメだとしたら、いろいろ本質的でないことを説明しなきゃいけない。

現状では onclick 属性が、もっとも簡単に HTML にイベントをマッピングできる方法だから、そんなに目くじらたてなくてもいいんじゃないかなあ?

たとえば

以下は、クリック時に href に GET を送るための onclick 属性。

<a id="hoge" href="/hoge" onclick="

var req = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('MSXML2.XMLHTTP');
req.open('GET', href);
req.send(null);
return false;

">click</a>

これを onclick 属性なしで「神経質に」やると以下のようになる

<script type="text/javascript">
// イベントハンドラを追加
function attach(elm, eventType, handler) {
    if (elm.addEventListener) {
        elm.addEventListener(eventType, handler, false);
    }
    else {
        elm.attachEvent('on' + eventType, handler);
    }
}

// イベントハンドラを削除
function detach(elm, eventType, handler) {
    if (elm.removeEventListener) {
        elm.removeEventListener(eventType, handler, false);
    }
    else {
        elm.detachEvent('on' + eventType, handler);
    }
}

var elm = document.getElementById('hoge');

var handler = function(event) {

    /* 本質的な部分はこの三行だけ */
    var req = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("MSXML2.XMLHTTP");
    req.open('GET', elm.href);
    req.send(null);

    event = event || window.event;

    // イベントによる default 動作(ページ遷移など)を無効化する
    if (event.preventDefault) {
        event.preventDefault();
    }
    else {
        event.returnValue = false;
    }

    // 必要であれば、イベント浮上も止める
    if (event.stopPropagation) {
        event.stopPropagation();
    }
    else {
        event.cancelBubble = true
    }
};

// イベントを割り当てる
attach(elm, 'click', handler);

// 古いブラウザだと unload 時に detach しないとメモリリークする
attach(elm, 'unload', function() {
  detach(elm, 'click', handler);
  detach(elm, 'unload', arguments.callee);
});
</script>
(略)
<a id="hoge" href="/hoge">click</a>

もちろん、これはおおげさな例だけど、 onclick 属性を使わないことによって本質的ではないことをたくさん書かないといけない

という訳で

JavaScript の入門記事なら onclick 属性はありだと思う。

余談 1:イベント割当て以外で、ブラウザの非互換ってあんまりない

実は、基本的な DOM に関して言えば、イベントの割当て以外の非互換ってあまりないと思う。

言い方を変えると、 IEJS で特に実装がムチャクチャなのはイベント周りってこと。

だから、 onclick 属性さえ使えれば、結構スムーズに入門記事が書けるんじゃないかあ。

余談 2:ライブラリに関して

ライブラリは流行り廃りが激しいから、特定のライブラリに依存した「おまじない」ばかり覚えているのはどうかと思うなあ

やっぱり、 DOM を直接書けたほうが、知識としては幅広く使えると思いますよ。

os0xos0x 2008/05/17 12:12 イベントの割当て(についてのIEの実装)は本当に困ったものですよね。。
ただ、
// 古いブラウザだと unload 時に detach しないとメモリリークする
これはIE6の昔あったバグの件ですよね。これって2007年6月に直っている( http://d.hatena.ne.jp/zorio/20070626/1182875782 )ので、現在ではそれほどプライオリティの高くない(バッド)ノウハウだと思ってます。
もちろん「おおげさな例」とある通り、意図的に書いているのだと思います。ただ、メモリリーク対策が必須のものと誤解されてしまうことは本意ではないだろうと思ったので、老婆心ながらツッコミしておきます。

amachangamachang 2008/05/17 13:54 そうなんですか。
知りませんでした><

もう unload 時のデタッチは必要ないんですね。

os0xos0x 2008/05/17 15:29 あ、ただIEの対応は完全ではないらしい( http://d.hatena.ne.jp/zorio/20070918/1190135017 )ので、不要とは言い切れない面がありますね。
ちなみに、prototype.jsの1.5くらいまではunload時にdetachしてましたけど、1.6ではdetachしなくなってるみたいです。

ひろきのだいちひろきのだいち 2008/05/17 23:48 うーん。
モダーンなjavascript関連のことを知りたい場合はjsと検索、
onclick見たいなDOM Levelの最初のほうを知りたい場合はjavascriptと検索して探すようにしていることを
思い出した。

いっそ、そういう区別をつけてしまうほうがこの問題はスッキリしそうw

atsukan_rockatsukan_rock 2008/05/18 12:06 パフォーマンス面で
・YAHOO! UI Library(YUI)の YAHOO.util.Event.addListener メソッド
・HTML 属性での記述
を比較したことがあるんですが、HTML 属性の方がはるかに(IE6 で 10 倍程度だったような・・)速かった記憶があります。この差は、ブラウザ上に画面が表示されてからユーザが画面上での操作を開始できるまでの時間に直結します(普通イベントリスナの attach は window.onload イベントで処理するので)。

ただし、パフォーマンス面での差が顕在化してくるのは、1000 回以上はその処理を繰り返す場合なので、そういう場合以外は神経質になる必要はないのでしょうが。

ちなみに僕の場合、いわゆる「画像ボタン」をコンポーネント化していて、onmousedown イベントなどで画像をすげ替える処理でこの差が顕在化しました(画面上に 5000 個も表示されるというデザイン‥)。そのときのはサーバサイドのコンポーネントで、HTML をどう出力するかをコンポーネントが制御できる場合だったので、HTML 属性を使った記述とすることで対処しました。

このようなパフォーマンスの差が無視できない場合に、HTML 属性ではなく DOM でのイベントリスナ attach でやる方法ってないものでしょうか?(個人的には、DOM でのイベントリスナ attach でやりたいので)

はてなユーザーのみコメントできます。はてなへログインもしくは新規登録をおこなってください。