1. ホーム
  2. javascript

[解決済み] addEventListenerを使用したハンドラ内のthisという値について

2023-03-10 17:49:47

質問

プロトタイピングでJavascriptオブジェクトを作成しました。 私は、テーブルを動的にレンダリングしようとしています。 レンダリング部分は単純でうまく動作しますが、私は動的にレンダリングされたテーブルの特定のクライアント側イベントを処理する必要があります。 これも簡単です。 問題は、イベントを処理する関数内の "this" 参照にあります。 オブジェクトを参照する代わりに、イベントを発生させた要素を参照しています。

コードを参照してください。 問題のある箇所は ticketTable.prototype.handleCellClick = function() :

function ticketTable(ticks)
{
    // tickets is an array
    this.tickets = ticks;
} 

ticketTable.prototype.render = function(element)
    {
        var tbl = document.createElement("table");
        for ( var i = 0; i < this.tickets.length; i++ )
        {
            // create row and cells
            var row = document.createElement("tr");
            var cell1 = document.createElement("td");
            var cell2 = document.createElement("td");

            // add text to the cells
            cell1.appendChild(document.createTextNode(i));
            cell2.appendChild(document.createTextNode(this.tickets[i]));

            // handle clicks to the first cell.
            // FYI, this only works in FF, need a little more code for IE
            cell1.addEventListener("click", this.handleCellClick, false);

            // add cells to row
            row.appendChild(cell1);
            row.appendChild(cell2);


            // add row to table
            tbl.appendChild(row);            
        }

        // Add table to the page
        element.appendChild(tbl);
    }

    ticketTable.prototype.handleCellClick = function()
    {
        // PROBLEM!!!  in the context of this function, 
        // when used to handle an event, 
        // "this" is the element that triggered the event.

        // this works fine
        alert(this.innerHTML);

        // this does not.  I can't seem to figure out the syntax to access the array in the object.
        alert(this.tickets.length);
    }

どのように解決するのですか?

ハンドラをインスタンスにバインドする必要があります。

var _this = this;
function onClickBound(e) {
  _this.handleCellClick.call(cell1, e || window.event);
}
if (cell1.addEventListener) {
  cell1.addEventListener("click", onClickBound, false);
}
else if (cell1.attachEvent) {
  cell1.attachEvent("onclick", onClickBound);
}

このイベントハンドラは event オブジェクト(第一引数として渡される)を正規化して handleCellClick を適切なコンテキストで呼び出します (つまり、イベントリスナーがアタッチされた要素を参照します)。

また、ここでのコンテキストの正規化(つまり、適切な this を設定すること) によって、イベントハンドラとして使われる関数 ( onClickBound ) と要素オブジェクト ( cell1 ). IE のいくつかのバージョン (6 と 7) では、これはメモリリークを引き起こす可能性があり、またおそらくそうなります。このリークは、本質的には、ネイティブ オブジェクトとホスト オブジェクトの間に存在する循環参照により、ページの更新時にブラウザがメモリを解放できないことです。

これを回避するには、a) ネイティブとホスト オブジェクトの間の this 正規化をやめる、b) 別の (より複雑な) 正規化戦略を採用する、c) ページアンロード時に既存のイベントリスナーをクリーンアップする、すなわち removeEventListener , detachEvent および要素 null を追加しました (これは残念ながら、ブラウザの高速履歴ナビゲーションを無用の長物にしてしまいます)。

また、この処理を行う JS ライブラリを見つけることもできます。それらのほとんど (例: jQuery、Prototype.js、YUI など) は、通常 (c) で説明したようにクリーンアップを処理します。