column.search() with an Html tag

column.search() with an Html tag

ThomasHerThomasHer Posts: 4Questions: 1Answers: 0

Hi !

I have a problem with the search function. My table has 2 Html tags inside a <td> tag and would like to search inside these tags to display the row I want. Here is the HTML for 1 cell

<td><checkOrCross><i class="fas fa-times w3-text-red"></i></checkOrCross></td>

The tag <checkOrCross> is an helper generated in the server-side. I would like to have a filter with a <select> so I did that

<select>
              <option value="" selected></option>
              <option value="True">Oui</option>
              <option value="False">Non</option>
</select>
$('select').on('change', function () {
        var val = $(this).val();
        var classExpected;
        if (val == "False")
            classExpected = "fa-times";
        else if (val == "True")
            classExpected = "fa-check";
        else
            classExpected = "";
        if (table.column(-1).type == "html")
        table
            .columns(-1)
            .search(classExpected)
            .draw();
    });

As you can see, I want to search the class name of my <i> which only display a cross or a check.
But, because it's inside an HTML tag, the search() method can't find all the attributes. I tried this

table
.columns(-1)
.search("<checkOrCross")
.draw();

But it failed. I tried with regular expressions but it was not a success neither.
so my question is : How can I use the search() function to search inside a Html tag / class name of a tag ?
at least, is it possible ? Do I have to escape some characters ?

thank you for your help.

