Answers.Body: <p>I think there's a few ways to skin this cat, but <a href="https://craftcms.stackexchange.com/a/11656/1098">the most robust way</a> would probably be to listen to the the <code>selectElements</code> event for the <a href="https://github.com/pixelandtonic/Craft-Release/blob/master/app/resources/js/craft.js#L3660" rel="nofollow noreferrer"><code>BaseElementSelectInput</code></a> instances bound to each field's container element (the instance will be bound to the <code>&lt;div class="elementselect"&gt;</code> node, except for Categories fields where it'll be bound to the <code>&lt;div class="categoriesfield"&gt;</code> node, for some reason) and can be accessed by calling <code>$('.elementselect').data('elementSelect')</code>.</p>
  
  <p>You can use the <code>.on()</code> method to listen to the actual events, here's one way to do it:</p>
  
  <pre><code>$('.elementselect, .categoriesfield').each(function () {
      $(this).data('elementSelect').on('selectElements', onSelectElements);
  });
  
  function onSelectElements(e) {
      var $field = $(e.target);
      var selectedElements = e.elements;
  }
  </code></pre>
  
  <p><strong>HUGE CAVEAT</strong></p>
  
  <p>Depending on <em>when</em> your plugin's JavaScript runs, the <code>elementSelect</code> data attribute for the element select input DOM nodes may return <code>null</code>.</p>
  
  <p>This is because Craft actually initialises the BaseElementSelectInput instances in some inline JavaScript injected into the source, and this JavaScript is, unfortunately, written to the page <em>after</em> your plugin's JavaScript resources are queued up (after any inline JavaScript your plugin adds, too).</p>
  
  <p>In other words, if you need to hook into the BaseElementSelctInput instances on pageload, you'll need to <em>poll</em> for the <code>data('elementSelect)</code> attribute (ew, I know):</p>
  
  <pre><code>$('.elementselect, '.categoriesfield').each(function () {
      var now = new Date().getTime();
      var getElementSelect = (function () {
          var elementSelect = $(this).data('elementSelect');
          if (elementSelect) {
              // Woo, I can haz elementSelect
              elementSelect.on('selectElements', onSelectElements);
          } else if (new Date().getTime() - now &lt; 5000) {
              // Poll for 5 secs (then give up)
              Garnish.requestAnimationFrame(getElementSelect);
          }
      }).bind(this)
      getElementSelect();
  });
  </code></pre>
  
  <p>Hack alert: If you don't want to poll for the elementSelect instances, there is another way. Credit due, I learnt this trick from looking at <a href="https://github.com/benjamminf" rel="nofollow noreferrer">Benjamin Fleming's</a> source code:</p>
  
  <pre><code>var elementSelectInit = Craft.BaseElementSelectInput.prototype.init;
  Craft.BaseElementSelectInput.prototype.init = function () {
      elementSelectInit.apply(this, arguments);
      this.on('selectElements', onSelectElements);
      this.on('removeElements', onRemoveElements);
  }
  
  function onSelectElements (e) {
      console.log(e);
  }
  </code></pre>
  
  <p>What the above does, is to basically hijack the BaseElementSelectInput class' <code>init</code> method, in order to add some custom event listeners <em>when each BaseElementSelectInput instance is initialised</em>. Works like a charm and – even if it's a huge hack – is quite a bit cleaner than the polling method, in my opinion.</p>
  
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2016-09-19T10:13:11.290Z
Answers.Text: Thanks Mats. That gets me up and running. I appreciate the additional considerations too!
Answers.UserDisplayName: Ben Parizek
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2016-09-19T10:45:22.210Z
Answers.Text: No worries Ben, happy it helped you out!
Answers.UserDisplayName: Mats Mikkel Rummelhoff
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2016-09-19T09:16:00.383Z
Answers.LastActivityDate: 2016-09-19T09:33:38.297Z
Answers.LastEditDate: 2017-04-13T12:51:00.303Z
Answers.UserDisplayName: Mats Mikkel Rummelhoff
Body: <p>I have a situation where I'd like to watch for a change in an Element Select field and determine if any Elements have been selected or not.</p>
  
  <p>Element Select fields have two <code>input</code> elements. One, is always present and never changes, and the other gets added and removed from the page depending on if any items have been selected. Here's a simplified version of the HTML:</p>
  
  <pre><code>&lt;div class="field" id="fields-fieldhandle-field"&gt;
  
      {# This input field is always present and never changes #}
      &lt;input type="hidden" name="fields[assets]" value=""&gt;
  
      &lt;div class="elements"&gt;
  
          {# When you add or remove elements, this .element div and input 
             get added and removed from the DOM #}
          &lt;div class="element&gt;
              &lt;input type="hidden" name="fields[assets][]" value="430"&gt;
          &lt;/div&gt;    
      &lt;/div&gt;
  
  &lt;/div&gt;
  </code></pre>
  
  <p>I've tried various listeners but so far they all get destroyed when the second input gets removed from the page, or never get added to the right input field if it is not on the page.</p>
  
  <p>How do I listen for an element that may or may not exist on the page?</p>
  
Tags: <plugin-development><javascript>
Title: How to watch for changes on an Element Select Relations field?
ThreadText: # How to watch for changes on an Element Select Relations field?
  
  (Asked by: username_0 on 2016-09-19)
  
  I have a situation where I'd like to watch for a change in an Element Select field and determine if any Elements have been selected or not.
  
  Element Select fields have two `input` elements. One, is always present and never changes, and the other gets added and removed from the page depending on if any items have been selected. Here's a simplified version of the HTML:
  
  ```
  <div class="field" id="fields-fieldhandle-field">
  
      {# This input field is always present and never changes #}
      <input type="hidden" name="fields[assets]" value="">
  
      <div class="elements">
  
          {# When you add or remove elements, this .element div and input 
             get added and removed from the DOM #}
          <div class="element>
              <input type="hidden" name="fields[assets][]" value="430">
          </div>    
      </div>
  
  </div>
  
  ```
  
  I've tried various listeners but so far they all get destroyed when the second input gets removed from the page, or never get added to the right input field if it is not on the page.
  
  How do I listen for an element that may or may not exist on the page?
  
  **Tags:** plugin-development, javascript
  
  
  ---
  
  ## 1 answers
  
  ### Answer by username_1 on 2016-09-19. Score: 2 | Accepted answer
  
  I think there's a few ways to skin this cat, but [the most robust way](https://craftcms.stackexchange.com/a/11656/1098) would probably be to listen to the the `selectElements` event for the [`BaseElementSelectInput`](https://github.com/pixelandtonic/Craft-Release/blob/master/app/resources/js/craft.js#L3660) instances bound to each field's container element (the instance will be bound to the `<div class="elementselect">` node, except for Categories fields where it'll be bound to the `<div class="categoriesfield">` node, for some reason) and can be accessed by calling `$('.elementselect').data('elementSelect')`.
  
  You can use the `.on()` method to listen to the actual events, here's one way to do it:
  
  ```
  $('.elementselect, .categoriesfield').each(function () {
      $(this).data('elementSelect').on('selectElements', onSelectElements);
  });
  
  function onSelectElements(e) {
      var $field = $(e.target);
      var selectedElements = e.elements;
  }
  
  ```
  
  **HUGE CAVEAT**
  
  Depending on *when* your plugin's JavaScript runs, the `elementSelect` data attribute for the element select input DOM nodes may return `null`.
  
  This is because Craft actually initialises the BaseElementSelectInput instances in some inline JavaScript injected into the source, and this JavaScript is, unfortunately, written to the page *after* your plugin's JavaScript resources are queued up (after any inline JavaScript your plugin adds, too).
  
  In other words, if you need to hook into the BaseElementSelctInput instances on pageload, you'll need to *poll* for the `data('elementSelect)` attribute (ew, I know):
  
  ```
  $('.elementselect, '.categoriesfield').each(function () {
      var now = new Date().getTime();
      var getElementSelect = (function () {
          var elementSelect = $(this).data('elementSelect');
          if (elementSelect) {
              // Woo, I can haz elementSelect
              elementSelect.on('selectElements', onSelectElements);
          } else if (new Date().getTime() - now < 5000) {
              // Poll for 5 secs (then give up)
              Garnish.requestAnimationFrame(getElementSelect);
          }
      }).bind(this)
      getElementSelect();
  });
  
  ```
  
  Hack alert: If you don't want to poll for the elementSelect instances, there is another way. Credit due, I learnt this trick from looking at [Benjamin Fleming's](https://github.com/benjamminf) source code:
  
  ```
  var elementSelectInit = Craft.BaseElementSelectInput.prototype.init;
  Craft.BaseElementSelectInput.prototype.init = function () {
      elementSelectInit.apply(this, arguments);
      this.on('selectElements', onSelectElements);
      this.on('removeElements', onRemoveElements);
  }
  
  function onSelectElements (e) {
      console.log(e);
  }
  
  ```
  
  What the above does, is to basically hijack the BaseElementSelectInput class' `init` method, in order to add some custom event listeners *when each BaseElementSelectInput instance is initialised*. Works like a charm and – even if it's a huge hack – is quite a bit cleaner than the polling method, in my opinion.
  
  > **Comments:**
  > **username_0**: Thanks username_1. That gets me up and running. I appreciate the additional considerations too!  
  > **username_1**: No worries Ben, happy it helped you out!  
  
  ---
  
  