Search code examples
jqueryinsertafter

Add TR after matched set, or at end if matched set is empty in jQuery


The Question

I'd like to add a new TR in a table after a given matched set, or instead, at the end of the table if the matched set is empty. Given the following html:

<table id="blax">
   <thead>
   </thead>
   <tbody>
      <tr class="alpha">
         <td>some stuff</td>
         <td>more stuff</td>
      </tr>
      <tr class="alpha">
         <td>some stuff</td>
         <td>more stuff</td>
      </tr>
      <!-- new "alpha" tr to go here-->
      <tr class="beta">
         <td>some stuff</td>
         <td>more stuff</td>
      </tr>
      <tr class="beta">
         <td>some stuff</td>
         <td>more stuff</td>
      </tr>
      <!-- but new "gamma" tr to go at end-->
   </tbody>'
</table>

I know how to add a new tr after some matched set, for example:

var b = $('#blax tbody');
var newAlphaTr = $('<tr class="alpha"></tr>')
   .insertAfter(b.find('tr.alpha:last'));

But what if the same code is run for class gamma (which doesn't exist in the table yet)? What's an easy way to then make the insertion point of the new <tr class="gamma"></tr> element just go at the end of the table using the same code as used for the alpha insertion?

function insertAtPosition(parentElement, afterElement, newElement) {
   // some code here that does the job for either "alpha" or "gamma"
}

function creatElement(className) {
   insertAtPosition(
      b,
      b.find('tr.' + className + ':last'),
      $('<tr class="' + className + '"></tr>'
   );
}

createElement('alpha');
createElement('gamma');

I need help filling in the "some code here" block.

Now for some idle chatter

And incidentally does insertAfter return the inserted element or the item it was added after? I'm guessing that after returns the inserted item and insertAfter returns the item it was attached to, like like the way append and appendTo work, yes? The jQuery docs are nice but have some big holes such as not listing what is returned from each function (and I'm quite puzzled about how anyone could miss this). In fact as I was writing this question it took some real concentration to make sure I'd gotten right.

Just idly thinking, it would be nice if append and appendTo supported an optional parameter which is either an index position or an immediate child element (or set of immediate child elements) after ALL of which the item will be appended. In the case of the index it could either be the desired position or the position after which to insert the item, so that 0 or -1 would mean as the first child.

var b = $('#blax tbody');
var newAlphaTr = $('<tr class="alpha"></tr>')
   .appendTo(b, $('tr.alpha', b));
// or perhaps a selector
var newAlphaTr2 = $('<tr class="alpha"></tr>')
  .appendTo(b, 'tr.alpha:last');

This would insert a new tr with class alpha after specified child set. I could add :last to the selector but this way would be nice. If the matched set of 'td.alpha' is empty, then it would act like append normally does and put it after the existing children.


Solution

  • With almost 2 more years of jQuery experience under my belt since asking the question, I can see now that the answer is easy:

    var b = $('#blax tbody'),
       newAlphaTr = $('<tr class="alpha"></tr>')
          .insertAfter(
             b.find('tr.alpha:last, tr:last')
                .first()
          );
    

    Instead of matching just on the tr with the desired class, also match the last tr regardless of class. Then, grab the first with first(), which will be the one we want to insert after. Problem solved.