Add listener to dynamic html elements I have a field type that I need to initialize. When the page loads I can use code like the following to handle certain behaviors of the field:

$('.myfieldwrapper').each(function() {
  new Craft.MyField(this);
});


Within the Craft.MyField object, I add a couple listeners like so:

this.addListener(this.$element, 'change', 'handleElementChange');
this.addListener(this.$element2, 'click', 'handleElement2Click');


That all works fine.  However, this field doesn't just get added to the page on page load. It exists in the row of a table, and a user can add additional rows to the table which add a new row and field to the DOM.

I've tried to instantiate the newly added field in several ways, and I have been unable to get it right yet. The Craft.MyField seems to be initializing, and I appear to be able to re-initialize the default fields on the page, but my attempts do not have any affect on the dynamic fields that get added.

I don't think the following is the best way to do this, but it's currently the closest I've come and is the simplest example code I can share reflecting my attempts. 

After a new field gets added dynamically, I trigger an event:

$('#tableId').trigger('rowAddOrRemove');


And I have additional code watching for that event and attempting to re-initialize all the fields on the page:

$('#tableId').on('rowAddOrRemove', function(event){
  $('.myfieldwrapper').each(function() {
    new Craft.MyField(this);
  });
});


When I do this the initial field on the page still works but none of the dynamically added fields behave as I need them too.

Any ideas on what I'm doing wrong or what concepts I need to understand better to get this right?
You might be looking at a simple race conditions issue, i.e. that the rowAddOrRemove event is triggering before the new .myfieldwrapper elements have been attached to the DOM. To make sure that you don't call new Craft.MyField too early, you can try to wrap the constructor call in an anonymous requestAnimationFrame handler:

$('#tableId').on('rowAddOrRemove', function(event){
    Garnish.requestAnimationFrame(function () {
        $('.myfieldwrapper').each(function() {
            new Craft.MyField(this);
        });
    });
});


Not sure if race conditions is even the issue here, but it's the only thing I can think of, based on the code you provided.

Some other thoughts:

I'm not sure you actually need the rowAddOrRemove event – unless other parts of your application also needs to respond to that event, I'd opt for calling a simple function, rather than triggering a jQuery event, e.g.:

# ... field gets added dynamically

Garnish.requestAnimationFrame(initFields); // Instead of $('#tableId').trigger('rowAddOrRemove');

function initFields()
{
    $('.myfieldwrapper').each(function () {
        new Craft.MyField(this);
    });
}


Sidenote: If you do need to trigger an event, I'd recommend looking at Signals.js, which is a much more robust Event/Promises implementation than $.trigger.

Another thing: I'm not sure if calling Craft.MyField(this) on DOM elements that have previously been initialized is an issue, but if it is, you could add a simple classname to the initialized elements, to prevent them being initialized multiple times:

function initFields()
{
    $('.myfieldwrapper:not(.js-initialized)').each(function () {
        $(this).addClass('js-initialized');
        new Craft.MyField(this);
    });
}


Finally, if you need to destroy instances of Craft.MyField that have previously been initialized but are now removed (to prevent memory leaks or other bugs), you could do something like this (note that this example assumes that your Craft.MyField class has a destroy method that e.g. removes event listeners, deletes variables etc):

var myFieldInstances = [];

function initFields()
{
    // Destroy removed fields
    var currentFieldInstances = myFieldInstances.slice(0);
    myFieldInstances = [];
    for (var i = 0; i &lt; currentFieldInstances.length; ++i) {
        var field = currentFieldInstances[i],
            $el = field.$el;
        if (!$el.closest('#tableId').length) {
            // This field's DOM element is no longer a child of #tableId – kill it with fire
            field.destroy();
        } else {
            myFieldInstances.push(field);
        }
    }
    // Init new fields
    $('.myfieldwrapper:not(.js-initialized)').each(function () {
        $(this).addClass('js-initialized');
        myFieldInstances.push(new Craft.MyField(this));
    });
}