Alphabet Search Reset Sorting along with Dropdowns

Alphabet Search Reset Sorting along with Dropdowns

redroosterdesignredroosterdesign Posts: 15Questions: 2Answers: 0
edited March 2021 in Free community support

Description of problem: I have a functioning alphabet sort, but I also have dropdown selections. I have a reset button that clears the search box and the drop downs, but I cannot figure out have to also make that same reset button reset the alphabet sort. I realize that the "All" button does this, but I need the other reset button to this as well because it isn't obvious when the user clicks reset that it has in fact reset when only one letter is still showing.

<script>var _alphabetSearch = '';
$.fn.dataTable.ext.search.push( function ( settings, searchData ) {
    if ( ! _alphabetSearch ) {
        return true;
    }
    if ( searchData[1].charAt(0) === _alphabetSearch ) {
        return true;
    }
    return false;
} );
/* Formatting function for row details - modify as you need */
function format ( d ) {
    // `d` is the original data object for the row
    return '<div class="extra">'+
        '<div class="row">'+
            '<div class="col-md-9">'+d.description+'</div>'+
            '<div class="col-md-3">'+'<a href="'+d.programurl+'" class="btn btn-default">'+'Visit Program Page'+'</a>'+'</div>'+
        '</tr>'+
    '</div>';
}
$(document).ready(function() {
    var table = $('#program-list').DataTable(
    {
        "ajax": "programs.txt",
        "columns": [
            {
                "className":      'details-control',
                "orderable":      false,
                "data":           null,
                "defaultContent": '',
                "render": function () {
                         return '<span class="fa fa-angle-down" aria-hidden="true"></span>';
                     },
            },
            { "data": "program" },
            { "data": "school" },
            { "data": "type" },
            { "data": "location" }
        ],
        "pageLength": 25,
        "order": [[1, 'asc']],
        "deferRender": true,
                initComplete: function () {
                this.api().column(2).every( function () {
                          var column = this;
                          var select = $('<select class="filter form-control mb-lg" id="school-filter"><option value="">School</option></select>')
                            .appendTo($("#college-filter"))
                            .on('change', function() {
                              var val = $.fn.dataTable.util.escapeRegex(
                                $(this).val()
                              );
                              column
                                .search(val ? '^' + val + '$' : '', true, false)
                                .draw();
                            });
                          column.data().unique().sort().each(function(d, j) {
                            select.append('<option value="' + d + '">' + d + '</option>')
                          });
                      });
                    
                    this.api().column(3).every( function () {
                          var column = this;
                          var select = $('<select class="filter form-control mb-lg" id="degree-type-filter"><option value="">Degree Type</option></select>')
                            .appendTo($("#degree-filter"))
                            .on('change', function() {
                              var val = $.fn.dataTable.util.escapeRegex(
                                $(this).val()
                              );
                              column
                                .search(val ? '^' + val + '$' : '', true, false)
                                .draw();
                            });
                          column.data().unique().sort().each(function(d, j) {
                            select.append('<option value="' + d + '">' + d + '</option>')
                          });
                      });
                    
                    this.api().column(4).every( function () {
                          var column = this;
                          var select = $('<select class="filter form-control mb-lg" id="location-type-filter"><option value="">Location</option></select>')
                            .appendTo($("#location-filter"))
                            .on('change', function() {
                              var val = $.fn.dataTable.util.escapeRegex(
                                $(this).val()
                              );
                              column
                                .search(val ? '^' + val + '$' : '', true, false)
                                .draw();
                            });
                          column.data().unique().sort().each(function(d, j) {
                            select.append('<option value="' + d + '">' + d + '</option>')
                          });
                      })
            }
    }
    );
 
 
    var alphabet = $('<div class="alphabet"/>').append( '' );
    $('<span class="clear active"/>')
        .data( 'letter', '' )
        .html( 'All' )
        .appendTo( alphabet );
    for ( var i=0 ; i<26 ; i++ ) {
        var letter = String.fromCharCode( 65 + i );
        $('<span/>')
            .data( 'letter', letter )
            .html( letter )
            .appendTo( alphabet );
    }
    alphabet.insertBefore( table.table().container() );
    alphabet.on( 'click', 'span', function () {
        alphabet.find( '.active' ).removeClass( 'active' );
        $(this).addClass( 'active' );
        _alphabetSearch = $(this).data('letter');
        table.draw();
    } );
    
    
    // Add event listener for opening and closing details
    $('#program-list tbody').on('click', 'td.details-control', function () {
        var tr = $(this).closest('tr');
        var tdi = tr.find(".fa");
        var row = table.row( tr );
 
        if ( row.child.isShown() ) {
            // This row is already open - close it
            row.child.hide();
            tr.removeClass('shown');
            tdi.first().removeClass('fa-angle-up');
            tdi.first().addClass('fa-angle-down');
        }
        else {
            // Open this row
            row.child( format(row.data()) ).show();
            tr.addClass('shown');
            tdi.first().removeClass('fa-angle-down');
            tdi.first().addClass('fa-angle-up');
        }
    });
    
    $('[data-toggle="tooltip"]').tooltip();
            if($('#school-filter').length == 0) {
                $('#college-filter').append('<label for="school-filter" class="sr-only">School</label>');
            }
            $('[data-toggle="tooltip"]').tooltip();
            if($('#degree-type-filter').length == 0) {
                $('#degree-filter').append('<label for="degree-type-filter" class="sr-only">Degree Type</label>');
            }
     $('[data-toggle="tooltip"]').tooltip();
            if($('#location-type-filter').length == 0) {
                $('#location-filter').append('<label for="location-type-filter" class="sr-only">Location</label>');
            }
    
    $('#reset-filters').click(function (e) {
    e.preventDefault();
    var table = $('#program-list').DataTable();
    console.log("reset table");
    $('.filter').val("");
    $('.filter').trigger('change'); 
    table
        .search('')
        .columns().search('')
        .draw();
});
    
});
      </script>
<div class="program-list">
<div class="filters-container">
     <div class="container">
         <div class="row">
                    <div class="col-md-4 col-xl-4">
                        <div id="college-filter"></div>
                    </div>
                    <div class="col-md-4 col-xl-4">
                        <div id="degree-filter"></div>
                    </div>
                     <div class="col-md-4 col-xl-2">
                        <div id="location-filter"></div>
                    </div>
                    <div class="col-md-12 col-xl-2">
                        <button id="reset-filters">Reset <span class="fas fa-redo-alt"></span></button>
                    </div>
                </div>
         </div>
</div>
     
    <div class="container p-0"> 
        <div class="row no-gutters">
            <div class="col">
                <div class="table-responsive">
                       <table id="program-list" class="table">
                           <thead>
                              <tr>
                                <th scope="col"><span class="sr-only">Arrow Icons to Expand Sections</span></th>
                                <th scope="col" id="program">Program</th>
                                <th scope="col" id="school">School</th>
                                <th scope="col" id="type">Degree Type</th>
                                <th scope="col" id="location">Location</th>
                             </tr>
                           </thead>
                       </table>
                    </div>
                </div>
           </div>
        </div>
  </div>

Edited by Kevin: Update the markdown backticks to fix formatting.

This question has accepted answers - jump to:

Answers

  • kthorngrenkthorngren Posts: 20,247Questions: 26Answers: 4,761
    Answer ✓

    This code:

        $('<span class="clear active"/>')
            .data( 'letter', '' )
            .html( 'All' )
    

    Is setting your All button's 'letter' data attribute to an empty string.

    The click event:

        alphabet.on( 'click', 'span', function () {
            alphabet.find( '.active' ).removeClass( 'active' );
            $(this).addClass( 'active' );
            _alphabetSearch = $(this).data('letter');
            table.draw();
        } );
    

    Is getting the 'letter' data attribute, assigning it to the global variable _alphabetSearch and calling draw() which runs the search plugin used for the alphabet search. Basically you will need to clear the active class and set _alphabetSearch to an empty string, for example add these line to your #reset-filters click event:

            alphabet.find( '.active' ).removeClass( 'active' );
            _alphabetSearch = '';
    

    Kevin

  • redroosterdesignredroosterdesign Posts: 15Questions: 2Answers: 0

    Thank you so much! That worked like a charm.

  • redroosterdesignredroosterdesign Posts: 15Questions: 2Answers: 0

    If I wanted to create an array for an item in this same table, for instance, I wanted to add more than one location to an item, how would I get the dropdown for location to separate the two items? I know that I would need to change on line 38 my "data": "location" to "data": "location[, ]" and I would put each location inside a bracket in the .txt file, but the dropdown is still putting the two locations together. Is there something that would need to be done to lines 78-92 as well?

  • kthorngrenkthorngren Posts: 20,247Questions: 26Answers: 4,761

    This code:

                              column.data().unique().sort().each(function(d, j) {
                                select.append('<option value="' + d + '">' + d + '</option>')
                              });
    

    Is generating the select list. d is the value in the cell. You can change this to build the select lists the way you want. You may need an if statement to check the column index and if its the location column use a different loop to build the select list otherwise use the one above.

    Kevin

  • redroosterdesignredroosterdesign Posts: 15Questions: 2Answers: 0

    Thank you Kevin, I thought that may be the case, but I am not familiar enough with arrays to be able to build it without an example. I've looked and looked, but haven't been able to find one. I was hoping there would be one on this site, but I just keep going in circles.

  • colincolin Posts: 15,142Questions: 1Answers: 2,586

    We're happy to take a look, but as per the forum rules, please link to a test case - a test case that replicates the issue will ensure you'll get a quick and accurate response. Information on how to create a test case (if you aren't able to link to the page you are working on) is available here.

    Cheers,

    Colin

  • redroosterdesignredroosterdesign Posts: 15Questions: 2Answers: 0
    edited April 2022

    I finally have a test case to share. You can see in the drop downs for core requirements and semesters that it is adding a comma between the array items, when I would like them to be listed as two separate entries.

    I'm using a .txt file that you can find here: https://mse-dev.uark.edu/programs.txt

    I've put the items in the .txt file that have multiple entries in brackets, which is working for displaying them inside the table, but not in the drop down filters above.

    I also noticed that if I don't put anything in one of the arrays, it leaves a blank spot in the drop down as opposed to not adding anything at all.

    I'm also wondering if filtering with some of these select inputs may be better as check boxes, but I haven't been able to find any examples of that on the datatable site.

    https://mse-dev.uark.edu/

  • colincolin Posts: 15,142Questions: 1Answers: 2,586

    In your code,

                              column.data().unique().sort().each(function(d, j) {
                                select.append('<option value="' + d + '">' + d + '</option>')
                              });
    

    you're adding all the data in that column. If you don't want black entries, just check for an empty string being adding to the select element.

    I've put the items in the .txt file that have multiple entries in brackets, which is working for displaying them inside the table, but not in the drop down filters above.

    I'm not clear on this one, please can you give an example from your page.

    There are a few threads here with checkbox searching, such as this example from this thread,

    Colin

  • redroosterdesignredroosterdesign Posts: 15Questions: 2Answers: 0

    I'm getting close, here is another example. This time I added checkboxes to some of the items and it appears to be working, but my issue is that I would like the items under semester and core requirements drop downs to have one item each and separate out the items instead of combining them with commas. I just don't know enough about generating that type of JavaScript to know how to make that happen.

    https://mse-dev.uark.edu/index-test.php

  • colincolin Posts: 15,142Questions: 1Answers: 2,586

    The issue will be similar to the one above, it'll be in the same code. There, it's adding the entire string into the select element. Instead, you'll want to split() the string, and add each element into the select, something like:

    column.data().unique().sort().each(function(d, j) {
     d.split(',').forEach(function(el) {
      select.append('<option value="' + el + '">' + el + '</option>')
      });
    });
    

    Colin

  • redroosterdesignredroosterdesign Posts: 15Questions: 2Answers: 0
    edited April 2022

    The split kind of worked. It did break the items into individual pieces, but now I have duplicates. I also still can't figure out how to check for empty items. It also appears when clicking on the items in the dropdown for the items that are split that it doesn't filter each one correctly. That may be because spaces are being added in front of the values of some items.

    https://mse-dev.uark.edu/index-test.php

    Just curious, would it be easier/or better to manually add the select filters at the top than pulling it dynamically? If so, what would that look like?

  • kthorngrenkthorngren Posts: 20,247Questions: 26Answers: 4,761

    That may be because spaces are being added in front of the values of some items.

    You can use Javascript trim() to remove leading and trailing spaces.

    but now I have duplicates.

    Use an array to keep track of what you have added to the select list to check against for duplicates.

    I also still can't figure out how to check for empty items.

    Do you mean like the second option in your list? Just is an if statement.

    Maybe something close to this will work:

    var options = [];
    column.data().unique().sort().each(function(d, j) {
     d.split(',').forEach(function(el) {
      el = el.trim();
      if ( el !== '' &&  ! options.include( el) ) { 
        options.push( el );
        select.append('<option value="' + el + '">' + el + '</option>')
      }
      });
    });
    

    Kevin

  • redroosterdesignredroosterdesign Posts: 15Questions: 2Answers: 0

    I tried that last suggestion, but I keep getting the error "options.include is not a function" You can see it here:

    https://mse-dev.uark.edu/index.php

  • kthorngrenkthorngren Posts: 20,247Questions: 26Answers: 4,761

    Sorry you need to use the Javascript array includes() method. Add an s to options.include( el) in the above code snippet.

    Kevin

  • redroosterdesignredroosterdesign Posts: 15Questions: 2Answers: 0

    So that appeared to have done the trick with listing one item in the select list and removing the empty item, but when I click on Math it only shows one item, the one by itself, even though there are two and when I click on computer, it doesn't show the listing that has computer in it. I feel like it's so close, but the filtering isn't working 100% correctly.

    https://mse-dev.uark.edu/index.php

  • kthorngrenkthorngren Posts: 20,247Questions: 26Answers: 4,761
    Answer ✓

    You are using this search:

                                  column
                                    .search(val ? '^' + val + '$' : '', true, false)
                                    .draw();
    

    This is a regex search that would look something like this: ^Computer$. This is looking for the start of the cell to have C and the end to have r but your data looks like this:

    Math, Computer, Communication, Structural
    

    You can try removing the ^ and $. You can try using smart search mode. See the search() docs for details of the different modes.

    Kevin

  • redroosterdesignredroosterdesign Posts: 15Questions: 2Answers: 0

    I think removing the ^ and $ did the trick. I think it's finally working the way I hoped it would. You are a life saver. Thank you!!!!!

  • redroosterdesignredroosterdesign Posts: 15Questions: 2Answers: 0

    One more question, I'm having trouble getting just the core requirements select drop down to show in alphabetical order. I used the sort(function(a, b){return a-b}) method and in the semester drop down it changed the order except for SP8W1, and for some reason the core requirements has math at the top.

    https://mse-dev.uark.edu/index.php

  • colincolin Posts: 15,142Questions: 1Answers: 2,586

    You're doing the sort on the full string, not when you've split them down to the individual components,

    Colin

  • redroosterdesignredroosterdesign Posts: 15Questions: 2Answers: 0

    If that's true, then do I move the sort code sort(function(a, b){return a-b}) down inside the forEach function? I'm not sure how to add that sort after they've been split.

    column.data().unique().sort(function(a, b){return a-b}).each(function(d, j) { d.split(',').forEach(function(el) { el = el.trim(); if ( el !== '' && ! options.includes(el) ) { options.push(el); select.append('<option value="' + el + '">' + el + '</option>') }

  • kthorngrenkthorngren Posts: 20,247Questions: 26Answers: 4,761

    The easiest way is to build the select options in a couple steps. The first step is to start with an empty array and loop through the column data and split the text - the order doesn't matter. Loop through the slit text result and push only those values that don't exist onto the array.

    Now you have an array of all the unique split text values. Loop through the sorted list and build your select options.

    Kevin

  • redroosterdesignredroosterdesign Posts: 15Questions: 2Answers: 0
Sign In or Register to comment.