Ryan Alcorn
Ryan Alcorn

Reputation: 39

Search bar with jquery dropdown showing results

When the user types something into the search bar I would like the results bellow to link to a page when you click it instead of filling the search bar with whatever you clicked. I'm very new to Jquery and I found this tutorial online but it isn't doing exactly what I wanted it to.

Index.php (Just showing some of the head and the body)

<script>
$(document).ready(function(){
 $("#tag").autocomplete("autocomplete.php", {
        selectFirst: true
    });
});
</script>
</head>

<body>
    <label>Tag:</label>
    <input name="tag" type="text" id="tag" size="20" style="width:541px; height:23px; font-size:16px; text-indent:5px;" placeholder="Search foods, shopping lists, meal plans and recipes" />
</body>

autocomplete.php (Even with the and tags I was unable to achieve the linking to another page.)

<?php
$q=$_GET['q'];
$my_data=mysql_real_escape_string($q);
include ("connect.php");
$sql="SELECT id, name, description, foodgroup FROM foods WHERE name LIKE '%$my_data%' ORDER BY name";
$result = mysql_query($sql);

if($result) {
    while($row=mysql_fetch_array($result)) {
        print "<a href='food.php?foodGroup=" . $row['foodgroup'] . "&name=" . $row['name'] . "&desc=" . $row['description'] . "&foodID=" . $row['id'] . "'><div id='resultContainerDiv'><span id='resultText'>" . $row['name'] . " - " . $row['description'] . "</span></div></a>\n";
    }
}
?>

lastly, jquery.autocomplete.js

/*  * jQuery Autocomplete plugin 1.1  *  * Copyright (c) 2009 Jörn Zaefferer  *  * Dual licensed under the MIT and GPL licenses:  *   http://www.opensource.org/licenses/mit-license.php  *   http://www.gnu.org/licenses/gpl.html  *  * Revision: $Id: jquery.autocomplete.js 15 2009-08-22 10:30:27Z joern.zaefferer $  */

;(function($) {      $.fn.extend({  autocomplete: function(urlOrData, options) {        var isUrl = typeof urlOrData == "string";       options = $.extend({}, $.Autocompleter.defaults, {          url: isUrl ? urlOrData : null,          data: isUrl ? null : urlOrData,             delay: isUrl ? $.Autocompleter.defaults.delay : 10,             max: options && !options.scroll ? 10 : 150      }, options);
                // if highlight is set to false, replace it with a do-nothing function      options.highlight = options.highlight || function(value) { return value; };
                // if the formatMatch option is not specified, then use formatItem for backwards compatibility      options.formatMatch = options.formatMatch || options.formatItem;
                return this.each(function() {           new $.Autocompleter(this, options);         });     },  result: function(handler) {         return this.bind("result", handler);    },  search: function(handler) {         return this.trigger("search", [handler]);   },  flushCache: function() {        return this.trigger("flushCache");  },  setOptions: function(options){      return this.trigger("setOptions", [options]);   },  unautocomplete: function() {        return this.trigger("unautocomplete");  } });

$.Autocompleter = function(input, options) {

    var KEY = {         UP: 38,         DOWN: 40,       DEL: 46,        TAB: 9,         RETURN: 13,         ESC: 27,        COMMA: 188,         PAGEUP: 33,         PAGEDOWN: 34,       BACKSPACE: 8    };

    // Create $ object for input element    var $input = $(input).attr("autocomplete", "off").addClass(options.inputClass);

    var timeout;    var previousValue = "";     var cache = $.Autocompleter.Cache(options);     var hasFocus = 0;   var lastKeyPressCode;   var config = {      mouseDownOnSelect: false    };  var select = $.Autocompleter.Select(options, input, selectCurrent, config);         var blockSubmit;        // prevent form submit in opera when selecting with return key  $.browser.opera && $(input.form).bind("submit.autocomplete", function() {       if (blockSubmit) {          blockSubmit = false;            return false;       }   });         // only opera doesn't trigger keydown multiple times while pressed, others don't work with keypress at all  $input.bind(($.browser.opera ? "keypress" : "keydown") + ".autocomplete", function(event) {         // a keypress means the input has focus         // avoids issue where input had focus before the autocomplete was applied       hasFocus = 1;       // track last key pressed       lastKeyPressCode = event.keyCode;       switch(event.keyCode) {
                    case KEY.UP:
                event.preventDefault();
                if ( select.visible() ) {
                    select.prev();
                } else {
                    onChange(0, true);
                }
                break;
                            case KEY.DOWN:
                event.preventDefault();
                if ( select.visible() ) {
                    select.next();
                } else {
                    onChange(0, true);
                }
                break;
                            case KEY.PAGEUP:
                event.preventDefault();
                if ( select.visible() ) {
                    select.pageUp();
                } else {
                    onChange(0, true);
                }
                break;
                            case KEY.PAGEDOWN:
                event.preventDefault();
                if ( select.visible() ) {
                    select.pageDown();
                } else {
                    onChange(0, true);
                }
                break;
                        // matches also semicolon           case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA:             case KEY.TAB:           case KEY.RETURN:
                if( selectCurrent() ) {
                    // stop default to prevent a form submit, Opera needs special handling
                    event.preventDefault();
                    blockSubmit = true;
                    return false;
                }
                break;
                            case KEY.ESC:
                select.hide();
                break;
                            default:
                clearTimeout(timeout);
                timeout = setTimeout(onChange, options.delay);
                break;      }   }).focus(function(){        // track whether the field has focus, we shouldn't process any      // results if the field no longer has focus         hasFocus++;     }).blur(function() {        hasFocus = 0;       if (!config.mouseDownOnSelect) {            hideResults();      }   }).click(function() {       // show select when clicking in a focused field         if ( hasFocus++ > 1 && !select.visible() ) {            onChange(0, true);      }   }).bind("search", function() {      // TODO why not just specifying both arguments?         var fn = (arguments.length > 1) ? arguments[1] : null;      function findValueCallback(q, data) {           var result;             if( data && data.length ) {
                for (var i=0; i < data.length; i++) {
                    if( data[i].result.toLowerCase() == q.toLowerCase() ) {
                        result = data[i];
                        break;
                    }
                }           }           if( typeof fn == "function" ) fn(result);           else $input.trigger("result", result && [result.data, result.value]);       }       $.each(trimWords($input.val()), function(i, value) {            request(value, findValueCallback, findValueCallback);       });     }).bind("flushCache", function() {      cache.flush();  }).bind("setOptions", function() {      $.extend(options, arguments[1]);        // if we've updated the data, repopulate        if ( "data" in arguments[1] )           cache.populate();   }).bind("unautocomplete", function() {      select.unbind();        $input.unbind();        $(input.form).unbind(".autocomplete");  });
            function selectCurrent() {      var selected = select.selected();       if( !selected )             return false;
                var v = selected.result;        previousValue = v;
                if ( options.multiple ) {           var words = trimWords($input.val());            if ( words.length > 1 ) {
                var seperator = options.multipleSeparator.length;
                var cursorAt = $(input).selection().start;
                var wordAt, progress = 0;
                $.each(words, function(i, word) {
                    progress += word.length;
                    if (cursorAt <= progress) {
                        wordAt = i;
                        return false;
                    }
                    progress += seperator;
                });
                words[wordAt] = v;
                // TODO this should set the cursor to the right position, but it gets overriden somewhere
                //$.Autocompleter.Selection(input, progress + seperator, progress + seperator);
                v = words.join( options.multipleSeparator );            }           v += options.multipleSeparator;         }
                $input.val(v);      hideResultsNow();       $input.trigger("result", [selected.data, selected.value]);      return true;    }       function onChange(crap, skipPrevCheck) {        if( lastKeyPressCode == KEY.DEL ) {             select.hide();          return;         }
                var currentValue = $input.val();
                if ( !skipPrevCheck && currentValue == previousValue )          return;
                previousValue = currentValue;
                currentValue = lastWord(currentValue);      if ( currentValue.length >= options.minChars) {             $input.addClass(options.loadingClass);          if (!options.matchCase)
                currentValue = currentValue.toLowerCase();          request(currentValue, receiveData, hideResultsNow);         } else {            stopLoading();          select.hide();      }   };      function trimWords(value) {         if (!value)             return [""];        if (!options.multiple)          return [$.trim(value)];         return $.map(value.split(options.multipleSeparator), function(word) {           return $.trim(value).length ? $.trim(word) : null;      });     }       function lastWord(value) {      if ( !options.multiple )            return value;       var words = trimWords(value);       if (words.length == 1)              return words[0];        var cursorAt = $(input).selection().start;      if (cursorAt == value.length) {             words = trimWords(value)        } else {            words = trimWords(value.replace(value.substring(cursorAt), ""));        }       return words[words.length - 1];     }       // fills in the input box w/the first match (assumed to be the best match)  // q: the term entered  // sValue: the first matching result    function autoFill(q, sValue){       // autofill in the complete box w/the first match as long as the user hasn't entered in more data       // if the last user key pressed was backspace, don't autofill       if( options.autoFill && (lastWord($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) {          // fill in the value (keep the case the user has typed)             $input.val($input.val() + sValue.substring(lastWord(previousValue).length));            // select the portion of the value not typed by the user (so the next character will erase)             $(input).selection(previousValue.length, previousValue.length + sValue.length);         }   };

    function hideResults() {        clearTimeout(timeout);      timeout = setTimeout(hideResultsNow, 200);  };

    function hideResultsNow() {         var wasVisible = select.visible();      select.hide();      clearTimeout(timeout);      stopLoading();      if (options.mustMatch) {            // call search and run callback             $input.search(
                function (result){
                    // if no value found, clear the input box
                    if( !result ) {
                        if (options.multiple) {
                            var words = trimWords($input.val()).slice(0, -1);
                            $input.val( words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "") );
                        }
                        else {
                            $input.val( "" );
                            $input.trigger("result", null);
                        }
                    }
                }           );      }   };

    function receiveData(q, data) {         if ( data && data.length && hasFocus ) {            stopLoading();          select.display(data, q);            autoFill(q, data[0].value);             select.show();      } else {            hideResultsNow();       }   };

    function request(term, success, failure) {      if (!options.matchCase)             term = term.toLowerCase();      var data = cache.load(term);        // recieve the cached data      if (data && data.length) {          success(term, data);        // if an AJAX url has been supplied, try loading the data now       } else if( (typeof options.url == "string") && (options.url.length > 0) ){
                        var extraParams = {
                timestamp: +new Date()          };          $.each(options.extraParams, function(key, param) {
                extraParams[key] = typeof param == "function" ? param() : param;            });
                        $.ajax({
                // try to leverage ajaxQueue plugin to abort previous requests
                mode: "abort",
                // limit abortion to this input
                port: "autocomplete" + input.name,
                dataType: options.dataType,
                url: options.url,
                data: $.extend({
                    q: lastWord(term),
                    limit: options.max
                }, extraParams),
                success: function(data) {
                    var parsed = options.parse && options.parse(data) || parse(data);
                    cache.add(term, parsed);
                    success(term, parsed);
                }           });         } else {            // if we have a failure, we need to empty the list -- this prevents the the [TAB] key from selecting the last successful match          select.emptyList();             failure(term);      }   };      function parse(data) {      var parsed = [];        var rows = data.split("\n");        for (var i=0; i < rows.length; i++) {           var row = $.trim(rows[i]);          if (row) {
                row = row.split("|");
                parsed[parsed.length] = {
                    data: row,
                    value: row[0],
                    result: options.formatResult && options.formatResult(row, row[0]) || row[0]
                };          }       }       return parsed;  };

    function stopLoading() {        $input.removeClass(options.loadingClass);   };

};

