2

「折りたたみの切り替え」ボタン(実際には画像)で2つのタスクを実行しようとしています:

  1. ページのセクションが (Cookie から) 折りたたまれた状態で読み込まれる場合は、祖先タグにクラス "collapsed" を追加します。
  2. ボタンがクリックされたときに「折りたたみ」クラスの存在を切り替えるイベント ハンドラーを追加します。

そのために、次のコードがあります。

function toggleCollapsedPlugin(sender, collapseState) {
    // do something
    // expects 1 OR 2 arguments
}

function initCollapsedPlugin() {
    var forumcats = document.getElementById('forums').getElementsByClassName('forumbit_nopost L1');
    var button;
    var buttonParent;
    for (var i = 0; i < forumcats.length; i++) {
        button = forumcats[i].getElementsByTagName('a')[1].getElementsByTagName('img')[0]; // will always exist
        buttonParent = button.parentNode.id; // will never be empty
        if (button.src.indexOf('_collapsed') >= 0) {
            toggleCollapsedPlugin(button.parentNode.id, true);
        }
        button.addEventListener('click', function () { toggleCollapsedPlugin(buttonParent); }, false);
    }
}

Chrome DevToolsinitCollapsedPlugin()の最初の項目に進むと、すべて問題ないように見えます。後で画像の 1 つをクリックして を呼び出すとtoggleCollapsedPlugin(sender)、は常にの最後の項目senderと等しくなります。誰かが理由とこれを修正する方法を説明できますか?buttonParentforumcats

(誰かが回答を提供するのに役立つ場合、これは vBulletin4 ソフトウェアです。)

4

3 に答える 3

2

これは、JavaScriptクロージャの典型的な「落とし穴」です。基本的に、buttonParent変数はすべての反復で上書きされ、参照に渡す無名関数はこの変数addEventListener をコピーしません。

おそらく次のようなものが必要になります。

button.addEventListener('click', (function (parent) {
    return function() {
        toggleCollapsedPlugin(parent);
    };
})(buttonParent), false);

詳細な説明については、JavaScriptクロージャはどのように機能するかを参照してください。

ただし、あなたの場合は、閉じた変数を完全に取り除き、無名関数が呼び出されたときにボタンの親を見つけることができます。これthisが機能するのは、コールバック関数の内部がイベントが登録された要素を参照しbutton、その反復で要素が参照するためです。

button.addEventListener('click', function() {
    return toggleCollapsedPlugin(this.parentNode.id);
}, false);
于 2012-07-12T13:47:04.680 に答える
1

buttonParentを宣言し、それをイベント呼び出しに割り当てますが、最終的に最後のアイテムを参照するまでループ内で変更します。

私はすべてのイベントがこれと同じ参照を指していると思います。

交換

button.addEventListener('click', function () { toggleCollapsedPlugin(buttonParent); }, false);

button.addEventListener('click', function () { toggleCollapsedPlugin(this.parentNode.Id); }, false);

ボタンオブジェクトのイベント内の「this」はボタン自体になります

于 2012-07-12T13:46:48.627 に答える
0

すべてのイベント ハンドラーが同じ変数を参照しているため、buttonParent の最後の値を取得します。buttonParent は、ローカル変数に対する関数のクロージャの一部としてキャプチャされます。

イベント ハンドラーが呼び出されるまでに、buttonParent の値は for ループによって最後のボタンの親に設定されています。

これが、通常、ループ内で関数を宣言すべきではない理由です。通常の解決策は、関数呼び出しを使用して値をイベント ハンドラーに渡すことです。addEventListener を直接呼び出す代わりに、関数を呼び出して、リスナーを追加するボタンを渡します。関数を呼び出すと、変数参照が関数の引数にコピーされるため、イベント リスナーごとに個別のコピーが存在します。

function(b) {
  var buttonParent=b.parentNode.id;
  b.addEventListener(...)
} (button);
于 2012-07-12T14:01:50.700 に答える