<p>You <em>might</em> be looking at a simple race conditions issue, i.e. that the <code>rowAddOrRemove</code> event is triggering before the new <code>.myfieldwrapper</code> elements have been attached to the DOM. To make sure that you don't call <code>new Craft.MyField</code> too early, you can try to wrap the constructor call in an anonymous <code>requestAnimationFrame</code> handler:</p>

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

<p>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.</p>

<p>Some other thoughts:</p>

<p>I'm not sure you actually need the <code>rowAddOrRemove</code> 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.:</p>

<pre><code># ... field gets added dynamically

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

function initFields()
{
    $('.myfieldwrapper').each(function () {
        new Craft.MyField(this);
    });
}
</code></pre>

<p>Sidenote: If you <em>do</em> need to trigger an event, I'd recommend looking at <a href="https://github.com/millermedeiros/js-signals" rel="nofollow">Signals.js</a>, which is a much more robust Event/Promises implementation than <code>$.trigger</code>.</p>

<p>Another thing: I'm not sure if calling <code>Craft.MyField(this)</code> 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:</p>

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

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

<pre><code>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));
    });
}
</code></pre>

CC BY-SA 3.0
2016-05-15T14:35:47.740Z
Thanks a lot for this answer, Mats.  The issue turned out to be a mis-spelled classname on the dynamic element, so the code in the question actually turns out to work. Lame. However! You're right that triggering an event here has very little appeal. Your answer helped me understand my options and refactor things to solve the issue more elegantly and get rid of that event entirely, which is the right answer.
Ben Parizek
CC BY-SA 3.0
2016-05-14T09:07:21.270Z
2016-05-14T09:21:10.603Z
2016-05-14T09:21:10.603Z
Mats Mikkel Rummelhoff
<p>In my question above I was chasing a misspelled class name with a limited understanding of how I should be approaching the problem I was asking about. Mats answer points out several considerations on how the problem could be approached differently and how the discussed code can be improved. I'd like to document one more approach to adding a listener which Mats answer helped me come to, and which is similar to how Craft initializes it's own UI elements.</p>

<p>Throughout the Craft javascript, as elements are dynamically added to the page, you will see that many of them also initialize UI elements:</p>

<pre><code>Craft.initUiElements($container);
</code></pre>

<p>The <code>initUiElements</code> method checks the page for a handful of class names that are used in the UI (i.e. <code>.grid</code>, <code>.pane</code>, <code>.fieldToggle</code>, <code>.menubtn</code>, and more). When found, each class calls it's respective initialization method which is a custom jQuery plugin that Craft prepares in <code>craft.js</code>.</p>

<p>I ended up using a similar approach for my situation. It's quite likely my code can still be improved, so take my javascript skills with a grain of salt, but for the sake of documenting multiple approaches, this approach includes:</p>

<h3>Create a custom jQuery plugin</h3>

<p>Using <a href="https://api.jquery.com/jquery.fn.extend/" rel="nofollow">jQuery.fn.extend()</a> we can <em>merge the contents of an object onto the jQuery prototype to provide new jQuery instance methods.</em>:</p>

<pre><code>$.extend($.fn,
{
    myfield: function()
    {
        return this.each(function()
        {
            if (!$.data(this, 'myfield-class'))
            {
                new Craft.MyField(this);
            }
        });
    },
});
</code></pre>

<p>In this example, we are passing two objects to <code>$.extend</code>:</p>

<ul>
<li><code>$.fn</code></li>
<li><code>{ myfield: function() { ... } }</code></li>
</ul>

<p>The result allows us to access our new method directly from the DOM:</p>

<pre><code>$('.myfield', $container).myfield();
</code></pre>

<p>Passing the second argument <code>$container</code> to the jQuery method allows us to restrict the behavior of our custom method to a specific context, such as elements within the container of whatever we are initializing (instead of the whole page).</p>

<h3>Create a way to initialize your elements</h3>

<p>Using <a href="https://api.jquery.com/jquery.extend/" rel="nofollow">jQuery.extend</a> we can <em>merge the contents of two or more objects together into the first object.</em>:</p>

<pre><code>$.extend(Craft.MyField,
{
    initFields: function($container)
    {
        $('.myfield', $container).myfield();
    }
});
</code></pre>

<p>In this example, we are passing two objects to <code>$.extend</code>:</p>

<ul>
<li><code>Craft.MyField</code></li>
<li><code>{ initFields: function($container) { ... } }</code></li>
</ul>

<p>The result is that we can now use the <code>initField</code> method in the second object on the first object: </p>

<pre><code>Craft.MyField.initField($container);
</code></pre>