$.Autocompleter.defaults = {    inputClass: "ac_input",     resultsClass: "ac_results",     loadingClass: "ac_loading",     minChars: 1,    delay: 400,     matchCase: false,   matchSubset: true,  matchContains: false,   cacheLength: 10,    max: 100,   mustMatch: false,   extraParams: {},    selectFirst: true,  formatItem: function(row) { return row[0]; },   formatMatch: null,  autoFill: false,    width: 0,   multiple: false,    multipleSeparator: ", ",    highlight: function(value, term) {      return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");  },
    scroll: true,
    scrollHeight: 180 };

$.Autocompleter.Cache = function(options) {

    var data = {};  var length = 0;         function matchSubset(s, sub) {      if (!options.matchCase)             s = s.toLowerCase();        var i = s.indexOf(sub);         if (options.matchContains == "word"){           i = s.toLowerCase().search("\\b" + sub.toLowerCase());      }       if (i == -1) return false;      return i == 0 || options.matchContains;     };      function add(q, value) {        if (length > options.cacheLength){          flush();        }       if (!data[q]){              length++;       }       data[q] = value;    }       function populate(){        if( !options.data ) return false;       // track the matches        var stMatchSets = {},           nullData = 0;

        // no url was specified, we need to adjust the cache length to make sure it fits the local data store       if( !options.url ) options.cacheLength = 1;
                // track all options for minChars = 0       stMatchSets[""] = [];
                // loop through the array and create a lookup structure         for ( var i = 0, ol = options.data.length; i < ol; i++ ) {          var rawValue = options.data[i];             // if rawValue is a string, make an array otherwise just reference the array            rawValue = (typeof rawValue == "string") ? [rawValue] : rawValue;
                        var value = options.formatMatch(rawValue, i+1, options.data.length);            if ( value === false )
                continue;
                            var firstChar = value.charAt(0).toLowerCase();          // if no lookup array for this character exists, look it up now             if( !stMatchSets[firstChar] ) 
                stMatchSets[firstChar] = [];

            // if the match is a string             var row = {
                value: value,
                data: rawValue,
                result: options.formatResult && options.formatResult(rawValue) || value             };
                        // push the current match into the set list             stMatchSets[firstChar].push(row);

            // keep track of minChars zero items            if ( nullData++ < options.max ) {
                stMatchSets[""].push(row);          }       };

        // add the data items to the cache      $.each(stMatchSets, function(i, value) {            // increase the cache size          options.cacheLength++;          // add to the cache             add(i, value);      });     }       // populate any existing data   setTimeout(populate, 25);       function flush(){       data = {};      length = 0;     }       return {        flush: flush,       add: add,       populate: populate,         load: function(q) {             if (!options.cacheLength || !length)
                return null;            /* 
             * if dealing w/local data and matchContains than we must make sure
             * to loop through all the data collections looking for matches
             */             if( !options.url && options.matchContains ){
                // track all matches
                var csub = [];
                // loop through all the data grids for matches
                for( var k in data ){
                    // don't search through the stMatchSets[""] (minChars: 0) cache
                    // this prevents duplicates
                    if( k.length > 0 ){
                        var c = data[k];
                        $.each(c, function(i, x) {
                            // if we've got a match, add it to the array
                            if (matchSubset(x.value, q)) {
                                csub.push(x);
                            }
                        });
                    }
                }               
                return csub;            } else              // if the exact item exists, use it             if (data[q]){
                return data[q];             } else          if (options.matchSubset) {
                for (var i = q.length - 1; i >= options.minChars; i--) {
                    var c = data[q.substr(0, i)];
                    if (c) {
                        var csub = [];
                        $.each(c, function(i, x) {
                            if (matchSubset(x.value, q)) {
                                csub[csub.length] = x;
                            }
                        });
                        return csub;
                    }
                }           }           return null;        }   }; };

$.Autocompleter.Select = function (options, input, select, config) {    var CLASSES = {         ACTIVE: "ac_over"   };      var listItems,      active =
-1,         data,       term = "",      needsInit = true,       element,        list;       // Create results   function init() {       if (!needsInit)             return;         element = $("<div/>")       .hide()         .addClass(options.resultsClass)         .css("position", "absolute")        .appendTo(document.body);
            list = $("<ul/>").appendTo(element).mouseover( function(event) {            if(target(event).nodeName && target(event).nodeName.toUpperCase()
== 'LI') {
                active = $("li", list).removeClass(CLASSES.ACTIVE).index(target(event));
                $(target(event)).addClass(CLASSES.ACTIVE);            
            }       }).click(function(event) {          $(target(event)).addClass(CLASSES.ACTIVE);          select();           // TODO provide option to avoid setting focus again after selection? useful for cleanup-on-focus            input.focus();          return false;       }).mousedown(function() {           config.mouseDownOnSelect = true;        }).mouseup(function() {             config.mouseDownOnSelect = false;       });
                if( options.width > 0 )             element.css("width", options.width);
                    needsInit = false;  }       function target(event) {        var element = event.target;         while(element && element.tagName != "LI")           element = element.parentNode;       // more fun with IE, sometimes event.target is empty, just ignore it then       if(!element)            return [];      return element;     }

    function moveSelect(step) {         listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE);        movePosition(step);
        var activeItem = listItems.slice(active, active + 1).addClass(CLASSES.ACTIVE);
        if(options.scroll) {
            var offset = 0;
            listItems.slice(0, active).each(function() {
                offset += this.offsetHeight;            });
            if((offset + activeItem[0].offsetHeight - list.scrollTop()) > list[0].clientHeight) {
                list.scrollTop(offset + activeItem[0].offsetHeight - list.innerHeight());
            } else if(offset < list.scrollTop()) {
                list.scrollTop(offset);
            }
        }   };      function movePosition(step) {       active += step;         if (active < 0) {           active = listItems.size() - 1;      } else if (active
>= listItems.size()) {          active = 0;         }   }       function limitNumberOfItems(available) {        return options.max && options.max < available           ? options.max           : available;    }       function fillList() {       list.empty();       var max = limitNumberOfItems(data.length);      for (var i=0; i < max; i++) {           if (!data[i])
                continue;           var formatted = options.formatItem(data[i].data, i+1, max, data[i].value, term);            if ( formatted === false )
                continue;           var li = $("<li/>").html( options.highlight(formatted, term) ).addClass(i%2 == 0 ? "ac_even" : "ac_odd").appendTo(list)[0];             $.data(li, "ac_data", data[i]);         }       listItems = list.find("li");        if ( options.selectFirst ) {            listItems.slice(0, 1).addClass(CLASSES.ACTIVE);             active = 0;         }       // apply bgiframe if available      if ( $.fn.bgiframe )            list.bgiframe();    }       return {        display: function(d, q) {           init();             data = d;           term = q;           fillList();         },      next: function() {          moveSelect(1);      },      prev: function() {          moveSelect(-1);         },      pageUp: function() {            if (active != 0 && active - 8 < 0) {
                moveSelect( -active );          } else {
                moveSelect(-8);             }       },      pageDown: function() {          if (active != listItems.size() - 1 && active + 8 > listItems.size()) {
                moveSelect( listItems.size() - 1 - active );            } else {
                moveSelect(8);          }       },      hide: function() {          element && element.hide();          listItems && listItems.removeClass(CLASSES.ACTIVE);             active = -1;        },      visible : function() {          return element && element.is(":visible");       },      current: function() {           return this.visible() && (listItems.filter("." + CLASSES.ACTIVE)[0] || options.selectFirst && listItems[0]);        },      show: function() {          var offset = $(input).offset();             element.css({
                width: typeof options.width == "string" || options.width > 0 ? options.width : $(input).width(),
                top: offset.top + input.offsetHeight,
                left: offset.left           }).show();
            if(options.scroll) {
                list.scrollTop(0);
                list.css({
                    maxHeight: options.scrollHeight,
                    overflow: 'auto'
                });

                if($.browser.msie && typeof document.body.style.maxHeight === "undefined") {
                    var listHeight = 0;
                    listItems.each(function() {
                        listHeight += this.offsetHeight;
                    });
                    var scrollbarsVisible = listHeight > options.scrollHeight;
                    list.css('height', scrollbarsVisible ? options.scrollHeight : listHeight );
                    if (!scrollbarsVisible) {
                        // IE doesn't recalculate width when scrollbar disappears
                        listItems.width( list.width() - parseInt(listItems.css("padding-left")) - parseInt(listItems.css("padding-right")) );
                    }
                }

            }       },      selected: function() {          var selected = listItems && listItems.filter("." + CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE);             return selected && selected.length && $.data(selected[0], "ac_data");       },      emptyList: function (){             list && list.empty();       },      unbind: function() {            element && element.remove();        }   }; };

