Search code examples
javascriptdomdom-eventsonchangeunobtrusive-javascript

Can I catch the change event from a dynamically created HTML input element farther up the DOM?


I have a <select> form element that is being generated dynamically after the page loads. I was using jQuery to attach a change event that would update other fields in the form, and wanted to eliminate the need to rewire the handler each time the form containing the <select> was replaced.

For click events on dynamic elements, I have been moving to unobtrusive JavaScript and wanted to use that approach for change - but no success. Attaching to the <select>directly was cumbersome because when the node is replaced in the DOM, the connected event listener dies with it, and the new node must be attached to again.

So I wanted to know, is there a way to unobtrusively assign a change event handler to the parent element of a newly-created <select> and let the event bubble up to a handler on a previously-existing element, in order to avoid attaching a handler each time a new target input element is created?


Solution

  • Yes!

    $('#StaticParentNode').on('change',"select.qualifiers", function(){ alert("Change detected from parent"); });

    Naturally, I finally got this working 2 minutes after posting my original answer (below). Doh.


    No.

    While attaching unobtrusive event listeners on a parent <div> or the document works great for bubbling click events from elements that didn't previously exist on the page, you can only attach an event listener to an element that has that type of event.

    As stated in the jQuery .change API docs (although this issue is not jQuery specific):

    The change event is sent to an element when its value changes. This event is limited to <input> elements, <textarea> boxes and <select> elements.`

    Since <div> and document do not have a change event, they cannot intercept one from their children either.

    Solutions:

    1. Just set onchange to a previously-defined function when you generate the element: <select onchange='MyModule.selectChanged()'>[<options>]</select>. Not pure from a separation of concerns standpoint, but a simple approach with minimal extra HTML added to an AJAX request, and no fiddling with handlers after the AJAX call completes.

      or,

    2. (Re)attach an event listener each time you create new elements that require it, either 'manually' (e.g. in a $.ajax success function) or by wiring it into whatever tool is handling your AJAX results (e.g. modifying or extending .prototype). See examples elsewhere on SO.