Sunday, December 28, 2014

Typeahead.js Autocomplete Suggestion + Bloodhount Remote Data - Tutorial and Demo

Typeahead.js improved version includes Bloodhound suggestion engine which handles filtering and sorting things for you. 

Typeahead Js is similar to google, facebook, twitter, gmail auto suggestion. It completes the full string when you type.

You need

  • JQuery
  • Typeahead JS Plugin
  • BloodHound Suggestion Engine (Part of Typeahead Project)


To initiate Typeahead on any input use $("ELEMENTSELECTOR").typeahead();

Typeahead has following notable options. read all here 

 hint  - If  true  shows the Hint as you Type.

typeahead Hint shown
typeahead autocompletes the word when you type - Hint

 highlight  -  If  true  highlights the options (Bold Text).


Typeahead Highlight Option explained
Highlight Object of Typeahead
If you want to have more control over the result set displayed as suggestions, use templates object.
template object with Typeahead JS
Templates Object with Suggestion function

Check out the working code
 
<!-- Bootstrap core CSS OPTIONAL -->
<link href="http://getbootstrap.com/dist/css/bootstrap.min.css" rel="stylesheet">
 <!-- JavaScript
 ================================================== -->
 <!-- Placed at the end of the document so the pages load faster -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="http://getbootstrap.com/dist/js/bootstrap.min.js"></script>
<script src="js/typeahead.bundle.js"></script>
<script src="js/bloodhound.js"></script>

Javascript Array of Country List

var country_list = ["Afghanistan","Albania","Algeria","Andorra","Angola","Anguilla","Antigua &amp; Barbuda","Argentina","Armenia","Aruba","Australia","Austria","Azerbaijan","Bahamas","Bahrain","Bangladesh","Barbados","Belarus","Belgium","Belize","Benin","Bermuda","Bhutan","Bolivia","Bosnia &amp; Herzegovina","Botswana","Brazil","British Virgin Islands","Brunei","Bulgaria","Burkina Faso","Burundi","Cambodia","Cameroon","Cape Verde","Cayman Islands","Chad","Chile","China","Colombia","Congo","Cook Islands","Costa Rica","Cote D Ivoire","Croatia","Cruise Ship","Cuba","Cyprus","Czech Republic","Denmark","Djibouti","Dominica","Dominican Republic","Ecuador","Egypt","El Salvador","Equatorial Guinea","Estonia","Ethiopia","Falkland Islands","Faroe Islands","Fiji","Finland","France","French Polynesia","French West Indies","Gabon","Gambia","Georgia","Germany","Ghana","Gibraltar","Greece","Greenland","Grenada","Guam","Guatemala","Guernsey","Guinea","Guinea Bissau","Guyana","Haiti","Honduras","Hong Kong","Hungary","Iceland","India","Indonesia","Iran","Iraq","Ireland","Isle of Man","Israel","Italy","Jamaica","Japan","Jersey","Jordan","Kazakhstan","Kenya","Kuwait","Kyrgyz Republic","Laos","Latvia","Lebanon","Lesotho","Liberia","Libya","Liechtenstein","Lithuania","Luxembourg","Macau","Macedonia","Madagascar","Malawi","Malaysia","Maldives","Mali","Malta","Mauritania","Mauritius","Mexico","Moldova","Monaco","Mongolia","Montenegro","Montserrat","Morocco","Mozambique","Namibia","Nepal","Netherlands","Netherlands Antilles","New Caledonia","New Zealand","Nicaragua","Niger","Nigeria","Norway","Oman","Pakistan","Palestine","Panama","Papua New Guinea","Paraguay","Peru","Philippines","Poland","Portugal","Puerto Rico","Qatar","Reunion","Romania","Russia","Rwanda","Saint Pierre &amp; Miquelon","Samoa","San Marino","Satellite","Saudi Arabia","Senegal","Serbia","Seychelles","Sierra Leone","Singapore","Slovakia","Slovenia","South Africa","South Korea","Spain","Sri Lanka","St Kitts &amp; Nevis","St Lucia","St Vincent","St. Lucia","Sudan","Suriname","Swaziland","Sweden","Switzerland","Syria","Taiwan","Tajikistan","Tanzania","Thailand","Timor L'Este","Togo","Tonga","Trinidad &amp; Tobago","Tunisia","Turkey","Turkmenistan","Turks &amp; Caicos","Uganda","Ukraine","United Arab Emirates","United Kingdom","Uruguay","Uzbekistan","Venezuela","Vietnam","Virgin Islands (US)","Yemen","Zambia","Zimbabwe"];

Javascript for Typeahead with Local Javascript Array

           $(document).ready(function() {

        // Set the Options for "Bloodhound" Engine
                var my_Suggestion_class = new Bloodhound({
                    limit: 10,
                    datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
                    queryTokenizer: Bloodhound.tokenizers.whitespace,
                    local: $.map(country_list, function(item) {
                        //console.log(item);
                        return {value: item};
                    })
                });

        //Initialize the Suggestion Engine
                my_Suggestion_class.initialize();

                var typeahead_elem = $('.typeahead');
                typeahead_elem.typeahead({
                    hint: true,
                    highlight: true,
                    minLength: 1
                },
                {
                    name: 'value',
                    displayKey: 'value',
                    source: my_Suggestion_class.ttAdapter(),
                    templates: {
                        empty: [
                            '<div class="noitems">',
                            'No Items Found',
                            '</div>'
                        ].join('\n')
                    }
                });
            });

Typeahead need array of JSON object to show the suggestion. 

{ "identifier" : "itemvalue}{ "identifier" : "itemvalue} ]

We have prepared above structured array from JQuery map function
                    local: $.map(country_list, function(item) {
                        return {value: item};
                    })

    datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
    queryTokenizer: Bloodhound.tokenizers.whitespace,
The above two Tokenizer lines may confuse every one. datumTokenizer needs the array of JSON as shown here

{ "identifier" : "itemvalue}{ "identifier" : "itemvalue} ]

If your array or JSON data structure is same as above then 

datumTokenizer: Bloodhound.tokenizers.obj.whitespace('identifier')

Typeahead shows plain string without any styling if you dont customize the response. You can modifiy the following style as you like.

Stlying the Suggestions

<style>
.typeahead,
.tt-query,
.tt-hint {
  width: 396px;
  height: 30px;
  padding: 8px 12px;
  font-size: 24px;
  line-height: 30px;
  border: 2px solid #ccc;
  -webkit-border-radius: 8px;
     -moz-border-radius: 8px;
          border-radius: 8px;
  outline: none;
}

.typeahead {
  background-color: #fff;
}

.typeahead:focus {
  border: 2px solid #0097cf;
}

.tt-query {
  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
     -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
}

.tt-hint {
  color: #999
}

.tt-dropdown-menu {
  width: 422px;
  margin-top: 12px;
  padding: 8px 0;
  background-color: #fff;
  border: 1px solid #ccc;
  border: 1px solid rgba(0, 0, 0, 0.2);
  -webkit-border-radius: 8px;
     -moz-border-radius: 8px;
          border-radius: 8px;
  -webkit-box-shadow: 0 5px 10px rgba(0,0,0,.2);
     -moz-box-shadow: 0 5px 10px rgba(0,0,0,.2);
          box-shadow: 0 5px 10px rgba(0,0,0,.2);
}

.tt-suggestion {
  padding: 3px 20px;
  font-size: 18px;
  line-height: 24px;
}

.tt-suggestion.tt-cursor {
  color: #fff;
  background-color: #0097cf;

}

.tt-suggestion p {
  margin: 0;
}
tt-highlight{
    color:red;
}
</style>

Output of Typeahead in browser

To modify the result displayed in suggestion you need to add templates object with typeahead.

It has four options
empty - If no result found return this custom text / style.
suggestion - Adding HTML Elements and customizing the result suggestion shown when you type.
header - To add header data for the suggestion list shown
footer - Similar to header, add footer text to result set shown in screen.

Typeahead JS with Bloodhound Remote and Prefetch


            $(document).ready(function() {
        //Set up "Bloodhound" Options 
                var my_Suggestion_class = new Bloodhound({
                    datumTokenizer: Bloodhound.tokenizers.obj.whitespace('vval'),
                    queryTokenizer: Bloodhound.tokenizers.whitespace,
                    prefetch: {
                        url: "countries.json",
                        filter: function(countryArray) {
                            return $.map(countryArray, function(country) {
                                return {vval: country.name};
                            });
                            //return catAR;
                        }
                    },
                    remote: {
                        url: "s.php?s=%QUERY",
                        filter: function(x) {
                            return $.map(x, function(item) {
                                return {vval: item.name};
                            });
                        },
                        wildcard: "%QUERY"
                    }
                    //http://code.educationgovt.com/typeaheadjs/countries.json
                });

                // Initialize Typeahead with Parameters
                my_Suggestion_class.initialize();
                var typeahead_elem = $('.typeahead');
                typeahead_elem.typeahead({
                    hint: false,
                    highlight: true,
                    minLength: 1
                },
                {
                    // `ttAdapter` wraps the suggestion engine in an adapter that
                    // is compatible with the typeahead jQuery plugin
                    name: 'vval',
                    displayKey: 'vval',
                    source: my_Suggestion_class.ttAdapter()
                });

                //Get the Typeahead Value on Following Events
                $('input').on([
                    'typeahead:initialized',
                    'typeahead:initialized:err',
                    'typeahead:selected',
                    'typeahead:autocompleted',
                    'typeahead:cursorchanged',
                    'typeahead:opened',
                    'typeahead:closed'
                ].join(' '), function(x) {
                    //console.log(this.value); 
                });
            });

How to get typeahead Selected input value 

Following events applicable on typeahead input element. I have given the events with complete example below
                $('input').on([
                    'typeahead:initialized',
                    'typeahead:initialized:err',
                    'typeahead:selected',
                    'typeahead:autocompleted',
                    'typeahead:cursorchanged',
                    'typeahead:opened',
                    'typeahead:closed'
                ].join(' '), function(x) {
                    console.log(this.value); 
                });

Prefetch Data will be stored in localstorage

When the page is loaded the response from prefetch will be stored in localstorage.

Fetching Remote JSON Data in suggestion

Check the Demo

<script type='text/javascript'>
var image_url = "http://image.tmdb.org/t/p/original/";
$(window).load(function(){
// Instantiate the Bloodhound suggestion engine
var movies = new Bloodhound({
    datumTokenizer: function (datum) {
        return Bloodhound.tokenizers.whitespace(datum.value);
    },
    queryTokenizer: Bloodhound.tokenizers.whitespace,
    remote: {
        url: 'http://api.themoviedb.org/3/search/movie?query=%QUERY&api_key=470fd2ec8853e25d2f8d86f685d2270e',
        filter: function (movies) {
            return $.map(movies.results, function (movie) {
                return {
                    value: movie.original_title,
                    release_date: movie.release_date,
                    poster_path:image_url+movie.poster_path
                };
            });
        }
    }
});

// Initialize the Bloodhound suggestion engine
movies.initialize();
// Instantiate the Typeahead UI
$('.typeahead').typeahead(null, {
    displayKey: 'value',
    source: movies.ttAdapter(),
    templates: {
        suggestion: Handlebars.compile("<p style='padding:6px'><img width=50 src='{{poster_path}}'> <b>{{value}}</b> - Release date {{release_date}} </p>"),
        footer: Handlebars.compile("<b>Searched for '{{query}}'</b>")
    }
});
});

</script>

The difference is remote data response is JSON array. We use filter function to prepare the data we need to display in suggestion.

Thats it.

27 comments:

  1. Thank you. Demo with remote is helpful

    ReplyDelete
  2. Wish i had seen this page before I used 2 days understanding bloodhound

    ReplyDelete
  3. Hi, My code with remote is not showing all the records.Only one record is showing all the time.Can u please to resolve this issue

    ReplyDelete
    Replies
    1. I hope it started working for you now.

      Delete
  4. setting remote: '/my_search_url/?q=%QUERY' is working but i want add a java script value with it like remote: '/my_search_url/?q=%QUERY, javavavariable' how can i do it

    ReplyDelete
    Replies
    1. Please follow string appending method.

      var url = '/my_search_url/?q=%QUERY'+YourJavascriptVariable;

      Delete
  5. The json URL in remote JSON demo is giving error 401, no data loading as of now.

    ReplyDelete
    Replies
    1. Thats because the API key has expired. Try using a new Key

      Delete
  6. Thanks a lot! Really helpful post.

    ReplyDelete
  7. Hi!
    my_Suggestion_class limit don't works. I want to show 3 suggested countries but always display 5.

    ReplyDelete
  8. This comment has been removed by the author.

    ReplyDelete
  9. I have been trying to fix a defect for the typeahead where we get the data remotely. Everything works fine, except that special characters appear fine in the suggestions but appear decoded (eg & appears as &) when in the input box (both hint and when option is selected). Any help will be appreciated.

    ReplyDelete
  10. & appears as & amp;

    ReplyDelete
  11. Please provide runnable code with required files

    ReplyDelete
  12. How to give input, because no text box has been given in code. Please clarify. separate file for array and separate file for JSON data will be very much helpful.

    ReplyDelete
  13. Thank you dude. It was very much useful to me to make an output.

    ReplyDelete
  14. Hi Abdul Rashid, Thanks for the code. I have a doubt. For 'Fetching Remote JSON Data in suggestion', menu is getting closed and re-opened as i type in the input box. Any suggestions?

    Thanks

    ReplyDelete
    Replies
    1. Hi Aftab, Did you find the solution to this?

      Delete
  15. Hello, nice tut! I have a question: is there option to disable showing/hiding suggestions while typing?(remote)

    ReplyDelete
  16. Thanks for this post.
    Helped me a lot.

    ReplyDelete
  17. Man!!!! I was stuck and couldn't understand the readme of typehead.js but with your explaination everything is clear. THanks a lot for this great tuto.

    ReplyDelete
  18. The demo of remote is not working the request is returning : '{"status_message":"Invalid API key: You must be granted a valid key.","success":false,"status_code":7} ' when I try to type something.Also the dropdown is not being displayed.

    Here is my input :
    input type="text" required data-provide="typeahead" autocomplete="off" placeholder="Complete Name or Description" id="offer-name" name="name" class="form-control"
    And My code is this:

    // instantiate the bloodhound suggestion engine
    var suggestedProducts = new Bloodhound({
    datumTokenizer: function (datum) {
    return Bloodhound.tokenizers.whitespace(datum.value);
    },
    queryTokenizer: Bloodhound.tokenizers.whitespace,
    remote: {
    url: '/suggestproduct/%search',
    filter: function (products) {
    return $.map(products.results, function (product,ind) {
    return {
    value: product.name,
    id: product.idproduct,
    default_img: product.defaultimg
    };
    });
    },wildcard:'%search'
    }
    });

    //// initialize the bloodhound suggestion engine
    suggestedProducts.initialize();
    var typeaheadInput = $('input[name=name]');
    typeaheadInput.typeahead({
    items:5,
    minLength: 2,
    hint: true,
    highlight: true,
    displayKey: 'value',
    source: suggestedProducts.ttAdapter(),
    templates: {
    suggestion: Handlebars.compile("div style='padding:6px'>img width=50 height=50 class='img-responsive' src='{{default_img}}'> {{value}}/div>")
    }
    });
    typeaheadInput.on("change", function () {

    var current = typeaheadInput.typeahead("getActive");
    if (current) {
    console.log("ID of the product is : " + current.id);
    $("input[name=idproduct]").val(current.id);
    }
    });
    $('input[name=service]').on('change', function () {
    if ($(this).val() == 1) {
    $('#product-section').hide();
    titreFromForm = "Your location"
    } else {
    $('#product-section').show();
    titreFromForm = "Product location"
    }
    });

    The bloodhound and the typehead javascript files were downloaded from Github, thinking that may be the problem is with the version of the files you used and the one I have, I copy the code inside your files from the demo page but still nothing is being display, the data are being served from the server as json :{results:[{name:coke coca cola,idproduct:5,defaultimg:url}]}
    In my examples I remove < from the tags to be able to post the code

    ReplyDelete
  19. take a look at this autocomplete, it's awesome, http://justwebcode.blogspot.com/2017/07/autocomplete-textbox-with-jquery.html

    ReplyDelete