当我尝试学习jquery时,我了解到$(selector)返回的对象具有该选择器的所有匹配项,并且像数组一样可迭代。例如,$("button")
将返回一个对象,该对象可以访问 DOM 的所有按钮标签,以便访问第一个按钮标签,您可以第二次使用$["button"][0]
使用$["button"][1]
,依此类推。
所以下面的代码集中在注释的第1行和第2行。
<body>
<button>Click me</button>
<script>
$(document).ready(function() {
// line 1
$("button").click(function() {
console.log("1");
// line 2
$("button").click();
});
});
</script>
</body>
第1行的事件处理函数中的第2行设置了一个无限循环,如您所见,当我单击“单击我”按钮时,它将触发第1行,其中第2行也将触发第1行,依此类推。 现在,请参阅下面的代码片段,其中第2行已更改。
<script>
$(document).ready(function() {
// line 1
$("button").click(function() {
console.log("1");
// line 2
$("button")[0].click();
});
});
</script>
这一次不是设置无限循环,而是为什么两次打印到控制台“ 1”?
答案 0 :(得分:6)
很可能是JQuery调用综合事件的方式。
这是用于事件分发的JQuery代码段。
dispatch: function( nativeEvent ) {
// Make a writable jQuery.Event from the native event object
var event = jQuery.event.fix( nativeEvent );
var i, j, ret, matched, handleObj, handlerQueue,
args = new Array( arguments.length ),
handlers = ( dataPriv.get( this, "events" ) || {} )[ event.type ] || [],
special = jQuery.event.special[ event.type ] || {};
// Use the fix-ed jQuery.Event rather than the (read-only) native event
args[ 0 ] = event;
for ( i = 1; i < arguments.length; i++ ) {
args[ i ] = arguments[ i ];
}
event.delegateTarget = this;
// Call the preDispatch hook for the mapped type, and let it bail if desired
if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
return;
}
// Determine handlers
handlerQueue = jQuery.event.handlers.call( this, event, handlers );
// Run delegates first; they may want to stop propagation beneath us
i = 0;
while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) {
event.currentTarget = matched.elem;
j = 0;
while ( ( handleObj = matched.handlers[ j++ ] ) &&
!event.isImmediatePropagationStopped() ) {
// If the event is namespaced, then each handler is only invoked if it is
// specially universal or its namespaces are a superset of the event's.
if ( !event.rnamespace || handleObj.namespace === false ||
event.rnamespace.test( handleObj.namespace ) ) {
event.handleObj = handleObj;
event.data = handleObj.data;
ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle ||
handleObj.handler ).apply( matched.elem, args );
if ( ret !== undefined ) {
if ( ( event.result = ret ) === false ) {
event.preventDefault();
event.stopPropagation();
}
}
}
}
}
// Call the postDispatch hook for the mapped type
if ( special.postDispatch ) {
special.postDispatch.call( this, event );
}
return event.result;
}
读取spec上的合成和真实点击事件:
当用户代理要在元素上运行单击后激活步骤时,它必须运行为该元素定义的激活行为(如果有)。激活行为可以指的是到目前为止导致上述步骤触发的点击事件。
由于JQuery实现了用于模拟点击事件的自定义分派处理程序,因此它们似乎添加了以下内容来模拟上述的本机html事件生命周期。
if ( special.postDispatch ) {
special.postDispatch.call( this, event );
}
在您查看以下控制台输出时,此处的调用很可能是可疑的。请注意,您永远不会看到console.log完成,因为调度处理程序实际上从不返回事件来完成其本机生命周期(无限递归),我怀疑本机html实现可以更好地处理该事件。
无限循环警告
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.0/jquery.min.js"></script>
<body>
<button>Click me</button>
<script>
$(document).ready(function() {
// line 1
$("button").click(function() {
console.log("start");
// line 2
$("button").click();
console.log("Done");
});
});
</script>
</body>
好发现!
答案 1 :(得分:1)
当您执行$(“ button”)[0]时,您将获得不是jQuery对象的基础本机HTMLButtonElement对象。
答案 2 :(得分:0)
$('button').click(fn)
做下面的事情
$("button").each(function() {
//this === native button htmlElement.
if (!this._event) this._event = {};
if (!this._event.click) this._event.click = [];
this._event.click.push(fn);
if (!this.clickHandler) {
this.clickHandler = e => {
this._event.click.forEach(f => f.bind(this)(e));
};
this.addEventListener("click", this.clickHandler);
}
});
$('button').click()
表示
$("button").each(function() {
//this === native button htmlElement.
if(this.clickHandler)this.clickHandler();
if(typeof this.onclick === 'function') this.onclick();
});
这只是一个例子,jquery事件的源代码要复杂得多。
为什么$("button")[0].click()
只打印'1'两次?
原生click
模拟了鼠标在按钮元素上的点击,因此出于安全原因,我认为该循环已被Explorer阻止。
答案 3 :(得分:0)
$(“ button”)返回一个jq对象,它调用jq对象上的方法,而$(“ button”)[0]返回一个DOM对象,它调用dom本机方法