Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using jQuery Tablesorter with Prototype

I'm having issues when adding jQuery tablesorter functionality on an oldish app that uses Prototype.js. I'd use TableKit, but the table I want to sort is built + cleared down dynamically in an AJAX callback, something which TableKit doesn't handle. Works fine except when I try to trigger an update on the tablesorter cache:

// Clear the list view
$$('#scrollingList tr.dataRow').each(function(e) { 
    e.remove();
});

addMapMarkers(); // This function rebuild the sortable table

// triggering update to clear tablesorter cache
jQuery('#soundTable').trigger('update');

When the "update" callback throws an error table.tBodies[0] is undefined.

I knocked up a simplified test case and it worked fine if I don't include the prototype.js library, so I'm pretty certain this is a compatibility thing between jQuery and Prototype. I don't really want to re-write the whole thing in jQuery just to use tablesorter, so any help would be well received. Thanks.

Here's the code of the simple example...

<script type="text/javascript" src="../jquery-latest.js"></script>
<script type="text/javascript" src="../jquery.tablesorter.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/prototype/1.6.0.2/prototype.js" type="text/javascript"></script>
<script type="text/javascript">

    jQuery(document).ready(function() {
    jQuery("table").tablesorter(); 
    jQuery("#append").click(function() {

    // add some html
    var html = "<tr id='deltest' class='data'><td>Peter</td><td>Parker</td><td>28</td><td>$9.99</td><td>20%</td><td>Jul 6, 2006 8:14 AM</td></tr>";
    html += "<tr class='data'><td>John</td><td>Hood</td><td>33</td><td>$19.99</td><td>25%</td><td>Dec 10, 2002 5:14 AM</td></tr>";
    html += "<tr class='data'><td>Clark</td><td>Kent</td><td>18</td><td>$15.89</td><td>44%</td><td>Jan 12, 2003 11:14 AM</td></tr>";        
    html += "<tr class='data'><td>Bruce</td><td>Almighty</td><td>45</td><td>$153.19</td><td>44%</td><td>Jan 18, 2001 9:12 AM</td></tr>";

    var sorting = [[2,1],[0,0]];

   // append new html to table body 
    jQuery("table tbody").append(html);

    jQuery('tr').click(function(){  
    jQuery(this).remove();
    jQuery('table').trigger('update');
    jQuery("table").trigger("sorton",[sorting]) 

   }) 
  return false;
    });
});
</script>

I have a table that gets built dynamically, have attached a click handler to the first row. Clicking the first row after the table is built deletes the row and triggers an update. As I say works fine without prototype.js, gets the same error if included which I think points to a jQuery/Prototype conflict.

like image 932
user1461619 Avatar asked Jun 17 '12 09:06

user1461619


1 Answers

This is a very interesting problem indeed. And I've found the answer: It's because Prototype adds a function called update to DOM elements which expects to update their content.

How I got there, followed by what to do about it:

How I got there

First, I created a live copy of your code:

  • Without Prototype | source
  • With Prototype | source

I'm not 100% sure the tablesorter plug-in part of that works, and I don't quite get the same error you do, but without Prototype the one row gets removed as you'd expect, and with Prototype the whole table body disappears. Since the whole table body disappearing might cause an error like table.tbodies[0] is undefined, I figured that was good enough.

So I thought: I'll remove the tablesorter from the equation. First I removed just the script tag and the call to it, but left the jQuery('table').trigger(...) calls in place. The full table still disappeared with Prototype included, so it wasn't the tablesorter code itself. So I commented those trigger calls out, and it started working (just the clicked row gets removed).

I looked at the trigger calls and thought: I bet it's the jQuery('table').trigger('update') call. And sure enough, that's the culprit. Comment it out and all is well; leave it in and your table body disappears.

So why would that be? Let's think about what trigger does: It creates a synthetic event, then fires that through its own event handlers, then "fires" the event on the DOM element if it exists by calling it. E.g., if you trigger a 'click' event, eventually it will call 'domelement.click(event)to call any old-style DOM0 handlers on the element. Similarly if you trigger an 'update' event, eventually it will call 'domelement.update(event) if the DOM element has such a function.

That's where Prototype comes into it: Prototype adds a function called update to all DOM elements. The function is analogous to jQuery's html function: It accepts new content for the element, wipes out the old content, and adds the new. That function was getting called on the table, wiping out the tbody. So jQuery ends up calling Prototype's update on the element, wiping out the table's contents.

We can prove that's happening without using Prototype at all, like this:

jQuery(function($) {

  var p = document.createElement('p');
  p.innerHTML = "Hi there";
  p.update = function() {
    display("<code>update</code> called on paragraph");
  };

  document.body.appendChild(p);

  $("#theButton").click(function() {
    $("p").trigger("update");
  });

  function display(msg) {
    $("<p>").html(String(msg)).appendTo(document.body);
  }
});

Live copy | source

And sure enough, if we click the button, we see the message that update was called on the paragraph.

What to do about it

Don't call jQuery('table').trigger('update'). I don't see anything in the tablesorter docs that says you should trigger an event called "update", so you may not need to do that (although the documentation seems a bit light on detail). If you really need to do that, to make the tablesorter plug-in compatible with Prototype you may have to edit the source of the plugin to use a different word other than "update" for that event ("tableupdate", for instance).

like image 56
T.J. Crowder Avatar answered Oct 11 '22 16:10

T.J. Crowder