$.fn.selection = function(start, end) {     if (start !== undefined) {      return this.each(function() {           if( this.createTextRange ){
                var selRange = this.createTextRange();
                if (end === undefined || start == end) {
                    selRange.move("character", start);
                    selRange.select();
                } else {
                    selRange.collapse(true);
                    selRange.moveStart("character", start);
                    selRange.moveEnd("character", end);
                    selRange.select();
                }           } else if( this.setSelectionRange ){
                this.setSelectionRange(start, end);             } else if( this.selectionStart ){
                this.selectionStart = start;
                this.selectionEnd = end;            }       });     }   var field = this[0];    if ( field.createTextRange ) {      var range = document.selection.createRange(),           orig = field.value,             teststring
= "<->",            textLength = range.text.length;         range.text = teststring;        var caretAt = field.value.indexOf(teststring);      field.value = orig;         this.selection(caretAt, caretAt + textLength);      return {            start: caretAt,             end: caretAt + textLength       }   } else if( field.selectionStart !== undefined ){        return {            start: field.selectionStart,            end: field.selectionEnd         }   } };

})(jQuery);

Thank you,

Ryan

Upvotes: 0

Views: 1053

Answers (1)

jeroen
jeroen

Reputation: 91734

In the search script you have an error:

<?php
$q=$_GET['q'];
$my_data=mysql_real_escape_string($q);
include ("connect.php");

mysql_real_escape_string will return false (the equivalent of an empty string) if there is no database connection yet so you are effectively emptying your search string.

You need to switch that around:

<?php
$q=$_GET['q'];
include ("connect.php");
$my_data=mysql_real_escape_string($q);

You should also add error handling to your database calls and move to PDO or mysqli if possible as the mysql_* functions are deprecated.

Upvotes: 1

Related Questions