Answers

  • allanallan Posts: 62,994Questions: 1Answers: 10,368 Site admin
    edited May 2018

    The way to do this is with an orthogonal data renderer. When DataTables asked for "search" data, pull the class name out of the HTML string (a regex should do it, or perhaps a simple .indexOf if the string is known to be unique) and return what you want DataTables to search on.

    Regards,
    Allan

  • ThomasHerThomasHer Posts: 4Questions: 1Answers: 0

    Ok but i'm beginner with this plug-in (and also with javascript) so how am I suppose to get the HTML string of each row ?
    I don't know how to access to the data when I got the column

  • colincolin Posts: 15,236Questions: 1Answers: 2,598

    Hi @ThomasHer ,

    This example here may help - it's showing how the column's data can be tweaked before being displayed.

    Cheers,

    Colin

  • ThomasHerThomasHer Posts: 4Questions: 1Answers: 0

    Hi @colin

    Yeah thank you, it helped me !
    I did that :

    "columnDefs": [
                {
                    "render": function (data, type, row) {
                        return data.substr(24, 25); // return "fas fa-times w3-text-red"
                    },
                    "targets": 6 // This is for the sixth column of my array
                }
            ]
    

    And it works ! But the problem is that it replace my icon, in my array, by the string returned in commentary. So I tried to use "filter" instead of "render" but it doesn't work.
    I have searched how to do with the link of @allan (thanks by the way) but i'm steal at the same point.

    I am trying to access to the columnDefs to if I can see the data but it's also a failure. If it doesn't return an error, it's undefined. I tried :

    // Also with .data after
    table.columnDefs["filter"]
    table.columnDefs[0]
    table.columnDefs.0
    table.columnDefs.filter
    

    Is the field filter not supposed to be used automatically called when I use search() ?

  • colincolin Posts: 15,236Questions: 1Answers: 2,598

    Hi @ThomasHer ,

    Yep, it's definitely the columns.render you want. The function can be multi-purpose and return different data for a cell depending on whether it's for a filter, search or just display. This example here for example returns different values for display (it makes a URL) than it does for everything else. This may be the way to go for you,

    Cheers,

    Colin

  • ThomasHerThomasHer Posts: 4Questions: 1Answers: 0
    edited May 2018

    Hi @colin ,

    Yeah I tried a lot of things with render, but it doesn't work. the problem is that the search function can't search occurences inside html tags. It can't search the html tag's name itself !
    With render, I try to return

    <i class="myClass"></i>
    

    But it cannot even find "i". If i return

    <i class="myClass">random</i>
    

    It will only search in the "random" string

    The problem is that my <td> has nothing except the <i> tag and I cant put anything inside it because I only want the icon in my <td>.
    So i'm lost. I don't see how can I use your tools to filter my column.

  • colincolin Posts: 15,236Questions: 1Answers: 2,598

    Hi @ThomasHer ,

    Probably the best bet here is if you create a live example/fiddle with what you've got (the data and the code), and saying what you want, then we'd have something to work with,

    Cheers,

    Colin

  • DAustinDAustin Posts: 16Questions: 0Answers: 1

    Bit late to the party, but I stumbled on this thread regarding the exact same issue (using font-awesome icons instead of text values), so I thought I'd post my solution for anyone who drops by.

    Caveat: This uses Dropdown filters (<select>) to apply filtering, but the solution should work wherever you call the search() function.

    The short version: Use a hidden <span> element that contains a string to search by using RegEx/Smart Search. You'll need to apply additional css if you need to hide it on printing etc.

    The long version:
    This builds on another post (which I can't be bothered to look for) regarding filtering columns with drop-downs. My apologies for not giving the original author credit! It's been a while since I wrote the original callback.

    This could probably be written better, anyone who wants to is welcome to update the code if needed.

    In your datatables options do something similar to the following:

    initComplete: function () {
    
                //Initialise the api
                var api = this.api();
    
                //Set a counter to zero, needed later for filter grouping
                var i = 0;
    
                //Use an array to define which columns we want to filter on and call the .every() method
                api.columns([0, 1, 2, 4, 5, 7]).every(function (index) {
    
                    //Reference the current column
                    var column = this;
    
                    //Custom function, basically a Switch statement that gets me back the original column title so users know what they're looking at
                    var colName = colTitle2(index);
    
                    //Here comes the filtering
                    //Create a variable to write our select element to the table header using .appendTo($(column.header()))
                    //The variable carries a trigger to detect when the value is changed .on('change', function() {...})
                    var select =
                        $(
                            '<select class="form-control input-sm column-filter-select" style="max-width:120px;" onclick="event.stopPropagation()"><option value="">' +
                            colName +
                            '</option></select>')
                            .appendTo($(column.header()))
                            .on('change',
                            function () {
    
                                //Create a variable to hold the value of the selected option/filter
                                var val;
    
                                //IMPORTANT - Check if the filter is on the offending column (The one with icons only, plus hidden text).  In my case this is column index of 7. Change the number to what you need
                                if (index == 7) {
    
                                    //Check the value of the selected option and set val variable to the same as your hidden text string.  I am using "True" and "False"
                                    if ($(this).val() == "Active") {
                                        val = 'True'
                                    } else if ($(this).val() == "Inactive") {
                                        val = 'False'
                                    }
    
                                    //Call the search method with regex and update/redraw the table
                                    column.search(val ? val : '', true, false).draw();
    
                                } else {
    
                                    //If we are not in the offending icon column, run a standard search
                                    val = $.fn.dataTable.util.escapeRegex(
                                        $(this).val()
                                    );
                                    column.search(val ? '^' + val + '$' : '', true, false).draw();
                                }                                
                            });
    
                    //This is where we set the Option Values
                    column.data().unique().sort().each(function (d, j) {
    
                        //The following is fairly straightforward.  You just need to catch whether or not you have the different icons present.
                        //This is done by checking the data object (d in this case) for the class name
    
                        //Is this cell a check?
                        if (d.indexOf("fa-check") > -1) {
                            select.append('<option class="column-filter" data-group="' +
                                i +
                                '" value="Active">Yes</option>')
                        }
    
                        //Or is this cell a cross?
                        else if (d.indexOf("fa-times") > -1) {
                            select.append('<option class="column-filter" data-group="' +
                                i +
                                '" value="Inactive">No</option>')
                        }
    
                        //Or is this neither?  Use a standard option bulder.
                        else {
                            select.append('<option class="column-filter" data-group="' +
                                i +
                                '" value="' +
                                d +
                                '">' +
                                d +
                                '</option>')
                        }                    
                    });
                    i++;
                });
            },
    
            //Finally we need to set the drawCallback option to handle the filtering
            drawCallback: function (settings) {
    
                //Initialise the api
                var api = this.api();
    
                //Set our counter for filter grouping
                var i = 0;
    
                //Run the .every() method again against the same columns as earlier
                api.columns([0, 1, 2, 4, 5, 7]).every(function (index) {
    
                    //Reference the current column
                    var thisColumn = this;
    
                    //Custom function for getting the column names again
                    var colName = colTitle2(index);
    
                    //Get the <th> element where the <select> resides
                    var columnIndex = $(thisColumn.header()).index();
    
                    //Find the <select> element.  This would be whatever your input is for filtering
                    var selectElement = $(thisColumn.header()).find('select');
    
                    //Check if the val of the <select> is not set.  If it isn't, build our options!
                    if (selectElement.val() == null || selectElement.val() == '') {
    
                        //Remove any existing options
                        selectElement.empty();
    
                        //Add the column title as our first choice with an empty value.  This will remove filtering when selected
                        selectElement.append('<option value="">' + colName + '</option>');
    
                        //Create the other options for the <select>. Same procedure as before...
                        api.column(index, { search: 'applied' }).data().unique().sort().each(function (d, j) {
    
                            //Is this a check?
                            if (d.indexOf("fa-check") > -1) {
                                selectElement.append('<option class="column-filter" data-group="' + i + '" value="Active">Yes</option>')
                            }
    
                            //Or is this a cross?
                            else if (d.indexOf("fa-times") > -1) {
                                selectElement.append('<option class="column-filter" data-group="' + i + '" value="Inactive">No</option>')
                            }
    
                            //Or is it neither?  Use a standard option builder
                            else {
                                selectElement.append('<option class="column-filter" data-group="' +
                                    i +
                                    '" value="' +
                                    d +
                                    '">' +
                                    d +
                                    '</option>')
                            }
                        });
                    }
    
                    //Increment our counter
                    i++;
                });
            }
    

    This will build dropdown selects for any columns that you specify in the array for the .columns([array]).every() method, and will support icon-only columns in the following format:

    <i class="fa fa-fw fa-check"></i><span class="hidden">_YOUR HIDDEN TEXT TO SEARCH AGAINST HERE_</span>

    Replace fa-check (or fa-times), with whatever icon you are using wherever you see it in the code above.

    Obviously this isn't supported behaviour for DataTables, however on exporting to excel only the text value is displayed as the html is stripped out, hence why I use "True" and "False" as my hidden text string.

    Hope this helps anyone else who swings by!

    :)

This discussion has been closed.