
Security News
Oracle Drags Its Feet in the JavaScript Trademark Dispute
Oracle seeks to dismiss fraud claims in the JavaScript trademark dispute, delaying the case and avoiding questions about its right to the name.
jquery-events-to-dom-events
Advanced tools
Listen for jQuery "events" with vanilla JS
$(document).trigger('fart') emits a $fart DOM CustomEvent
disconnect()
window.$
to avoid a jQuery fixationYou can try it now on CodePen or even better, clone a sample Rails project to experiment in a mutation-first context with Stimulus.
The Rails project is called jboo. Don't read into the name.
First, the right music is important for establishing proper context.
You don't have to listen to music, but your transpiler configuration will almost certainly fail lint checks if you are not listening to "In Harmony New Found Freedom" by The Swirlies, from their 1996 album "They Spent Their Wild Youthful Days In The Glittering World Of The Salons" while you integrate this library.
Next, make sure that you've loaded jQuery and this library into your project.
yarn install jquery jquery-events-to-dom-events
This library assumes that jQuery is available as $
on the global window
object. You can verify this by opening your browser's Console Inspector and typing window.$
. You should see something like:
ƒ jQuery(selector, context)
If you are working in Rails and $
is not available, try modifying your config/webpack/environment.js
like this:
const { environment } = require('@rails/webpacker')
const webpack = require('webpack')
environment.plugins.prepend(
'Provide',
new webpack.ProvidePlugin({
$: 'jquery/src/jquery',
jQuery: 'jquery/src/jquery'
})
)
module.exports = environment
In the most basic configuration, you:
import { delegate } from 'jquery-events-to-dom-events'
delegate(eventName)
for every jQuery event you want to capture.Let's say that you want to respond to the user closing a Bootstrap modal window:
import { delegate } from 'jquery-events-to-dom-events'
delegate('hidden.bs.modal')
document.addEventListener('$hidden.bs.modal', () => console.log('Modal closed!'))
That might be it. Go make a sandwich - you've earned it.
Note: The mechanism this library uses is to capture jQuery events using jQuery event listeners, and then create DOM events that contain all of the same information as the original. There is no way to actually catch jQuery events with a vanilla event handler because the jQuery implementation is proprietary and non-trivial.
Technically, this library repeats events. Quantum entanglement for events? Perfect! Ship it.
Some events, such as the jQuery Ajax callbacks - return with additional parameters attached, and for these exceptions you need to specify a second parameter defining an array of strings representing these parameters. The first element of this array must always be event
.
Event | Parameters |
---|---|
ajax:success | ['event', 'data', 'status', 'xhr'] |
ajax:error | ['event', 'xhr', 'status', 'error'] |
ajax:complete | ['event', 'xhr', 'status'] |
ajax:beforeSend | ['event', 'xhr', 'settings'] |
ajax:send | ['event', 'xhr'] |
ajax:aborted:required | ['event', 'elements'] |
ajax:aborted:file | ['event', 'elements'] |
You can listen for notifications that Ajax requests have completed like so:
import { delegate } from 'jquery-events-to-dom-events'
delegate('ajax:complete', ['event', 'xhr', 'status'])
document.addEventListener('$ajax:complete', () => console.log('Ajax request happened!'))
You can pass parameters from your own jQuery events to DOM events. You just have to give each parameter a name, and those parameters will be processed in order. Named parameters are accessible through the detail
object of the event.
import { delegate } from 'jquery-events-to-dom-events'
delegate('birthday', ['event', 'beast'])
document.addEventListener('$birthday', event => console.log('birthday received as $birthday from DOM', event.detail.beast))
window.$(document).trigger('birthday', 666)
You've heard the fuss. Now it's time to get real about making your code idempotent. If you take pride in the quality of the code you write, Stimulus makes it easy to structure your logic so that it automatically works with Turbolinks and doesn't leak memory when you morph DOM elements out of existence that still have event listeners attached.
Let's start with an HTML fragment that attaches a Stimulus controller called delegate
to a DIV:
<div data-controller="jquery-to-dom">
<button data-action="jquery-to-dom#trigger">Trigger jQuery event</button>
</div>
That Stimulus controller imports a second function called abnegate
, which releases your delegated events while your component teardown happens:
import { Controller } from 'stimulus'
import { delegate, abnegate } from 'jquery-events-to-dom-events'
const eventHandler = () => console.log('jquery received as $jquery from DOM')
export default class extends Controller {
connect () {
this.delegate = delegate('jquery')
document.addEventListener('$jquery', eventHandler)
}
disconnect () {
abnegate('jquery', this.delegate)
document.removeEventListener('$jquery', eventHandler)
}
trigger () {
window.$(document).trigger('jquery')
}
}
We use Stimulus to wire the click event of the button to call the triggerjQ
method of the delegate
controller. You can also call $(document).trigger('test')
from your Console Inspector without clicking the button.
The important takeaway is that the delegate
function returns the jQuery event handler, which can be stored as a property of the controller instance. This handler then gets passed back to the abnegate
function so that jQuery can release its own event listener on elements that might soon be removed from the DOM.
It's only by strictly adhering to good habits around attaching listeners during connect()
and removing them during disconnect()
that we can be confident we're releasing references properly. This convention helps us eliminate weird glitches and side-effects that come from blending legacy jQuery components with Turbolinks. They were written for a time when there was a single page load event, and clicks triggered page refresh operations.
Remember: if you define event handlers with anonymous functions passed to a listener, you can't remove them later. Only you can prevent forest fires.
It's important to strike a balance between being opinionated and imposing ideological limitations. While this library is definitely intended to act as a bridge to help jQuery developers move to using vanilla JS, at some point I realized that if I don't make it easy to send DOM events into jQuery as well, people will choose a library that does.
To capture DOM events inside of your jQuery code, you essentially want to invert all previous instructions. The delegate and abnegate functions accept event names that start with a $
character, and that tells the library to listen for DOM events and fire them as jQuery events.
import { delegate } from 'jquery-events-to-dom-events'
const eventHandler = (_, detail) => console.log('$wedding received as wedding by jQuery', detail)
this.delegate = delegate('$wedding')
document.dispatchEvent(new CustomEvent('$wedding', { detail: 666 }))
While the syntax is quite similar, there is a significant difference in the way events are passed into a jQuery event. The CustomEvent constructor can take an object as an optional second parameter, and the key in that object must be detail
. Interestingly, the value of detail
can be just about anything - such as 666 above - but most frequently, it's an object with key/value pairs in it.
Bug reports and pull requests are welcome.
This package is available as open source under the terms of the MIT License.
FAQs
Capture jQuery 'events' with DOM event listeners
We found that jquery-events-to-dom-events demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Oracle seeks to dismiss fraud claims in the JavaScript trademark dispute, delaying the case and avoiding questions about its right to the name.
Security News
The Linux Foundation is warning open source developers that compliance with global sanctions is mandatory, highlighting legal risks and restrictions on contributions.
Security News
Maven Central now validates Sigstore signatures, making it easier for developers to verify the provenance of Java packages.