Skip to content

Instantly share code, notes, and snippets.

@groovenectar
Last active August 29, 2015 14:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save groovenectar/17153f0da7ac2b29c981 to your computer and use it in GitHub Desktop.
Save groovenectar/17153f0da7ac2b29c981 to your computer and use it in GitHub Desktop.
jquery.searchify.js
<!doctype html>
<html>
<title>jquery.searchify.js</title>
<link rel="stylesheet" href="style.css">
<script type="text/javascript" src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
<script type="text/javascript" src="jquery.searchify.js"></script>
<style type="text/css">
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
</style>
<script type="text/javascript">
jQuery(function($) {
$('.example').searchify();
$('.example2').hide();
});
</script>
</html>
<body>
<div class="nav"></div>
<ul class="example">
<li>Lorem</li>
<li>ipsum</li>
<li>dolor</li>
<li>amet</li>
<li>consectetur</li>
<li>adipiscing</li>
<li>elit</li>
<li>consequat</li>
<li>consectetur</li>
<li>sollicitudin</li>
<li class="test">fermentum</li>
<li>auctor</li>
<li>elementum</li>
<li class="test">Integer</li>
<li>neque</li>
<li class="test">enim</li>
<li>fermentum</li>
<li>commodo</li>
<li>tincidunt</li>
<li>ligula</li>
<li class="test">dapibus</li>
<li>luctus</li>
<li>quam</li>
<li class="test">vitae</li>
<li>feugiat</li>
<li>sagittis</li>
<li class="test">Aliquam</li>
<li>ante</li>
<li>enim</li>
<li>elementum</li>
<li>scelerisque</li>
<li class="test">Cras</li>
<li>hendrerit</li>
<li>massa</li>
<li class="test">imperdiet</li>
<li>Fusce</li>
<li>cursus</li>
<li>vitae</li>
<li class="test">interdum</li>
<li>magna</li>
<li>Quisque</li>
<li>sagittis</li>
<li>lobortis</li>
<li>fringilla</li>
<li>Proin</li>
<li class="test">venenatis</li>
<li>ligula</li>
<li>Vestibulum</li>
<li>ante</li>
<li>vestibulum</li>
<li>sollicitudin</li>
<li class="test">sapien</li>
<li>suscipit</li>
<li>elit</li>
<li class="test">sapien</li>
<li>ante</li>
<li>cursus</li>
<li>condimentum</li>
<li>dapibus</li>
</ul>
<div class="nav"></div>
<ul class="example2">
<li>Lorem</li>
<li>ipsum</li>
<li>dolor</li>
<li>amet</li>
<li>consectetur</li>
<li>adipiscing</li>
<li>elit</li>
<li>consequat</li>
<li>consectetur</li>
<li>sollicitudin</li>
<li class="test">fermentum</li>
<li>auctor</li>
<li>elementum</li>
<li class="test">Integer</li>
<li>neque</li>
<li class="test">enim</li>
<li>fermentum</li>
<li>commodo</li>
<li>tincidunt</li>
<li>ligula</li>
<li class="test">dapibus</li>
<li>luctus</li>
<li>quam</li>
<li class="test">vitae</li>
<li>feugiat</li>
<li>sagittis</li>
<li class="test">Aliquam</li>
<li>ante</li>
<li>enim</li>
<li>elementum</li>
<li>scelerisque</li>
<li class="test">Cras</li>
<li>hendrerit</li>
<li>massa</li>
<li class="test">imperdiet</li>
<li>Fusce</li>
<li>cursus</li>
<li>vitae</li>
<li class="test">interdum</li>
<li>magna</li>
<li>Quisque</li>
<li>sagittis</li>
<li>lobortis</li>
<li>fringilla</li>
<li>Proin</li>
<li class="test">venenatis</li>
<li>ligula</li>
<li>Vestibulum</li>
<li>ante</li>
<li>vestibulum</li>
<li>sollicitudin</li>
<li class="test">sapien</li>
<li>suscipit</li>
<li>elit</li>
<li class="test">sapien</li>
<li>ante</li>
<li>cursus</li>
<li>condimentum</li>
<li>dapibus</li>
</ul>
</body>
</html>
// Based on https://github.com/jlafitte/jquery-quick-pagination
;(function ($, window, document, undefined) {
"use strict";
var defaults = {
label: 'Search:', // Inserted before input
typing_threshold: 500, // Delay in milliseconds to determine whether user is finished typing
min_chars: 3, // Must have typed at least this many characters to initiate search
match_class: 'search-match', // Matched items gets this class
search_location: 'before', // Possible values are "before,after,both,[jQuery Object]"
search_class: 'search-filter-form', // Class name of the search container
label_template: '<label>', // HTML or jQuery object. Any special attributes/classes can be added
input_template: '<input type="search">', // HTML or jQuery object. Any special attributes/classes can be added
wrap_label: true, // If false, give the input template an "id" attribute and label template a "for" attribute
item_filter: '*', // .filter() to run on items to determine what is included (E.g. ":visible")
form_input: null, // String selector or jQuery object. If specified, will only use existing input for search
item_text: function() { // Item text to match against. "this" context is the item element.
return this.text();
},
search_start: function(search_terms) { // "this" context is the plugin instance
this._debug('options.search_start called');
this.items.hide();
},
clear: function() { // "this" context is the plugin instance
this._debug('options.clear called');
this.element.prev('.no-matches').remove();
this.items.show();
},
matches: function(matches, search_terms) { // Called when there are search results. "this" context is the plugin instance
this._debug('options.matches called');
matches.show();
},
no_matches: function(search_terms) { // Called when there are no search results. "this" context is the plugin instance
this._debug('options.no_matches called');
this.element.before($('<p>').addClass('no-matches').text('No results for "' + search_terms + '"'));
},
complete: function() { // Called whether or not there are search results. "this" context is the plugin instance
this._debug('options.complete called');
}
};
function Plugin(element, options) {
this.element = $(element);
this.options = $.extend({}, defaults, options);
this._defaults = defaults;
this.init();
}
$.extend(Plugin.prototype, {
init: function () {
this.debug = true;
this._debug('init called');
this._loaditems();
this._render();
this.options.search_applied_class = this.options.match_class + '-applied';
this.search_terms = '';
this.typing_timer = null;
this.num_results = 0;
this.matches = null;
return this;
},
_render: function() {
this._debug('_render called');
// We don't need to build a search form if using external
if (this.options.form_input !== null) {
this.input = $(this.options.form_input);
return this;
}
this.search_form = $();
this.input = $();
this._insert_search_form();
return this;
},
_loaditems: function(filter) {
this._debug('_loaditems called');
filter = filter ? filter : this.options.item_filter;
this.items = this.element.children().filter(filter);
return this;
},
filter: function(filter) {
this._debug('filter called');
this._loaditems(filter);
this.reload();
return this;
},
_search_applied: function() {
this._debug('_search_applied called');
return this.element.hasClass(this.options.search_applied_class);
},
search: function(val) {
this._debug('search called');
if (this._search_applied()) {
this.clear();
}
this.search_terms = val;
if (this.search_terms.length < this.options.min_chars) {
return this;
}
this.options.search_start.call(this, this.search_terms);
this.element.addClass(this.options.search_applied_class);
var regex = new RegExp(this.search_terms, 'i');
var $plugin = this;
$plugin.items.filter(function() {
var text = $plugin.options.item_text.call($(this));
if (regex.test(text)) {
$(this).addClass($plugin.options.match_class);
$plugin.num_results++;
}
});
if (this.num_results > 0) {
this.matches = this.items.filter('.' + this.options.match_class);
this.options.matches.call(this, this.matches, this.search_terms);
} else {
this.matches = null;
this.options.no_matches.call(this, this.search_terms);
}
this.options.complete.call(this);
return this;
},
clear: function() {
this._debug('clear called');
this.matches = null;
this.search_terms = '';
this.num_results = 0;
this.element.removeClass(this.options.search_applied_class);
this.items.removeClass(this.options.match_class);
this.options.clear.call(this);
return this;
},
_sync_inputs: function(val) {
this._debug('_sync_inputs called');
this.input.val(val);
return this;
},
_build_input: function() {
this._debug('_build_input called');
var input = $(this.options.input_template);
var $plugin = this;
input.on('keyup', function(e) {
clearTimeout($plugin.typing_timer);
var input = $(this);
$plugin.typing_timer = setTimeout(function() {
input.trigger('search');
}, $plugin.options.typing_threshold);
});
input.on('keydown', function(e) {
clearTimeout($plugin.typing_timer);
});
// input.on('click', function(e) { });
// input.on('input', function(e) { });
input.on('search', function(e) {
$plugin._sync_inputs($(this).val());
$plugin.search($(this).val());
});
return input;
},
_build_search_form: function() {
this._debug('_build_search_form called');
var search_form = $('<form>').addClass(this.options.search_class);
search_form.on('submit', function(e) {
e.preventDefault();
});
var label = $(this.options.label_template).text(this.options.label);
search_form.append(label);
var input = this._build_input();
if (this.options.wrap_label) {
label.append(input);
} else {
search_form.append(input);
}
this.input = this.input.add(input);
this.search_form = this.search_form.add(search_form);
return search_form;
},
_insert_search_form: function(search_location) {
this._debug('_insert_search_form called');
search_location = search_location ? search_location : this.options.search_location;
if (search_location instanceof jQuery) {
this.search_form = search_location.append(this._build_search_form()).find('.' + this.options.search_class);
} else {
var search_form;
switch (search_location) {
case 'before':
search_form = this._build_search_form().addClass(this.options.search_class + '-before');
this.element.before(search_form);
break;
case 'after':
search_form = this._build_search_form().addClass(this.options.search_class + '-after');
this.element.after(search_form);
break;
case 'both':
this._insert_search_form('before');
this._insert_search_form('after');
return this;
break;
default:
return this;
break;
}
}
return this;
},
reload: function() {
this._debug('reload called');
this.remove();
this._render();
return this;
},
_remove_search_form: function() {
this._debug('_remove_search_form called');
this.search_form.remove();
return this;
},
remove: function() {
this._debug('remove called');
this.clear();
this._remove_search_form();
return this;
},
// Also removes plugin reference from this.element
// Additional functionality below
destroy: function() {
this._debug('destroy called');
this.remove();
},
_debug: function(string) {
if (!this.debug) {
return this;
}
if (new RegExp(' called$').test(string)) {
return this;
}
console.log(string);
}
});
var plugin_name = 'searchify';
$.fn[plugin_name] = function(options) {
var args = arguments;
if (options === undefined || typeof options === 'object') {
return this.each(function () {
if (!$.data(this, 'plugin_' + plugin_name)) {
$.data(this, 'plugin_' + plugin_name, new Plugin(this, options));
}
});
} else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') {
var returns;
this.each(function() {
var instance = $.data(this, 'plugin_' + plugin_name);
if (instance instanceof Plugin && typeof instance[options] === 'function') {
returns = instance[options].apply( instance, Array.prototype.slice.call(args, 1));
}
if (options === 'destroy') {
$.data(this, 'plugin_' + plugin_name, null);
}
});
return returns !== undefined ? returns : this;
}
};
}(jQuery, window, document));
.search-filter-form {
display: inline-block;
}
.search-filter-form label {
font-size: .8rem;
display: inline-block;
}
.search-filter-form input {
vertical-align: bottom;
display: block;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment