jQuery Isotope search filter
Asked Answered
K

1

14

Im using the following snippet to facilitate a live search on my Isotope implementation.

It works great, but im wondering if there is a way to limit the scope of what it searches for.

Our isotope returns HTML like the following:

<div class="item">
<span class="title"></span>
<span calss="description"></span>

Right now it searches everything, how can i limit it to filter in results from just the "title" field?

jQuery(document).ready(function($) {
    $(document).ready(function(){

        var $container = $('#portfolio'),

        // create a clone that will be used for measuring container width
        $containerProxy = $container.clone().empty().css({ visibility: 'hidden' });   

        $container.after( $containerProxy );  

        // get the first item to use for measuring columnWidth
        var $item = $container.find('.portfolio-item').eq(0);
        $container.imagesLoaded(function(){
            $(window).smartresize( function() {

                // calculate columnWidth
                var colWidth = Math.floor( $containerProxy.width() / 5 ); // Change this number to your desired amount of columns

                // set width of container based on columnWidth
                $container.css({
                    width: colWidth * 5 // Change this number to your desired amount of columns
                })
                .isotope({

                    // disable automatic resizing when window is resized
                    resizable: false,

                    // set columnWidth option for masonry
                    masonry: {
                        columnWidth: '.grid-sizer',
                        gutter: '.gutter-sizer'
                    }
                });

            // trigger smartresize for first time
            }).smartresize();
        });



        $( function() {
        // quick search regex
            var qsRegex;

            // init Isotope
            var $grid = $('#portfolio').isotope({
                itemSelector: '.portfolio-item',
                layoutMode: 'fitRows',
                filter: function() {
                    return qsRegex ? $(this).text().match( qsRegex ) : true;
                }
            });

            // use value of search field to filter
            var $quicksearch = $('.quicksearch').keyup( debounce( function() {
                qsRegex = new RegExp( $quicksearch.val(), 'gi' );
                $grid.isotope();
            }, 200 ) );


            // bind filter on select change
            $('#filter-select').on( 'change', function() {
                // get filter value from option value
                var filterValue = this.value;

                $grid.isotope({ filter: filterValue });
            });

        });



        // debounce so filtering doesn't happen every millisecond
        function debounce( fn, threshold ) {
            var timeout;
            return function debounced() {
                if ( timeout ) {
                    clearTimeout( timeout );
                }
                function delayed() {
                    fn();
                    timeout = null;
                }
                timeout = setTimeout( delayed, threshold || 100 );
            }
        }


        // filter items when filter link is clicked
        $('#filters a').click(function(){
            $('#filters a.active').removeClass('active');
            var selector = $(this).attr('data-filter');
            $container.isotope({ filter: selector, animationEngine : "css" });
            $(this).addClass('active');
            return false;

        });

    });

});
Katti answered 29/1, 2016 at 21:20 Comment(2)
Can you please show more of your HTML? At the very least, the #filter-select element so we know what element is triggering the change event.Stater
Please provide HTML as wellIgnorance
K
19

I believe this is what you want in your init to match only against the title:

return qsRegex ? $(this).find('.title').text().match( qsRegex ) : true;

i.e. Find the .title inside the current node, get its text(), then run the regex against that.

For the followup question about handling multiple filters: Currently you're running separate $grid.isotope({ filter: ...}) functions with a different filter for each UI widget, which overwrites the filter settings from any previous selection.

One way to avoid that problem would be to write a single filter function that checks all your conditions. A working example of this is below; the interesting bits are commented.

$(function() {

    var $grid = $('#container');
    $grid.isotope({itemSelector: '.item'});

    var filters = []; // A convenient bucket for all the filter options, 
                      // just so we don't have to look them up in the DOM every time.
                      // (a global array is maybe sort of not the most elegant 
                      // way you could deal with this but you get the idea.)
    
    // Search event handlers
    $('.quicksearch').on('keyup', function() {
        // debounce removed for brevity, but you'd put it here
        filters[0] = this.value;
        runFilter();
    });
    $('#filter-select').on('change', function() {
        filters[1] = this.value;
        runFilter();
    });
    // and so on if more filters needed

    // The filter itself
    var runFilter = function() {
        $grid.isotope({
            filter: function() {
                if (filters[0]) {
                    // at least some search text was entered:
                    var qsRegex = new RegExp(filters[0], 'gi');

                    // if the title doesn't match, eliminate it:
                    if (!$(this).find('.title').text().match(qsRegex)) {
                        return false;
                    }
                }

                if (filters[1]) {
                    // a category was selected; filter out others:
                    if (!($(this).hasClass(filters[1]))) {
                        return false;
                    }
                }

                // etcetera, for any other filters 

                // successfully passed all conditions, so:
                return true;
            }
        });
    }
});
.item {
  width: 120px;
  height: 70px;
  margin: 3px; padding: 3px;
  display: inline-block;
}

.red {background: red;}
.blue {background: blue;}
.green {background: green;}
.yellow {background: yellow;}
/* isotope css (animations etc) omitted */
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://isotope.metafizzy.co/v1/jquery.isotope.min.js"></script>
<div id="filters">
    Color: <select id="filter-select">
        <option value="">All</option>
        <option value="red">Red</option>
        <option value="green">Green</option>
        <option value="blue">Blue</option>
    </select><br>
    Title: <input type="text" class="quicksearch">
</div>

<div id="container">
    <div class="red item"><div class="title">Title aaa</div><div class="description">Description xxx</div></div>
    <div class="green item"><div class="title">Title bbb</div><div class="description">Description yyy</div></div>
    <div class="blue item"><div class="title">Title ccc</div><div class="description">Description zzz</div></div>
    <div class="yellow item"><div class="title">Title aaa</div><div class="description">Description xxx</div></div>
    <div class="red item"><div class="title">Title bbb</div><div class="description">Description yyy</div></div>
    <div class="green item"><div class="title">Title ccc</div><div class="description">Description zzz</div></div>
    <div class="blue item"><div class="title">Title aaa</div><div class="description">Description xxx</div></div>
    <div class="yellow item"><div class="title">Title bbb</div><div class="description">Description yyy</div></div>
    <div class="red item"><div class="title">Title ccc</div><div class="description">Description zzz</div></div>
    <div class="green item"><div class="title">Title aaa</div><div class="description">Description xxx</div></div>
    <div class="blue item"><div class="title">Title bbb</div><div class="description">Description yyy</div></div>
    <div class="yellow item"><div class="title">Title ccc</div><div class="description">Description zzz</div></div>
</div>
Kalmuck answered 2/2, 2016 at 19:32 Comment(9)
this works great except that now once a filter has been selected, the search no longer works. I updated my code to include all isotope related JS.Katti
hm, apparently I, too, need to check my work :/ It looks like selecting a filter overwrites the filter function with $('#filter-select').value() -- I think you'll need to change the contents of #filter-select with new values (can you include that HTML for us? Or at least a representative example of some of its current options?)Kalmuck
Updated answer; if I guessed wrong about how #filter-select is intended to work let me know.Kalmuck
Gotcha, I see what you mean, it's a separate filter, not a modifier of the first. Updated answer again. (I'm a little surprised that isotope doesn't seem to have a good built-in way to handle multiple complex filters other than just smushing together class names, but this seems to be how it's done)Kalmuck
hmm this seems great but now getting error in firefox?Katti
The snippet is working for me in firefox. What error are you seeing?Kalmuck
Never mind, I see it in your site. That appears to be a syntax error unrelated to this code (it's on an addEventListener somewhere in there.)Kalmuck
Found it, looks good to go! Last question, cant he quicksearch regrex do a starts with instead of contains?Katti
Sure, just add a caret: var qsRegex = new RegExp('^'+filters[0], 'i');Kalmuck

© 2022 - 2024 — McMap. All rights reserved.