As of jQuery 1.7 you should use jQuery.fn.on
with the selector parameter filled:
$(staticAncestors).on(eventName, dynamicChild, function() {});
Explanation:
This is called event delegation and works as followed. The event is attached to a static parent (staticAncestors
) of the element that should be handled. This jQuery handler is triggered every time the event triggers on this element or one of the descendant elements. The handler then checks if the element that triggered the event matches your selector (dynamicChild
). When there is a match then your custom handler function is executed.
Prior to this, the recommended approach was to use live()
:
$(selector).live( eventName, function(){} );
However, live()
was deprecated in 1.7 in favour of on()
, and completely removed in 1.9. The live()
signature:
$(selector).live( eventName, function(){} );
... can be replaced with the following on()
signature:
$(document).on( eventName, selector, function(){} );
For example, if your page was dynamically creating elements with the class name dosomething
you would bind the event to a parent which already exists (this is the nub of the problem here, you need something that exists to bind to, don't bind to the dynamic content), this can be (and the easiest option) is document
. Though bear in mind document
may not be the most efficient option.
$(document).on('mouseover mouseout', '.dosomething', function(){
// what you want to happen when mouseover and mouseout
// occurs on elements that match '.dosomething'
});
Any parent that exists at the time the event is bound is fine. For example
$('.buttons').on('click', 'button', function(){
// do something here
});
would apply to
<div class="buttons">
<!-- <button>s that are generated dynamically and added here -->
</div>
Think of it like you're just calling JavaScript functions. You can't use a for
loop where the arguments to a function call would go:
return tbody(
for (var i = 0; i < numrows; i++) {
ObjectRow()
}
)
See how the function tbody
is being passed a for
loop as an argument – leading to a syntax error.
But you can make an array, and then pass that in as an argument:
var rows = [];
for (var i = 0; i < numrows; i++) {
rows.push(ObjectRow());
}
return tbody(rows);
You can basically use the same structure when working with JSX:
var rows = [];
for (var i = 0; i < numrows; i++) {
// note: we are adding a key prop here to allow react to uniquely identify each
// element in this array. see: https://reactjs.org/docs/lists-and-keys.html
rows.push(<ObjectRow key={i} />);
}
return <tbody>{rows}</tbody>;
Incidentally, my JavaScript example is almost exactly what that example of JSX transforms into. Play around with Babel REPL to get a feel for how JSX works.
Best Answer
Applying handlers to elements in function components should generally just look like this:
If you need to do anything much more complex it's a sign that either a) the component shouldn't be stateless (use a class, or hooks), or b) you should be creating the handler in an outer stateful container component.
As an aside, and undermining my first point slightly, unless the component is in a particularly intensively re-rendered part of the app there's no need to worry about creating arrow functions in
render()
.