29

私は人々が彼らのページに含めているJavascriptを持っています。私のJavascriptには、独自の名前空間に分割され、グローバル変数(ただし、「$」または「jQuery」の2つのデフォルト変数の1つではない)を介して参照されるバージョンのjQuery(簡単に参照できるように1.8)があります。 。これにより、ユーザーは自分のページにjQueryを含めることができ、関数の内部で実行している作業に干渉することはありません。

つまり、jQueryがすでに含まれている1つのページ(1.4)があり、ユーザーと私のコードの両方が要素の「クリック」イベントをリッスンしていて、それらが最初に実行されることを除いて、すべてが正常に機能します。 falseを返すと、jQueryは伝播を停止し、イベントがトリガーされることはありません。私は自分のイベントを最初に行う必要があります。onClickユーザーは私の機能がまだ機能することを期待しています。

これで、jQueryはオブジェクトを介して内部でイベントの独自の順序を維持することがわかりました。これにより、_data()既存のイベントのバインドを解除し、イベントをバインドしてから、既存のイベントを再バインドできますが、これはjQueryのそのインスタンスを介してバインドされたオブジェクトにのみ適用されます。競合がユーザー自身のバージョンのjQueryによって引き起こされたことを期待して、jQueryオブジェクトを盲目的に探すだけではありません。結局のところ、ユーザーがjQueryを介さずにイベントをバインドするとどうなりますか?ページ内の既存のjQueryオブジェクトを操作しようとするのは良い解決策ではありません。

ブラウザによっては、addEventListener/removeEventListenerまたはattachEvent/を使用していることを私は知っていdetachEventます。すでに追加されているイベントのリストを取得できれば、希望の順序で再バインドできますが、その方法がわかりません。ChromeでDOMを調べてみると、onclickどこにもバインドされていないことがわかります(オブジェクトでも、ウィンドウでもドキュメントでもありません)。

私は、jQueryがリスニングをバインドする場所を正確に把握しようと非常に時間を費やしています。独自のイベントの順序を制御できるようにするには、jQueryはどこかで包括的にリッスンしてから、独自の関数を起動する必要がありますか?それがどこで行われているのかを理解できれば、自分のイベントが常に最初になるようにする方法についての洞察を得ることができます。あるいは、Googleで見つけられなかったJavascriptAPIがあるかもしれません。

助言がありますか?

4

7 に答える 7

24

イベントチェーンの先頭にイベントを挿入する小さなjQuery拡張機能を追加するだけで、これを解決しました。

$.fn.bindFirst = function(name, fn) {
  var elem, handlers, i, _len;
  this.bind(name, fn);
  for (i = 0, _len = this.length; i < _len; i++) {
    elem = this[i];
    handlers = jQuery._data(elem).events[name.split('.')[0]];
    handlers.unshift(handlers.pop());
  }
};

次に、イベントをバインドするには:

$(".foo").bindFirst("click", function() { /* Your handler */ });

簡単ピーシー!

于 2012-12-20T20:50:22.833 に答える
13

Bergi と Chris Heald がコメントで述べたように、DOM から既存のイベントを取得する方法はなく、イベントを「最初に」挿入する方法もありません。これらは、設計上挿入された順序で起動され、設計上非表示になります。いくつかの投稿者が言及したように、jQuery のデータを介して使用している jQuery の同じインスタンスを介して追加されたものにアクセスできますが、それだけです。

コードが実行される前にバインドされたイベントの前に実行できる別のケースが 1 つあります。それは、"onclick" HTML 属性を使用した場合です。その場合、ラッパー関数を書くことができます。以下のかなり過激なトーンのコメントで指摘する必要はありません。これは私が尋ねた元の質問のインスタンスでは役に立ちませんが、イベントがこのようにバインドされることは非常にまれですが (現在、ほとんどの人やフレームワークは addEvent または attachEventListener を使用しています)、解決できるシナリオの 1 つです。多くの人がこの質問にアクセスして答えを探しているので、答えが完全であることを確認したいと思いました.

于 2012-12-28T15:26:01.590 に答える
3

event.stopImmediatePropagation()要素を使用するライブラリを Web サイトに含めるように依頼されたという反対の状況に遭遇しました。そのため、一部のイベント ハンドラーがスキップされます。これが私がすることです(ここで答えたように):

<span onclick="yourEventHandler(event)">Button</span>

警告: これは、イベントをバインドするための推奨される方法ではありません。他の開発者がこのためにあなたを殺してしまう可能性があります。

于 2016-02-18T04:05:25.263 に答える
2

適切な解決策ではありませんが...キャプチャフェーズで親ノードにイベントハンドラーを追加できます。ターゲット要素自体ではありません!

<div>
    <div id="target"></div>
</div>

target.parentNode.addEventListener('click',()=>{console.log('parent capture phase handler')},true)

addEventListener の 3 番目の引数は、次のことを意味します。

  • true - キャプチャ フェーズ
  • false - バブル フェーズ

役立つリンク: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener

https://javascript.info/bubbling-and-capturing

于 2021-03-15T08:30:55.887 に答える
1

