title: Actioning styling and a tooltipster on a dynamically created select2 element
body: <p>I have a page partly dynamically created with some select2 elements being returned from an ajax call. I then need to do some jquery on it to manipulate it. The page is a bit complex as it displays forms in a table, each row being one form.</p>
  
  <p>What I want to do is render a red colored border and a tooltipster when no data is typed in the select2 element. The select2 field is required and it should display a user error message as no data is typed in. There are several such  select2 elements on the page and none of them display any tooltipster. Other non-select2 fields do display their tooltipster.</p>
  
  <p>With some console loggers, I can see that the error message data to be fed to the tooltipster is coming back from the form submission. I can also see the tooltipster object is seen by jquery. But it just won't display and it won't get colored in red.</p>
  
  <p>In the successful property of the ajax call to save one row of data:</p>
  
  <pre><code>if ($.isEmptyObject(result['messages']['success'])) {
    if (!$.isEmptyObject(result['messages']['client_id'])) {
      var html = '';
      $.each(result['messages']['client_id'], function( index, value ) {
        html += ' ' + value;
      });
      displayError('#clients[' + maxLevel + ']-' + id, html);
      displayError('#client_id-' + id, html);
    }
    for (j = 0; j &lt;= maxLevel; j++) {
      if (!$.isEmptyObject(result['messages']['clients[' + j + ']'])) {
        var html = '';
        $.each(result['messages']['clients[' + j + ']'], function( index, value ) {
          html += ' ' + value;
        });
  console.log($('[name^="clients[' + j + ']-'+id+'"]'));
  console.log(html);
        displayError('[name^="clients[' + j + ']-'+id+'"]', html);
        displayError('#clients[' + j + ']-' + id, html);
        displayError('#client_id-' + id, html);
      }
    }
  </code></pre>
  
  <p>The functions to display the error:</p>
  
  <pre><code>function displayError(id, html) {
     $(id).css('border-color', 'red');
     $(id).attr('title', html);
     displayTooltipster(id);
  }
  
  function displayTooltipster(id) {
    if (!$(id).hasClass("tooltipstered")) {
      $(id).tooltipster({
        position: 'top-left',
        contentAsHTML: 'true',
        theme: '.tooltipster-default',
        animation: 'grow'
      });
    } else {
      $(id).tooltipster('enable');
    }
  }
  </code></pre>
  
  <p>The console loggers have this to display:</p>
  
  <pre><code>[input#clients[0]-212713.form-control.input-small.tooltipster.select2-offscreen, prevObject: init(1), context: document, selector: "[name^="clients[0]-212713"]"]
  vehicule-realocate:554  Une valeur est requise et ne peut être vide
  vehicule-realocate:553 
  [input#clients[1]-212713.form-control.input-small.tooltipster.select2-offscreen, prevObject: init(1), context: document, selector: "[name^="clients[1]-212713"]"]
  vehicule-realocate:554  Une valeur est requise et ne peut être vide
  vehicule-realocate:553
  </code></pre>
  
  <p>On each row, there are several select2 fields; each being displayed in a chained fashion, the next one replacing the previous one, allowing the selection of nodes in a tree. This works just fine and allows me to navigate the tree of nodes:</p>
  
  <pre><code>function handleSelect2Fields() {
      for (var i=0; i &lt;= maxLevel; i++) {
          var el = $('[name^="clients[' + i + ']"]');
          el.data('level', i);
          el.select2({
              width: 'element',
              ajax: {
                  url: '&lt;?php echo $this-&gt;url('users/get-client-options'); ?&gt;',
                  type: 'post',
                  dataType: 'json',
                  data: function(term, page) {
                      var level = parseInt(this.data('level'));
                      if (level &gt; 0) {
                          var sameRowClients = $(this).parent().parent();
                          var oneClientFromSameRow = sameRowClients.find('[name^="clients[' + (level-1) + ']"]');
                          var el = oneClientFromSameRow;
                          return {client_id : el.val(), term : term}
                      }
                  },
                  results: function(data, page) {
                      data.datas.push({'id':0, 'text':'[Retour]'});
                      return {results : data.datas, more : false}
                  }
              },
              initSelection : function (element, callback) {
              }
          });
          el.on('change', function(e) {
            var selectedValue = e.val;
              var level = parseInt($(this).data('level'));
              if (selectedValue == 0) {
                if (level &gt; 0) {
                  level -= 1;
                }
              } else {
                level += 1;
              }
              for (var i=0; i &lt;= maxLevel; i++) {
                  var sameRowClients = $(this).parent().parent();
                  var oneClientFromSameRow = sameRowClients.find('[name^="clients[' + i + ']"]');
                  var el = oneClientFromSameRow;
                  if (i == level) {
                    oneClientFromSameRow.parent().show();
                      el.select2("val", '');
                      el.select2("readonly", false);
                      el.select2("open");
                  } else if (i &lt; level &amp;&amp; level &lt;= maxLevel) {
                    oneClientFromSameRow.parent().hide();
                  } else if (i &gt; level) {
                    oneClientFromSameRow.parent().hide();
                      el.select2("readonly", true);
                      el.select2("val", '');
                  }
              }
              var id_suffix = oneClientFromSameRow.attr('id').split('-')[1];
              if (level &gt; maxLevel) {
                  $('[name=client_id-' + id_suffix + ']').val($(this).val());
              } else {
                  $('[name=client_id-' + id_suffix + ']').val('');
              }
              $('[name=client_id-' + id_suffix + ']').val($(this).val());
          });
          if (i != 0) {
              if (! el.val()) {
                el.select2("readonly", true);
                $('[name^="clients[' + i + ']"]').parent().hide();
              }
          }
      }
  
  function loadItems() {
      $.ajax({
          url: "&lt;?php echo $this-&gt;url('ajax', array('action' =&gt; 'get-vehicules-to-realocate'));?&gt;",
          data: {'maxLevel': maxLevel},
          type: 'post',
          success: function(items) {
              $.each(items['html'], function(id, contentHtml) {
                  $('#list-items').append(contentHtml);
              });
  
              $('#nb_items').html(items['total']);
              $('#header').html(items['header']);
  
              $(".datepicker").datepicker({ 'dateFormat': 'yy-mm-dd'});
  
              handleSelect2Fields();
          }
      });
  }
  </code></pre>
  
  <p>On the server side, some markup is generated to render the select2 elements:</p>
  
  <pre><code>$client = '&lt;input type="hidden" name="client_id-' . $id . '" id="client_id-' . $id . '" value="" class="tooltipster"&gt;';
  for ($j = 0; $j &lt;= $maxLevel; $j++) {
    $client .= '&lt;span&gt;&lt;input type="hidden" name="clients[' . $j . ']-' . $id . '" id="clients[' . $j . ']-' . $id . '" value="" class="form-control input-small tooltipster" required&gt;&lt;/span&gt;';
  }
  </code></pre>
  
  <p>The client fields are inside a bootstrap table:</p>
  
  <pre><code>$html[$i] = '&lt;div class="row" id="bloc-' . $id . '"&gt;'
      . '&lt;div class="span1" style="text-align:center; white-space:nowrap;"&gt;' . $client . '&lt;/div&gt;'
  </code></pre>