I am having a problem understanding the different behavior of $("button").click() and $("button")[0].click()
It is most likely the way in which the synthetic event is being invoked by JQuery.
Here is the JQuery snippet for event dispatch.
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;
}
Reading the spec for synthetic and authentic click events:
When a user agent is to run post-click activation steps on an element, it must run the activation behavior defined for that element, if any. Activation behaviors can refer to the click event that was fired by the steps above leading up to this point.
As JQuery has implemented a custom dispatch handler for emulating click events it looks like they have added the following in order to emulate that aforementioned native html event lifecycle.
if ( special.postDispatch ) {
special.postDispatch.call( this, event );
}
This call here is likely the suspect, proven when you look at the console output for the following. Note that you never see the console.log Done because the dispatch handler is never actually returning the event to complete it's native lifecycle (endless recursion), which I suspect is just frankly handled better by the native html implementation.
Infinite loop warning
<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>
Good find!