Last active
December 14, 2015 20:59
-
-
Save nikcree/c467069a510f11452230 to your computer and use it in GitHub Desktop.
Tool Tip Search in Primary Nav Location
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// NB search for domain.com in this file and change it to your live domain. | |
jQuery(function($) | |
{ | |
"use strict"; | |
$(document).ready(function() | |
{ | |
//creates search tooltip | |
new $.Tooltip(); | |
//creates ajax search | |
new $.AjaxSearch(); | |
}); | |
$.AjaxSearch = function(options) | |
{ | |
this.options = { | |
delay: 300, //delay in ms until the user stops typing. | |
minChars: 3, //dont start searching before we got at least that much characters | |
scope: 'body' | |
} | |
this.scope = $(this.options.scope); | |
this.timer = false; | |
this.lastVal = ""; | |
this.bind_events(); | |
} | |
$.AjaxSearch.prototype = | |
{ | |
bind_events: function() | |
{ | |
this.scope.on('keyup', '#s' , $.proxy( this.try_search, this)); | |
}, | |
try_search: function(e) | |
{ | |
clearTimeout(this.timer); | |
//only execute search if chars are at least "minChars" and search differs from last one | |
if(e.currentTarget.value.length >= this.options.minChars && this.lastVal != $.trim(e.currentTarget.value)) | |
{ | |
//wait at least "delay" miliseconds to execute ajax. if user types again during that time dont execute | |
this.timer = setTimeout($.proxy( this.do_search, this, e), this.options.delay); | |
} | |
}, | |
do_search: function(e) | |
{ | |
var obj = this, | |
currentField = $(e.currentTarget).attr( "autocomplete", "off" ), | |
form = currentField.parents('form:eq(0)'), | |
results = form.find('.ajax_search_response'), | |
loading = $('<div class="ajax_load"><span class="ajax_load_inner"></span></div>'), | |
action = form.attr('action'), | |
values = form.serialize(); | |
values += '&action=ajax_search'; | |
//check if the form got get parameters applied and also apply them | |
if(action.indexOf('?') != -1) | |
{ | |
action = action.split('?'); | |
values += "&" + action[1]; | |
} | |
if(!results.length) results = $('<div class="ajax_search_response"></div>').appendTo(form); | |
//return if we already hit a no result and user is still typing | |
if(results.find('.ajax_not_found').length && e.currentTarget.value.indexOf(this.lastVal) != -1) return; | |
this.lastVal = e.currentTarget.value; | |
$.ajax({ | |
url: 'http://domain.com/wp-admin/admin-ajax.php', // add the domain of the site here | |
type: "POST", | |
data:values, | |
beforeSend: function() | |
{ | |
loading.insertAfter(currentField); | |
}, | |
success: function(response) | |
{ | |
if(response == 0) response = ""; | |
results.html(response); | |
}, | |
complete: function() | |
{ | |
loading.remove(); | |
} | |
}); | |
} | |
} | |
$.Tooltip = function() | |
{ | |
this.options = { | |
delay: 1500, //delay in ms until the tooltip appears | |
delayOut: 300, //delay in ms when instant showing should stop | |
delayHide: 0, //delay hiding of tooltip in ms | |
"class": "search-tooltip", //tooltip classname for css styling and alignment | |
scope: "body", //area the tooltip should be applied to | |
data: "search-tooltip", //data attribute that contains the tooltip text | |
attach:"element", //either attach the tooltip to the "mouse" or to the "element" // todo: implement mouse, make sure that it doesnt overlap with screen borders | |
event: 'click', //mousenter and leave or click and leave | |
position:'bottom', //top or bottom | |
extraClass:'tooltip-class' //extra class that is defined by a tooltip element data attribute | |
} | |
this.body = $('body'); | |
this.scope = $(this.options.scope); | |
this.tooltip = $('<div class="'+this.options['class']+'"><span class="arrow-wrap"><span class="arrow"></span></span></div>'); | |
this.inner = $('<div class="inner_tooltip"></div>').prependTo(this.tooltip); | |
this.open = false; | |
this.timer = false; | |
this.active = false; | |
this.bind_events(); | |
} | |
$.Tooltip.openTTs = []; | |
$.Tooltip.prototype = | |
{ | |
bind_events: function() | |
{ | |
this.scope.on(this.options.event + ' mouseleave', '[data-'+this.options.data+']', $.proxy( this.start_countdown, this) ); | |
if(this.options.event != 'click') | |
{ | |
this.scope.on('mouseleave', '[data-'+this.options.data+']', $.proxy( this.hide_tooltip, this) ); | |
} | |
else | |
{ | |
this.body.on('mousedown', $.proxy( this.hide_tooltip, this) ); | |
} | |
}, | |
start_countdown: function(e) | |
{ | |
clearTimeout(this.timer); | |
if(e.type == this.options.event) | |
{ | |
var delay = this.options.event == 'click' ? 0 : this.open ? 0 : this.options.delay; | |
this.timer = setTimeout($.proxy( this.display_tooltip, this, e), delay); | |
} | |
else if(e.type == 'mouseleave') | |
{ | |
this.timer = setTimeout($.proxy( this.stop_instant_open, this, e), this.options.delayOut); | |
} | |
e.preventDefault(); | |
}, | |
reset_countdown: function(e) | |
{ | |
clearTimeout(this.timer); | |
this.timer = false; | |
}, | |
display_tooltip: function(e) | |
{ | |
var target = this.options.event == "click" ? e.target : e.currentTarget, | |
element = $(target), | |
text = element.data(this.options.data), | |
newTip = element.data('created-tooltip'), | |
extraClass = element.data('tooltip-class'), | |
attach = this.options.attach == 'element' ? element : this.body, | |
offset = this.options.attach == 'element' ? element.position() : element.offset(), | |
position = element.data('tooltip-position'), | |
align = element.data('tooltip-alignment'); | |
text = $.trim(text); | |
if(text == "") return; | |
if(position == "" || typeof position == 'undefined') position = this.options.position; | |
if(align == "" || typeof align == 'undefined') align = 'center'; | |
if(typeof newTip != 'undefined') | |
{ | |
newTip = $.Tooltip.openTTs[newTip] | |
} | |
else | |
{ | |
this.inner.html(text); | |
newTip = this.options.attach == 'element' ? this.tooltip.clone().insertAfter(attach) : this.tooltip.clone().appendTo(attach); | |
if(extraClass != "") newTip.addClass(extraClass); | |
} | |
this.open = true; | |
this.active = newTip; | |
if((newTip.is(':animated:visible') && e.type == 'click') || element.is('.'+this.options['class']) || element.parents('.'+this.options['class']).length != 0) return; | |
var animate1 = {}, animate2 = {}, pos1 = "", pos2 = ""; | |
if(position == "top" || position == "bottom") | |
{ | |
switch(align) | |
{ | |
case "left": pos2 = offset.left; break; | |
case "right": pos2 = offset.left + element.outerWidth() - newTip.outerWidth(); break; | |
default: pos2 = (offset.left + (element.outerWidth() / 2)) - (newTip.outerWidth() / 2); break; | |
} | |
} | |
else | |
{ | |
switch(align) | |
{ | |
case "top": pos1 = offset.top; break; | |
case "bottom": pos1 = offset.top + element.outerHeight() - newTip.outerHeight(); break; | |
default: pos1 = (offset.top + (element.outerHeight() / 2)) - (newTip.outerHeight() / 2); break; | |
} | |
} | |
switch(position) | |
{ | |
case "top": | |
pos1 = offset.top - newTip.outerHeight(); | |
animate1 = {top: pos1 - 10, left: pos2}; | |
animate2 = {top: pos1}; | |
break; | |
case "bottom": | |
pos1 = offset.top + element.outerHeight(); | |
animate1 = {top: pos1 + 10, left: pos2}; | |
animate2 = {top: pos1}; | |
break; | |
case "left": | |
pos2 = offset.left - newTip.outerWidth(); | |
animate1 = {top: pos1, left: pos2 -10}; | |
animate2 = {left: pos2}; | |
break; | |
case "right": | |
pos2 = offset.left + element.outerWidth(); | |
animate1 = {top: pos1, left: pos2 + 10}; | |
animate2 = {left: pos2}; | |
break; | |
} | |
animate1['display'] = "block"; | |
animate1['opacity'] = 0; | |
animate2['opacity'] = 1; | |
newTip.css(animate1).stop().animate(animate2,200); | |
newTip.find('input, textarea').focus(); | |
$.Tooltip.openTTs.push(newTip); | |
element.data('created-tooltip', $.Tooltip.openTTs.length - 1); | |
}, | |
hide_tooltip: function(e) | |
{ | |
var element = $(e.currentTarget) , newTip, animateTo, | |
position = element.data('tooltip-position'), | |
align = element.data('tooltip-alignment'); | |
if(position == "" || typeof position == 'undefined') position = this.options.position; | |
if(align == "" || typeof align == 'undefined') align = 'center'; | |
if(this.options.event == 'click') | |
{ | |
element = $(e.target); | |
if(!element.is('.'+this.options['class']) && element.parents('.'+this.options['class']).length == 0) | |
{ | |
if(this.active.length) { newTip = this.active; this.active = false;} | |
} | |
} | |
else | |
{ | |
newTip = element.data('created-tooltip'); | |
newTip = typeof newTip != 'undefined' ? $.Tooltip.openTTs[newTip] : false; | |
} | |
if(newTip) | |
{ | |
var animate = {opacity:0}; | |
switch(position) | |
{ | |
case "top": | |
animate['top'] = parseInt(newTip.css('top'),10) - 10; | |
break; | |
case "bottom": | |
animate['top'] = parseInt(newTip.css('top'),10) + 10; | |
break; | |
case "left": | |
animate['left'] = parseInt(newTip.css('left'), 10) - 10; | |
break; | |
case "right": | |
animate['left'] = parseInt(newTip.css('left'), 10) + 10; | |
break; | |
} | |
newTip.animate(animate, 200, function() | |
{ | |
newTip.css({display:'none'}); | |
}); | |
} | |
}, | |
stop_instant_open: function(e) | |
{ | |
this.open = false; | |
} | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Place in functions.php | |
****************************************************************************************** */ | |
// Register and Enqueue Ajax Search JS | |
wp_register_script( 'nikcree-ajax-search', get_stylesheet_directory_uri() . '/js/ajax-search.js', array('jquery'), false, false ); | |
wp_enqueue_script( 'nikcree-ajax-search' ); | |
/* AJAX SEARCH */ | |
function search_form( $form ) { | |
$search_params = array( | |
'placeholder' => __( 'Search', 'nikcree' ), | |
'search_id' => 's', | |
'form_action' => home_url( '/' ), | |
'ajax_disable' => false | |
); | |
$value = ''; | |
if ( ! empty( $_GET['s'] ) ) | |
$value = get_search_query(); | |
$form = '<form action="' . $search_params['form_action'] . '" id="searchform" method="get">'; | |
$form .= ' <div>'; | |
$form .= ' <input type="submit" value="" id="searchsubmit" class="button" />'; | |
$form .= ' <input type="text" id="s" name="' . $search_params['search_id'] . '" value="' . $value . '" placeholder=\'' . $search_params['placeholder'] . '\' />'; | |
$form .= ' </div>'; | |
$form .= '</form>'; | |
return $form; | |
} | |
//first append search item to main menu | |
add_filter( 'wp_nav_menu_items', 'append_search_nav', 10, 2 ); | |
function append_search_nav ( $items, $args ) { | |
if ( ( is_object( $args ) && $args->theme_location == 'primary' ) ) { | |
add_filter( 'genesis_search_form', 'search_form' ); | |
$form = get_search_form( false ); | |
remove_filter( 'genesis_search_form', 'search_form' ); | |
$items .= '<li id="menu-item-mobile-search" class="mobile-nav menu-item menu-item-mobile-search"> | |
' . get_search_form( false ) . ' | |
</li>'; | |
$items .= '<li id="menu-item-search" class="menu-item menu-item-search-dropdown"> | |
<a class="search-icon" href="?s=" rel="nofollow" data-search-tooltip="' . htmlspecialchars( $form ) . '"><span class="hidden_link_text">' . __( 'Search' , 'nikcree' ) . '</span></a> | |
</li>'; | |
} | |
return $items; | |
} | |
// now hook into wordpress ajax function to catch any ajax requests | |
add_action( 'wp_ajax_ajax_search', 'ajax_search' ); | |
add_action( 'wp_ajax_nopriv_ajax_search', 'ajax_search' ); | |
function ajax_search() | |
{ | |
unset($_REQUEST['action']); | |
if(empty($_REQUEST['s'])) $_REQUEST['s'] = array_shift(array_values($_REQUEST)); | |
if(empty($_REQUEST['s'])) die(); | |
$defaults = array('numberposts' => 5, 'post_type' => 'any', 'post_status' => 'publish', 'post_password' => '', 'suppress_filters' => false); | |
$_REQUEST['s'] = apply_filters( 'get_search_query', $_REQUEST['s']); | |
$search_parameters = array_merge($defaults, $_REQUEST); | |
$search_query = apply_filters('avf_ajax_search_query', http_build_query($search_parameters)); | |
$query_function = apply_filters('avf_ajax_search_function', 'get_posts', $search_query, $search_parameters, $defaults); | |
$posts = (($query_function == 'get_posts') || !function_exists($query_function)) ? get_posts($search_query) : $query_function($search_query, $search_parameters, $defaults); | |
$search_messages = array( | |
'no_criteria_matched' => __("Sorry, no posts matched your criteria", 'nikcree'), | |
'another_search_term' => __("Please try another search term", 'nikcree'), | |
'time_format' => get_option('date_format'), | |
'all_results_query' => http_build_query($_REQUEST), | |
'all_results_link' => home_url('?' . http_build_query($_REQUEST)), | |
'view_all_results' => __( 'View all results', 'nikcree' ) | |
); | |
$search_messages = apply_filters('avf_ajax_search_messages', $search_messages, $search_query); | |
if(empty($posts)) | |
{ | |
$output = "<span class='ajax_search_entry ajax_not_found'>"; | |
$output .= "<span class='ajax_search_image without_image info'>"; | |
$output .= "</span>"; | |
$output .= "<span class='ajax_search_content'>"; | |
$output .= " <span class='ajax_search_title'>"; | |
$output .= $search_messages['no_criteria_matched']; | |
$output .= " </span>"; | |
$output .= " <span class='ajax_search_excerpt'>"; | |
$output .= $search_messages['another_search_term']; | |
$output .= " </span>"; | |
$output .= "</span>"; | |
$output .= "</span>"; | |
echo $output; | |
die(); | |
} | |
//if we got posts resort them by post type | |
$output = ""; | |
$sorted = array(); | |
$post_type_obj = array(); | |
foreach($posts as $post) | |
{ | |
$sorted[$post->post_type][] = $post; | |
if(empty($post_type_obj[$post->post_type])) | |
{ | |
$post_type_obj[$post->post_type] = get_post_type_object($post->post_type); | |
} | |
} | |
//now we got everything we need to preapre the output | |
foreach($sorted as $key => $post_type) | |
{ | |
if(isset($post_type_obj[$key]->labels->name)) | |
{ | |
$label = apply_filters('avf_ajax_search_label_names', $post_type_obj[$key]->labels->name); | |
$output .= "<h4>".$label."</h4>"; | |
} | |
else | |
{ | |
$output .= "<hr />"; | |
} | |
foreach($post_type as $post) | |
{ | |
$image = get_the_post_thumbnail( $post->ID, 'thumbnail' ); | |
$extra_class = $image ? "with_image" : "without_image"; | |
$post_type = $image ? "" : get_post_format($post->ID) != "" ? get_post_format($post->ID) : "standard"; | |
$excerpt = ""; | |
if(!empty($post->post_excerpt)) | |
{ | |
/** | |
* This function shortens a string | |
*/ | |
$string = $post->post_excerpt; | |
$limit = 70; | |
$break = " "; | |
$pad = "..."; | |
$stripClean = true; | |
$excludetags = ''; | |
$safe_truncate = true; | |
if($stripClean) | |
{ | |
$string = strip_shortcodes(strip_tags($string, $excludetags)); | |
} | |
if(strlen($string) <= $limit) return $string; | |
if(false !== ($breakpoint = strpos($string, $break, $limit))) | |
{ | |
if($breakpoint < strlen($string) - 1) | |
{ | |
if($safe_truncate || is_rtl()) | |
{ | |
$string = mb_strimwidth($string, 0, $breakpoint) . $pad; | |
} | |
else | |
{ | |
$string = substr($string, 0, $breakpoint) . $pad; | |
} | |
} | |
} | |
// if there is no breakpoint an no tags we could accidentaly split split inside a word | |
if(!$breakpoint && strlen(strip_tags($string)) == strlen($string)) | |
{ | |
if($safe_truncate || is_rtl()) | |
{ | |
$string = mb_strimwidth($string, 0, $limit) . $pad; | |
} | |
else | |
{ | |
$string = substr($string, 0, $limit) . $pad; | |
} | |
} | |
$excerpt = apply_filters( 'avf_ajax_search_excerpt', $string ); | |
} | |
else | |
{ | |
$excerpt = get_the_time($search_messages['time_format'], $post->ID); | |
} | |
$link = apply_filters('av_custom_url', get_permalink($post->ID), $post); | |
$output .= "<a class ='ajax_search_entry {$extra_class}' href='".$link."'>"; | |
$output .= "<span class='ajax_search_image {$extra_class} {$post_type}'>"; | |
$output .= $image; | |
$output .= "</span>"; | |
$output .= "<span class='ajax_search_content'>"; | |
$output .= " <span class='ajax_search_title'>"; | |
$output .= get_the_title($post->ID); | |
$output .= " </span>"; | |
$output .= " <span class='ajax_search_excerpt'>"; | |
$output .= $excerpt; | |
$output .= " </span>"; | |
$output .= "</span>"; | |
$output .= "</a>"; | |
} | |
} | |
$output .= "<a class='ajax_search_entry ajax_search_entry_view_all' href='".$search_messages['all_results_link']."'>".$search_messages['view_all_results']."</a>"; | |
echo $output; | |
die(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment