Link to home
Start Free TrialLog in
Avatar of mbizup
mbizupFlag for Kazakhstan

asked on

Help needed deciphering javascript code

Visual Studio.NET 2017

I'm new to javascript, and am looking for help understanding this block of code.
It seems to be filtering an entire table, based on user input in SearchBox, character by character as the user types.

Does the search criteria get built by some built-in functionality, or should I be searching the code for some user-defined function outside of this block of code building a criteria string?

If the functionality is built in, can I restrict it to a particular column of the table, and if so, how?

    function FilterTable() {
        $(document).ready(function () {
            $("#MainContent_SearchBox").on("keyup", function () {
                var value = $(this).val().toLowerCase();
                $("#SubmitterTable tr").filter(function () {
                    $(this).toggle($(this).text().toLowerCase().indexOf(value) > -1);

                });

            });

        });

    }

Open in new window

SOLUTION
Avatar of Zakaria Acharki
Zakaria Acharki
Flag of Morocco image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of mbizup

ASKER

Zakaria,

Thanks for the line-by line explanation, and the second column example.  That was extremely helpful.

Julian,

Let me know if I'm understanding your post correctly.  Are you saying that:
- (document).ready is not an event handler, but more like a 'status' of the page?  (I had been interpreting it as comparable to a Page Load event)  
Would the keyup event even happen before the document was ready?


-  If I wanted to have a few search boxes for specific columns, should I create separate functions, and attach them to the keyup events of those search boxes... or create a function that accepts a parameter specifying the column to be searched, and have the keyup events of the search boxes call that function?



As an aside, I haven't been able to find it, but is there a site with a good comprehensive list of available javascript event names for dropdown lists, textboxes, etc?


PS - I'm coming from a VB.NET background.
From the official documentation

A page can't be manipulated safely until the document is "ready." jQuery detects this state of readiness for you. Code included inside $( document ).ready() will only run once the page Document Object Model (DOM) is ready for JavaScript code to execute.

In your case, IMO the right thing you could do is to attach the keyup() event to the search input you want inside the ready function with a callback to the related function created outside of it, example :

$(function () {
  $("#MainContent_SearchBox").on("keyup", mainContentFilter);
  $("#ColumnContent_SearchBox").on("keyup", columnContentFilter);
});

function mainContentFilter(){
    var value = $(this).val().toLowerCase();

    $("#SubmitterTable tr").filter(function () {
      $(this).toggle($(this).text().toLowerCase().indexOf(value) > -1);
    });
}

function columnContentFilter(){
    var value = $(this).val().toLowerCase();

    $("#SubmitterTable tr td:nth-child(2)").filter(function () {
      $(this).parent().toggle( $(this).text().toLowerCase().indexOf(value) > -1 );
    });
}

Open in new window


NOTE that you can always attach the same event to several elements using comma ',' separator like :

$(function () {
  $("#MainContent_SearchBox, #ColumnContent_SearchBox").on("keyup", globalContentFilter);
});

function globalContentFilter(){
    //You global logic here
}

Open in new window


Some links to help you undestand the events :

(document).ready is not an event handler
No, it is an event handler, it fires when the document is ready (once per document).
I had been interpreting it as comparable to a Page Load event
It is a page load event - which is why we bind to it in the "wild" - not inside a function. A function is called at some indeterminate time AFTER page load has completed - putting code inside a function that then binds to page load - makes no sense.

Would the keyup event even happen before the document was ready?
Possible but unlikely - but not sure how that is relevant?

Whether to have multiple or single KeyUp handlers - definitely Single.
Why - less code maintenance.
The functionality is identical in all cases - the only thing that changes is the source. Perfect case for a single (re-useable) block of code.

Consider this - 20 columns - now you have to create 20 event handlers for each - all doing basically the same thing?
Does not make sense.
Use custom attributes and properties / attributes to determine source and target and then create generic code for doing the search.

