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;

        });

    });

});
Ashwin Aggarwal
  • 589
  • 7
  • 20
js111
  • 1,266
  • 4
  • 29
  • 54

1 Answers1

18

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>
Daniel Beck
  • 16,972
  • 5
  • 29
  • 49
  • 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. – js111 Feb 02 '16 at 19:34
  • 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?) – Daniel Beck Feb 02 '16 at 19:42
  • Updated answer; if I guessed wrong about how `#filter-select` is intended to work let me know. – Daniel Beck Feb 02 '16 at 21:22
  • 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) – Daniel Beck Feb 03 '16 at 13:02
  • hmm this seems great but now getting error in firefox? – js111 Feb 03 '16 at 16:32
  • The snippet is working for me in firefox. What error are you seeing? – Daniel Beck Feb 03 '16 at 16:41
  • 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.) – Daniel Beck Feb 03 '16 at 16:44
  • Found it, looks good to go! Last question, cant he quicksearch regrex do a starts with instead of contains? – js111 Feb 03 '16 at 16:47
  • Sure, just add a caret: `var qsRegex = new RegExp('^'+filters[0], 'i');` – Daniel Beck Feb 03 '16 at 16:50