Mottie / tablesorter

Github fork of Christian Bach's tablesorter plugin + awesomeness ~
https://mottie.github.io/tablesorter/docs/
2.61k stars 754 forks source link

In IE <= 8, tablesorter (Pager?) throws error on AJAX-loaded table #148

Closed cautionbug closed 12 years ago

cautionbug commented 12 years ago

Error: SCRIPT5007: Unable to get value of the property 'row': object is null or undefined

IE 7/8 report a problem in the TS internal function below.

My HTML has no TR's in the TBODY. They're loaded via AJAX & the "update" method is triggered on the table once content is provided.

The AJAX callback appends TR's to the TBODY & then calls .tablesorter() on the table. However, while it works using several approaches in other browsers, IE 7/8 fail. It seems to attempt the for (k... loop too many times, or an extra time after, or something.

i've also tried calling tablesorter() outside this AJAX call, then triggering the "update" method in the AJAX callback. That also works in everything except IE 7/8.

Now, for all this code, it may be something stupid, but i can't seem to trace it, and it works fine in every other browser, and IE9.

Just before submitting the ticket: i commented out the stickyHeaders widget & IE8 loaded the page fine. Beyond that discovery, i have no idea what's wrong.

function appendToTable(table, callback, init) {
  var c = table.config,
  b = table.tBodies,
  rows = [],
  c2 = c.cache,
  r, n, totalRows, checkCell, $bk, $tb,
  i, j, k, l, pos, appendTime;
  if (c.debug) {
    appendTime = new Date();
  }
  for (k = 0; k < b.length; k++) {
    $bk = $(b[k]);
    if (!$bk.hasClass(c.cssInfoBlock)) {
      // get tbody
      $tb = ts.processTbody(table, $bk, true);

/** ERROR HERE **/
      r = c2[k].row;
      n = c2[k].normalized;
      totalRows = n.length;
      checkCell = totalRows ? (n[0].length - 1) : 0;
      for (i = 0; i < totalRows; i++) {
        pos = n[i][checkCell];
        rows.push(r[pos]);
        // removeRows used by the pager plugin
        if (!c.appender || !c.removeRows) {
          l = r[pos].length;
          for (j = 0; j < l; j++) {
            $tb.append(r[pos][j]);
          }
        }
      }
      // restore tbody
      ts.processTbody(table, $tb, false);
    }
  }
  if (c.appender) {
    c.appender(table, rows);
  }
  if (c.debug) {
    benchmark("Rebuilt table", appendTime);
  }
  // apply table widgets
  if (!init) { ts.applyWidget(table); }
  // trigger sortend
  $(table).trigger("sortEnd", table);
}

My HTML:

<table class="dashboard nowrap tablesorter" id="manager_dashboard">
  <caption>
    <p><span class="bold">Manager Dashboard</span>: Set training deadlines in your department for policies listed here.</p>
  </caption>
  <thead>
    <tr>
      <th class="{sorter:'text'}">Category</th>
      <th class="{sorter:'text'}">Policy Title</th>
      <th class="{sorter:'isoDate'}">Last Major<br>Change</th>
      <th class="{sorter:'isoDate'}">Last Review</th>
      <th class="{sorter:'isoDate'}">Last Training<br>Deadline</th>
      <th class="{sorter:'digit'}">Next Training Due</th>
      <th class="{sorter:false}">New Training<br>Deadline</th>
    </tr>
  </thead>
  <tbody></tbody>
  <tfoot></tfoot>
</table>

My Javascript (tablesorter init):

// Scope variables
$("#employee_dashboard, #manager_dashboard").each(function (i, dash){
  $this = $(this);
  _id = this.id;
  $this.tablesorter({
    sortList        : _sortList[_id],
    sortMultiSortKey: 'ctrlKey',
    textExtraction  : 'complex',
    widthFixed      : true,
    widgets         : ['uitheme', 'zebra', 'stickyHeaders'], // stickyHeaders seems to be the culprit
    widgetOptions   : {
      stickyHeaders: 'tablesorter-stickyHeader',
      uitheme      : 'bootstrap',
      zebra:['even', 'odd']
    }
  }).tablesorterPager({
    container : $("#" + _id + "_pager"),
    removeRows: false
  });

// Then in an AJAX callback:
$tbody.parent('table')
  .trigger("update", [true,
    function _dashboardUpdate(table){
      dashboardTrainingFormEvents($table, EmpMgr);
    }
  ]);
Mottie commented 12 years ago

Hi cautionbug!

Does the ajax loaded data include sub-tables? I'm having a hard time figuring out why older IE would have a problem with the cached data unless there happened to be an additional tbody in there somewhere.

The method you are using is how I would do it as well... initialize the table, then update it in the ajax callback.

I don't have the time to troubleshoot this right now, but I'll try to work on it tomorrow.

cautionbug commented 12 years ago

No extra TBODY tags in my table. No nested tables.

Two additional things i can think of: i think i'm using the hidden TR method to cache data. i dynamically replace rows & it messed up the default (better) cache. Also, there are 2 tables on the page being treated for tablesorter/pager. That seems less likely, but can't rule it out.

It's not urgent for me at this point, so when you have time, great. Thanks! On Oct 8, 2012 6:46 PM, "Rob G" notifications@github.com wrote:

Hi cautionbug!

Does the ajax loaded data include sub-tables? I'm having a hard time figuring out why older IE would have a problem with the cached data unless there happened to be an additional tbody in there somewhere.

The method you are using is how I would do it as well... initialize the table, then update it in the ajax callback.

I don't have the time to troubleshoot this right now, but I'll try to work on it tomorrow.

— Reply to this email directly or view it on GitHubhttps://github.com/Mottie/tablesorter/issues/148#issuecomment-9243184.

Mottie commented 12 years ago

Maybe consider using jQuery data to store the information you need... or alternatively, you can add an info only tbody, which uses the class name of tablesorter-infoOnly, set by the cssInfoBlock option, then hide it using css. Tablesorter will completely ignore anything inside that tbody.

By the way, are you also using the metadata plugin? I ask because the header class names are set up that way

<th class="{sorter:'text'}">Category</th>

And I was curious if people still use the meta data plugin since I was planning on removing support for it in Tablesorter version 3.

cautionbug commented 12 years ago

i don't think i need an infoOnly TBODY. i'm using the removeRows:false option to keep the HTML rows in the actual table, but hidden. Each row has a form that makes an AJAX call, updates data, and receives back a revised data row that replaces the original. There doesn't appear to be any direct access to the cache to update rows in it, so this was my workaround. When i was using the cache, updating rows resulted in the revised row being planted as expected, but the old row didn't go away. i took the shortcut rather than work out what i was doing wrong. :)

i just stumbled across the updateCell trigger demo, so maybe that's an approach i'll try. If i can figure that out, i'll try re-enabling stickyHeaders. At this point they're a minor convenience in this particular app, especially with Pager working.

i've been including the metadata plugin, though i don't make any direct calls to it. After removing it, my tables still function, so i guess i don't actually need it.

Thanks for your help.

--EDIT-- i see now the distinction between the class attribute (for metadata) and the data- attribute (jQuery). i'll revise my HTML to use data- & hopefully that'll eliminate any dependency on metadata (and be more semantically correct). :)

Mottie commented 12 years ago

Hiya! Please make sure to update to the latest version as I did tweak the pager ajax code in v2.4.2.

Otherwise, have you made any progress on this issue?