Are you wanting to do a search box for each column and have the search text target the content of that column?
Avatar of mbizup

ASKER

<<Are you wanting to do a search box for each column and have the search text target the content of that column? >>

Yes - that is my goal.

(Let me know if/when I need to open a new question)
I see that you don't need to code it from scratch (unless you do that to learn the JS), I suggest to you the use of datatable that contains a set of reach built-in functionalities around the table component, `Column filtering` is the one you're searching for :

FixedHeader being used with individual column filters

If you're using bootstrap I would suggest the use of `filter-control` extension of `bootstrapTable`, here is a live example :

Bootstrap table filter control
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of mbizup

ASKER

Thanks, both of you for the examples and explanations.  Julian, particularly for the working sample.

I'm going to have to return to re-read some of this to fully understand it, but in the meantime I was able to work Julian's sample code into the page to get the search functionality I was looking for:

    function populateData(json, query) {
        if (json.length > 0) {
            $(query).find('thead').empty();
            $(query).find('tbody').empty();

            var head = '<tr>';
            // Julian's search boxes
            for (x in json[0]) {

                head = head + '<td><input class="search" placeholder="' + x + '"></td>';

            }

            head = head + '</tr><tr>';      

            //Set Headers of the Table
            for (x in json[0]) {
                if (x === 'Total') {
                    head = head + '<th class="linetotal">' + x + '</th>';
                } else {
                    head = head + '<th>' + x + '</th>';
                }
            }
            head = head + '</tr>';
            
            $(query).find('thead').append(head);

            for (i = 0; i < json.length; i++) {
                var body = '<tr>';
                for (x in json[0]) {
                    if (x === 'Total') {
                        body = body + '<td class="linetotal">' + safeHTML(json[i][x]) + '</td>';
                    } else {
                        if (x === 'RFM Number') {
                            body = body + '<td>' + json[i][x] + '</td>';
                        } else {
                            body = body + '<td>' + safeHTML(json[i][x]) + '</td>';
                        }
                    }
                }
                body = body + '</tr>';
                $(query).find('tbody').append(body);
            }


// etc..


// Julian's search function

    $(function () {
        $('table').on('keyup', '.search', function () {
            var index = $(this).closest('td').index();
            var parent = $(this).closest('table');

            var searchText = this.value.toLowerCase();
            parent.find('tbody tr').each(function (i, e) {
                $('td:eq(' + index + ')', this).filter(function (item) {
                    $(e).toggle($(this).text().toLowerCase().indexOf(searchText) > -1);
                });
            });
        });
    });

Open in new window

You're welcome, glad we could help,

Here are some minor adjustments to your code in the creation of new elements using jQuery instead of the string implementation :

function populateData(json, query) {
    if (json.length > 0) {
        $('table').find('thead, tbody').empty();

        var head_searchboxes = $('<tr/>');

        // Julian's search boxes
        for (x in json[0]) {
            head_searchboxes.append('<td><input class="search" placeholder="' + x + '"></td>');
        }


        var headers = $('<tr/>');
        
        //Set Headers of the Table
        for (x in json[0]) {
            if (x === 'Total') {
                headers.append('<th class="linetotal">' + x + '</th>');
            } else {
                headers.append('<th>' + x + '</th>');
            }
        }
        
        $(query).find('thead').append(head_searchboxes).append(headers);

        for (i = 0; i < json.length; i++) {
            var body = $('<tr/>');

            for (x in json[0]) {
                if (x === 'Total') {
                    body.append('<td class="linetotal">' + safeHTML(json[i][x]) + '</td>');
                } else {
                    if (x === 'RFM Number') {
                        body.append('<td>' + json[i][x] + '</td>');
                    } else {
                        body.append('<td>' + safeHTML(json[i][x]) + '</td>');
                    }
                }
            }

            $(query).find('tbody').append(body);
        }
        ....
}

Open in new window

Avatar of mbizup

ASKER

Thanks, Zakaria.