jQuery.on()如何工作?

我还没有看到这个函数的来源,但我想知道,它是否像这样工作:

  1. 通过其选择器选择元素
  2. 将提供的事件处理程序委托给他们
  3. 在该选择器上运行setInterval并不断取消委派,然后重新委派相同的事件

或者对此有一个纯JavaScript DOM解释?

我假设你的问题是关于.on的事件委托版本。

在JavaScript中,大多数事件在DOM层次结构中冒出来。 这意味着,当一个可以为一个元素触发的事件触发时,它将冒泡到DOM直到document级别。

考虑这个基本标记:

 
1 2

现在我们应用事件委托:

 $('div').on('click', 'span', fn); 

事件处理程序仅附加到div元素。 由于span位于div内部,因此span中的任何单击都会冒泡到div ,触发div的单击处理程序。 此时,剩下的就是检查event.target (或target和delegateTarget之间的任何元素)是否与委托目标选择器匹配。


让我们更复杂一点:

 
1 2 another nested element inside span

paragraph won't fire delegated handler

基本逻辑如下:

 //attach handler to an ancestor document.getElementById('parent').addEventListener('click', function(e) { //filter out the event target if (e.target.tagName.toLowerCase() === 'span') { console.log('span inside parent clicked'); } }); 

虽然event.target嵌套在您的filter中,但上述内容不匹配。 我们需要一些迭代逻辑:

 document.getElementById('parent').addEventListener('click', function(e) { var failsFilter = true, el = e.target; while (el !== this && (failsFilter = el.tagName.toLowerCase() !== 'span') && (el = el.parentNode)); if (!failsFilter) { console.log('span inside parent clicked'); } }); 

小提琴

编辑:更新代码以仅匹配jQuery的.on所做的后代元素。

注意:这些片段仅用于说明目的,不在现实世界中使用。 当然,除非你不打算支持旧的IE。 对于旧的IE,您需要针对addEventListener / attachEvent以及event.target || event.srcElement event.target || event.srcElement ,可能还有一些其他的怪癖,比如检查事件对象是传递给处理函数还是在全局范围内可用。 值得庆幸的是,jQuery为我们无缝地完成了所有这些工作。 =]

Necromancing:
以防任何人需要使用Vanilla-JavaScript替换JQuery on / live:

打字稿:

 /// attach an event handler, now or in the future, /// for all elements which match childselector, /// within the child tree of the element maching parentSelector. export function subscribeEvent(parentSelector: string | Element , eventName: string , childSelector: string , eventCallback) { if (parentSelector == null) throw new ReferenceError("Parameter parentSelector is NULL"); if (childSelector == null) throw new ReferenceError("Parameter childSelector is NULL"); // nodeToObserve: the node that will be observed for mutations let nodeToObserve: Element = parentSelector; if (typeof (parentSelector) === 'string') nodeToObserve = document.querySelector(parentSelector); let eligibleChildren: NodeListOf = nodeToObserve.querySelectorAll(childSelector); for (let i = 0; i < eligibleChildren.length; ++i) { eligibleChildren[i].addEventListener(eventName, eventCallback, false); } // Next i // https://stackoverflow.com/questions/2712136/how-do-i-make-this-loop-all-children-recursively function allDescendants(node: Node) { if (node == null) return; for (let i = 0; i < node.childNodes.length; i++) { let child = node.childNodes[i]; allDescendants(child); } // Next i // IE 11 Polyfill if (!Element.prototype.matches) Element.prototype.matches = Element.prototype.msMatchesSelector; if ((node).matches) { if ((node).matches(childSelector)) { // console.log("match"); node.addEventListener(eventName, eventCallback, false); } // End if ((node).matches(childSelector)) // else console.log("no match"); } // End if ((node).matches) // else console.log("no matchfunction"); } // End Function allDescendants // Callback function to execute when mutations are observed let callback:MutationCallback = function (mutationsList: MutationRecord[], observer: MutationObserver) { for (let mutation of mutationsList) { // console.log("mutation.type", mutation.type); // console.log("mutation", mutation); if (mutation.type == 'childList') { for (let i = 0; i < mutation.addedNodes.length; ++i) { let thisNode: Node = mutation.addedNodes[i]; allDescendants(thisNode); } // Next i } // End if (mutation.type == 'childList') // else if (mutation.type == 'attributes') { console.log('The ' + mutation.attributeName + ' attribute was modified.'); } // Next mutation }; // End Function callback // Options for the observer (which mutations to observe) let config = { attributes: false, childList: true, subtree: true }; // Create an observer instance linked to the callback function let observer = new MutationObserver(callback); // Start observing the target node for configured mutations observer.observe(nodeToObserve, config); } // End Function subscribeEvent 

JavaScript的:

  /// attach an event handler, now or in the future, /// for all elements which match childselector, /// within the child tree of the element maching parentSelector. function subscribeEvent(parentSelector, eventName, childSelector, eventCallback) { if (parentSelector == null) throw new ReferenceError("Parameter parentSelector is NULL"); if (childSelector == null) throw new ReferenceError("Parameter childSelector is NULL"); // nodeToObserve: the node that will be observed for mutations var nodeToObserve = parentSelector; if (typeof (parentSelector) === 'string') nodeToObserve = document.querySelector(parentSelector); var eligibleChildren = nodeToObserve.querySelectorAll(childSelector); for (var i = 0; i < eligibleChildren.length; ++i) { eligibleChildren[i].addEventListener(eventName, eventCallback, false); } // Next i // https://stackoverflow.com/questions/2712136/how-do-i-make-this-loop-all-children-recursively function allDescendants(node) { if (node == null) return; for (var i = 0; i < node.childNodes.length; i++) { var child = node.childNodes[i]; allDescendants(child); } // Next i // IE 11 Polyfill if (!Element.prototype.matches) Element.prototype.matches = Element.prototype.msMatchesSelector; if (node.matches) { if (node.matches(childSelector)) { // console.log("match"); node.addEventListener(eventName, eventCallback, false); } // End if ((node).matches(childSelector)) // else console.log("no match"); } // End if ((node).matches) // else console.log("no matchfunction"); } // End Function allDescendants // Callback function to execute when mutations are observed var callback = function (mutationsList, observer) { for (var _i = 0, mutationsList_1 = mutationsList; _i < mutationsList_1.length; _i++) { var mutation = mutationsList_1[_i]; // console.log("mutation.type", mutation.type); // console.log("mutation", mutation); if (mutation.type == 'childList') { for (var i = 0; i < mutation.addedNodes.length; ++i) { var thisNode = mutation.addedNodes[i]; allDescendants(thisNode); } // Next i } // End if (mutation.type == 'childList') // else if (mutation.type == 'attributes') { console.log('The ' + mutation.attributeName + ' attribute was modified.'); } // Next mutation }; // End Function callback // Options for the observer (which mutations to observe) var config = { attributes: false, childList: true, subtree: true }; // Create an observer instance linked to the callback function var observer = new MutationObserver(callback); // Start observing the target node for configured mutations observer.observe(nodeToObserve, config); } // End Function subscribeEvent