addListenerandremoveListenerメソッドを追加するのが最も簡単であることがわかりましたdocument(それは私がそれらを必要とする場所だけなので、代わりにElement.prototypeandを使用できると思いますthis)。タイプごとに 1 つの「実際の」リスナーのみが追加されます。実際のリスナーを順番に呼び出すのは単なる機能です。eventListenersディクショナリがドキュメントに追加されます (そのため、ハンドラーまたは順序が混乱する可能性があります)。

[編集] ほとんどの場合の正解は、 https://stackoverflow.com/a/29923421 の 3 番目の引数を使用することだとaddEventListener思います。以下の回答では、(意図的に) 引数を無視しています。

[編集]追加のプロパティを1 つだけ追加するようにコードを更新しました: document.eventHandlers+ 命名の変更。

// Storage.
document.eventListeners             = {};   // { type: [ handlerFunc, listenerFuncs ] }

// Add event listener - returns index.
document.addListener                = (type, listener, atIndex) => {
    // Get info.
    const listening                 = document.eventListeners[type];
    // Add to existing.
    if (listening) {
        // Clean up.
        atIndex                     = atIndex || 0;
        const listeners             = listening[1];     // Array of funcs.
        // Already has.
        const iExists               = listeners.indexOf(listener);
        if (iExists !== -1) {
            // Nothing to do.
            if (iExists === atIndex)
                return              atIndex;
            // Remove from old position.
            listeners.splice(atIndex, 1);
        }
        // Add (supporting one cycle of negatives).
        const nListeners            = listeners.length;
        if (atIndex > nListeners)
            atIndex                 = nListeners;
        else if (atIndex < 0)
            atIndex                 = Math.max(0, atIndex + nListeners + 1);
        listeners.splice(atIndex, 0, listener);
    }
    // New one.
    else {
        // Handler func.
        const handler               = (...args) => {
            const listening         = document.eventListeners[type];
            if (listening) {
                const listeners     = listening[1];     // Array of funcs.
                for (const listener of listeners)
                    listener(...args);
            }
        };
        // Update dictionary.
        document.eventListeners[type]   = [ handler, [ listener ] ];
        // Add listener.
        document.addEventListener(type, handler);
        // First one.
        atIndex                     = 0;
    }
    // Return index.
    return                          atIndex;
};

// Remove event listener - returns index (-1 if not found).
document.removeListener             = (type, listener) => {
    // Get info.
    const listening                 = document.eventListeners[type];
    if (!listening)
        return                      -1;
    // Check if exists.
    const listeners                 = listening[1];
    const iExists                   = listeners.indexOf(listener);
    if (iExists !== -1) {
        // Remove listener.
        listeners.splice(iExists, 1);
        // If last one.
        if (!listeners.length) {
            // Remove listener.
            const handlerFunc       = listening[0];
            document.removeEventListener(type, handlerFunc);
            // Update dictionary.
            delete                  document.eventListeners[type];
        }
    }
    // Return index.
    return                          iExists;
}
于 2020-11-16T22:30:29.483 に答える
0

言われている通り、これらの関数のネイティブ実装をオーバーライドすれば、これは可能かもしれないと思います。これは悪い習慣です。他のライブラリと簡単に競合する可能性があるため、ネイティブ実装を変更するライブラリを開発するときの非常に悪い習慣です。

ただし、完全を期すために、ここに 1 つの可能性を示します (完全にテストされておらず、一般的な概念を示しているだけです)。

// override createElement()
var temp = document.createElement;
document.createElement = function() {
    // create element
    var el = document.createElement.original.apply(document, arguments);

    // override addEventListener()
    el.addEventListenerOriginal = el.addEventListener;
    el._my_stored_events = [];

    // add custom functions
    el.addEventListener = addEventListenerCustom;
    el.addEventListenerFirst = addEventListenerFirst;
    // ...
};
document.createElement.original = temp;

// define main event listeners
function myMainEventListeners(type) {
    if (myMainEventListeners.all[type] === undefined) {
        myMainEventListeners.all[type] = function() {
            for (var i = 0; i < this._my_stored_events.length; i++) {
                var event = this._my_stored_events[i];
                if (event.type == type) {
                    event.listener.apply(this, arguments);
                }
            }
        }
    }
    return myMainEventListeners.all[type];
}
myMainEventListeners.all = {};

// define functions to mess with the event list
function addEventListenerCustom(type, listener, useCapture, wantsUntrusted) {
    // register handler in personal storage list
    this._my_stored_events.push({
        'type' : type,
        'listener' : listener
    });

    // register custom event handler
    if (this.type === undefined) {
        this.type = myMainEventListeners(type);
    }
}

function addEventListenerFirst(type, listener) {
    // register handler in personal storage list
    this._my_stored_events.push({
        'type' : type,
        'listener' : listener
    });

    // register custom event handler
    if (this.type === undefined) {
        this.type = myMainEventListeners(type);
    }
}

// ...

これを真にロックダウンするには、この点に関してさらに多くの作業を行う必要があります。繰り返しますが、ネイティブ ライブラリを変更しないことが最善です。しかし、このような問題を解決する際に JavaScript が提供する柔軟性を示すのに役立つ、頭の体操として役立ちます。

于 2015-04-06T22:25:20.107 に答える