fnRowCallback with JSON data source and search match highlighting

fnRowCallback with JSON data source and search match highlighting

CHgsdCHgsd Posts: 25Questions: 0Answers: 0
edited July 2011 in DataTables 1.8
I have recently revisited the project I was working on here: http://datatables.net/forums/discussion/2123/filter-post-processing-and-highlighting/p1
which allowed highlighting of matched text while filtering.

Some new features in DataTables 1.8 allowed me to do a much better task of loading large datasets, most notably bDeferRender which is absolutely brilliant.

The problem which I have run into is that fnRowCallback no longer works and I have been unable to determine why.
The passage is the following:
[code]"fnRowCallback": function( nRow, aData, iDisplayIndex, iDisplayIndexFull ) {
var settings = this.fnSettings();
var str = settings.oPreviousSearch.sSearch;
var regex = new RegExp( str + "(?!([^<]+)?>)", 'i')
$('td', nRow).each( function (i) {
this.innerHTML = aData[i].replace( regex, function(matched) { return ""+matched+"";} );
} );
return nRow;
},[/code]
The error returned is:[code]aData[i] is null[/code]

I can't help but suspect that this may be due to my moving from using DOM as a source to a JS array, but searching around hasn't yielded any answers.

Would anyone happen to know what may be causing this?

Cheers,

CHgsd

Post Scriptum: I am using 1.8.1

Replies

  • CHgsdCHgsd Posts: 25Questions: 0Answers: 0
    After some further debugging I discovered the problem. Indirectly it came from switching from DOM to JS array as this introduced the possibility of NULL values vs. empty values in the DOM.
    The solution was simple enough. I just needed to add a NULL check before the line modifying the innerHTML. Updated working code is as follows:
    [code] "fnRowCallback": function( nRow, aData, iDisplayIndex, iDisplayIndexFull ) {
    var settings = this.fnSettings();
    var str = settings.oPreviousSearch.sSearch;
    var regex = new RegExp( str + "(?!([^<]+)?>)", 'i');
    $('td', nRow).each( function (i) {
    if (!!aData[i]) {
    this.innerHTML = aData[i].replace( regex, function(matched) { return ""+matched+"";} );
    }
    } );
    return nRow;
    },[/code]

    I didn't fully think through the move from DOM to JSON and that ended up being the cause for the problem. JSON fed directly from a DB can have NULL data if the DB table has fields which can be NULL.
  • fbasfbas Posts: 1,094Questions: 4Answers: 0
    what is each() passing into the 'i' variable? I suspect that's the problem
  • CHgsdCHgsd Posts: 25Questions: 0Answers: 0
    According to http://api.jquery.com/jQuery.each/ and console.log()'s, the 'i' variable being passed is the index. This is allowing to search from the original data each time around I believe.
    You made me call into question my methodology however and I am now unsure that what I saw as the problem was the problem. It turns out that aData being either empty or NULL exhibits that problem as opposed to NULL as I originally thought.
    Also in debugging, I noticed remnant empty filterMatch spans hanging around so I made yet another change. The updated code is as follows:
    [code]
    "fnRowCallback": function( nRow, aData, iDisplayIndex, iDisplayIndexFull ) {
    var settings = this.fnSettings();
    var str = settings.oPreviousSearch.sSearch;
    if (str != "") {
    var regex = new RegExp( str + "(?!([^<]+)?>)", 'i');
    $('td', nRow).each( function (i) {
    if ((!!aData[i]) {
    this.innerHTML = aData[i].replace( regex, function(matched) { return ""+matched+"";} );
    }
    else {
    /*console.log(i);
    console.log(aData);
    console.log(nRow);
    console.log(this.innerHTML);*/

    }
    } );
    }
    return nRow;
    },
    [/code]
    Although it appears to work as expected I am happy to hear your critique if something looks like it could be improved.
  • fbasfbas Posts: 1,094Questions: 4Answers: 0
    edited July 2011
    the index of the element in the jquery set is NOT the same as the index within aData. this is a huge problem. it can work for the first N elements since 'i' is an integer... but after a point, you may get out of bounds array index errors.. DON'T use this value 'i'.


    rather than getting the index from your jquery set, the below code just uses the aData passed in, and 'i' is within the range of aData's indexes.
    [code]
    "fnRowCallback": function( nRow, aData, iDisplayIndex, iDisplayIndexFull ) {
    var settings = this.fnSettings();
    var str = settings.oPreviousSearch.sSearch;
    var regex = new RegExp( str + "(?!([^<]+)?>)", 'i')
    for (i in aData) {
    if (!!aData[i]) { // not sure what this is for..
    aData[i] = aData[i].replace( regex, function(matched) { return ""+matched+"";} );
    }
    }

    return nRow;
    },
    [/code]

    by the way, what is the "if (!!aData[i])" thing about?
  • CHgsdCHgsd Posts: 25Questions: 0Answers: 0
    Thank you fbas for your help and suggestions. I have not yet tried your code but reading it I see one thing which may be a problem. aData should remain "clean" so that each filter change it gets written from a clean copy.
    About the indexes being different, it was my belief that the aData array was used to create the 's and that as a result they had the same order and count. From the console.log() calls this also seemed to hold true. I have been away from datatables and js for a while as a whole though so appreciate your corrections.
    Finally the !!aData[i] was to recast as a boolean and check for true/false to avoid the null/empty value problems I was having.
    Basically the goal of the code and how it was working correctly previously was to highlight whatever matched the filter string. This is why it needs to match on a clean copy each time and use that to rewrite the html. The bug I ran into that prompted all this revisiting was with null/empty values.
    The one missing feature I have been unable to resolve is the lack of highlighting when filtering individual columns.
  • fbasfbas Posts: 1,094Questions: 4Answers: 0
    You're totally right, since you've defined the context of the jquery call to that row, the indexes will be limited to that set and I would expect them to be the same.

    You may be right as well that aData passed in is not tied to the data in the row/column. To get a reference into the actual data there is an interface function fnGetData(nRow).

    [code]

    "fnRowCallback": function( nRow, aData, iDisplayIndex, iDisplayIndexFull ) {
    var settings = this.fnSettings();
    var str = settings.oPreviousSearch.sSearch;
    var regex = new RegExp( str + "(?!([^<]+)?>)", 'i')

    // ditch the aData sent to us and use the data from DataTables for this row
    aData = oTable.fnGetData(nRow);

    for (i in aData) {
    if (!!aData[i]) {
    aData[i] = aData[i].replace( regex, function(matched) { return ""+matched+"";} );
    }
    }

    return nRow;
    },

    [/code]
  • CHgsdCHgsd Posts: 25Questions: 0Answers: 0
    fbas,

    Thank you again for your continued help and feedback. I really appreciate the dialogue.
    I am having trouble adapting your latest code to work for what I am trying to do.
    The supplied aData to fnRowCallback is always clean as it is never modified. What does get modified, at least how I was doing it before, is the innerHTML of the given table cell from within nRow.
    This allows the highlight/unhighlighting to take place.
    I have in the process of this thread resolved the original problem caused by empty/null values which I ran into causing me to start this thread. That being said I have encountered a much more significant problem which I could desperately use your help with if you are still interested. This new problem involves the highlighting code breaking ColVis.
    The most up to date version is presently here: http://datatables.net/forums/discussion/comment/23571#Comment_23571

    Thanks again!

    CHgsd
This discussion has been closed.