<h3>Initialize your elements when the appropriate action is triggered</h3>

<p>With the above items in place, now, each time a specific action happens we can initialize our fields:</p>

<pre><code>Craft.MyField.initFields($container);
</code></pre>

<p>In my case, the field I needed to initialize was within a table row. So, each time new table row is added to the page, I can now initialize the fields within that row, treating the row as the container:</p>

<pre><code>addRow: function()
{
  // Do stuff that adds the table row and fields

  // Initialize the fields
  Craft.MyField.initFields($tr);
}
</code></pre>

<hr>

CC BY-SA 3.0
2016-05-15T15:45:48.037Z
2016-05-15T16:29:23.880Z
2016-05-15T16:29:23.880Z
Ben Parizek
<p>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:</p>

<pre><code>$('.myfieldwrapper').each(function() {
  new Craft.MyField(this);
});
</code></pre>

<p>Within the Craft.MyField object, I add a couple listeners like so:</p>

<pre><code>this.addListener(this.$element, 'change', 'handleElementChange');
this.addListener(this.$element2, 'click', 'handleElement2Click');
</code></pre>

<p>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.</p>

<p>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.</p>

<p>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. </p>

<p>After a new field gets added dynamically, I trigger an event:</p>

<pre><code>$('#tableId').trigger('rowAddOrRemove');
</code></pre>

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

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

<p>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.</p>

<p>Any ideas on what I'm doing wrong or what concepts I need to understand better to get this right?</p>

<javascript>
Add listener to dynamic html elements
# Add listener to dynamic html elements

(Asked by: username_0 on 2016-05-14)

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?

**Tags:** javascript

---

## 2 answers

### Answer by username_1 on 2016-05-14. Score: 2 | Accepted answer

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 < 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));
    });
}

> **Comments:**
> **username_0**: Thanks a lot for this answer, Mats.  The issue turned out to be a mis-spelled classname on the dynamic element, so the code in the question actually turns out to work. Lame. However! You're right that triggering an event here has very little appeal. Your answer helped me understand my options and refactor things to solve the issue more elegantly and get rid of that event entirely, which is the right answer.  

---

### Answer by username_0 on 2016-05-15. Score: 2

In my question above I was chasing a misspelled class name with a limited understanding of how I should be approaching the problem I was asking about. Mats answer points out several considerations on how the problem could be approached differently and how the discussed code can be improved. I'd like to document one more approach to adding a listener which Mats answer helped me come to, and which is similar to how Craft initializes it's own UI elements.
Throughout the Craft javascript, as elements are dynamically added to the page, you will see that many of them also initialize UI elements:
Craft.initUiElements($container);

The initUiElements method checks the page for a handful of class names that are used in the UI (i.e. .grid, .pane, .fieldToggle, .menubtn, and more). When found, each class calls it's respective initialization method which is a custom jQuery plugin that Craft prepares in craft.js.
I ended up using a similar approach for my situation. It's quite likely my code can still be improved, so take my javascript skills with a grain of salt, but for the sake of documenting multiple approaches, this approach includes:
Create a custom jQuery plugin
Using jQuery.fn.extend() we can merge the contents of an object onto the jQuery prototype to provide new jQuery instance methods.:
$.extend($.fn,
{
    myfield: function()
    {
        return this.each(function()
        {
            if (!$.data(this, 'myfield-class'))
            {
                new Craft.MyField(this);
            }
        });
    },
});

In this example, we are passing two objects to $.extend:

$.fn
{ myfield: function() { ... } }

The result allows us to access our new method directly from the DOM:
$('.myfield', $container).myfield();

Passing the second argument $container to the jQuery method allows us to restrict the behavior of our custom method to a specific context, such as elements within the container of whatever we are initializing (instead of the whole page).
Create a way to initialize your elements
Using jQuery.extend we can merge the contents of two or more objects together into the first object.:
$.extend(Craft.MyField,
{
    initFields: function($container)
    {
        $('.myfield', $container).myfield();
    }
});

In this example, we are passing two objects to $.extend:

Craft.MyField
{ initFields: function($container) { ... } }

The result is that we can now use the initField method in the second object on the first object: 
Craft.MyField.initField($container);

Initialize your elements when the appropriate action is triggered
With the above items in place, now, each time a specific action happens we can initialize our fields:
Craft.MyField.initFields($container);

In my case, the field I needed to initialize was within a table row. So, each time new table row is added to the page, I can now initialize the fields within that row, treating the row as the container:
addRow: function()
{
  // Do stuff that adds the table row and fields

  // Initialize the fields
  Craft.MyField.initFields($tr);
}


---

