Autocomplete is implemented in Drupal through AJAX. When users type into a textbox, code on the client page dynamically loads new data from the server (a Drupal website) and uses this data to update the user display (provide a drop-down list of matching options, which the user can select from).

Handily, all the mechanics of exchanging data between client and server and of updating the display are handled by the widget, autocomplete.js. Implementing a particular autocomplete textfield requires two parts: (a) a caller (the textfield, with special additions) and (b) a handler, which is a PHP function that parses the request and returns a response.

Prebuilt autocomplete functions

Two autocomplete functions ship with the Drupal core. Each is referenced by an "autocomplete_path"--the uri to which autocomplete requests are sent.

* user_autocomplete()
Use this function to load matching user names. Autocomplete path: user/autocomplete.
* taxonomy_autocomplete()
Use this function to load matching taxonomy terms from a given vocabulary. Autocomplete path: taxonomy/autocomplete.

If one of these matches your needs, then all you need to do is include the special #autocomplete_path selector in a form field. Here's an example for user autocomplete (from comment.module):

        $form['admin']['author'] = array(
          '#type' => 'textfield',
          '#title' => t('Authored by'),
          '#size' => 30,
          '#maxlength' => 60,
          '#autocomplete_path' => 'user/autocomplete',
          '#default_value' => $author,
          '#weight' => -1,
        );

For taxonomy autocomplete, include a vocabulary id, as in this example from taxonomy.module:

        $form['taxonomy']['tags'][$vocabulary->vid] = array('#type' => 'textfield',
          '#title' => $vocabulary->name,
          '#description' => $help,
          '#required' => $vocabulary->required,
          '#default_value' => $typed_string,
          '#autocomplete_path' => 'taxonomy/autocomplete/'. $vocabulary->vid,
          '#weight' => $vocabulary->weight,
          '#maxlength' => 1024,
        );

For Drupal 7 the taxonomy_autocomplete function takes as parameter the field_name, which is the name of the term reference field. This is the field name of term reference assigned to entity(i.e. node) to be edited. A quick verification may help by manually making a request via http://site.com/taxonomy/autocomplete/@field_name from a browser. If it returns '[]' then it is working, otherwise. It will provide an error message that can help troubleshoot further.

Example

  $form['example'] = array(
    '#type' => 'textfield',
    '#title' => t('This is the Title'),
    '#autocomplete_path' => 'taxonomy/autocomplete/<strong>taxonomy_vocabulary_1</strong>',
    '#maxlength' => 30,
  );

where taxonomy_vocabulary_1 is the machine name of the taxonomy reference field.

Building a custom autocomplete function

If you want to make your own autocomplete function to answer a need not already met, there are a couple of additional steps.

The form

The form will contain an element such as

  $form['city'] = array(
    '#type' => 'textfield',
    '#title' => 'City',
    '#maxlength' => 128,
    '#autocomplete_path' => 'cities/autocomplete',
  );

Addition to hook_menu

You must add #autocomplete_path to your hook_menu function.

    function cities_menu(){
    ...
    // Path with autocomplete function for cities.
    $items['cities/autocomplete'] = array(
      'title' => 'Autocomplete for cities',
      'page callback' => '_cities_autocomplete',
      'access arguments' => array('use autocomplete'),  //or whatever permission makes sense
      'type' => MENU_CALLBACK
    );
    return $items;

Make a call to the Database, D6 Version

Write the handler function. This will receive an autocomplete request and return data to the client in a form ready to be parsed by automplete.js.

/**
* autocomplete helper
* $string = string for search
*/
function _cities_autocomplete($string) {
  $matches = array();
  //search table `cities` for cities that begin with the letters the user enters in the form
  $result = db_query_range("SELECT city FROM {cities} WHERE LOWER(city) LIKE LOWER('%s%')", $string, 0, 10);
  // add matches to $matches
  while ($data = db_fetch_object($result)) {
    $matches[$data->city] = check_plain($data->city);
  }
  // return for JS
  print drupal_to_js($matches);
  exit();
}

Note that you are (a) finding matching records based on user input (b) constructing an array of matches (c) converting the array to JavaScript code and (d) outputting the results.

Also note that we need to escape the city name in the $matches array's values: this is because the values are HTML. Not escaping would open up XSS holes. On the other hand, it also means that you can mark-up the autocomplete suggestions any way you like.

Make a call to the Database, D7 Version

If you on Drupal 7, and want to use the Database API, your code would look slightly different.

/**
 * autocomplete helper
 * $string = string for search
 */
function _cities_autocomplete($string) {
  $matches = array();
  $result = db_select('cities', 'c')
    ->fields('c', array('city'))
    ->condition('city', '%' . db_like($string) . '%', 'LIKE')
    ->execute();

  // save the query to matches
  foreach ($result as $row) {
    $matches[$row->city] = check_plain($row->city);
  }

  // Return the result to the form in json
  drupal_json_output($matches);
}

Security note:

Make sure your autocomplete handler has appropriate menu permissions set on it, and respects existing access control mechanisms. Otherwise, you might be exposing sensitive information through the autocomplete.

Adapted from http://drupal.org/node/208424#comment-686252, http://drupal.org/node/42552 and http://timonweb.com/how-create-ajax-autocomplete-textfield-drupal-7

Additional example for D6: http://digitalillusion.altervista.org/wordpress/2009/01/12/drupal-6-link...

Comments

ngstigator’s picture

I've replaced:
print drupal_to_js($matches);
exit();

with:
drupal_json($matches);

with good results.

Reference: http://drupal.org/update/modules/6/7#rename-drupal-to-js

dewolfe001’s picture

I was hitting a wall about autocomplete and how I couldn't get mine to work.
Then, I found the small glitch.

This didn't work:

$form['account_sync_fields_map']['sync_'.$map_id]['remote'] = array(
	  '#type' => 'textfield',
          '#default_value' => $details['remote'],
          '#autocomplete_path' => '/admin/account_sync/autocomplete',
          '#maxlength' => 1024,
        );

This did:

$form['account_sync_fields_map']['sync_'.$map_id]['remote'] = array(
	  '#type' => 'textfield',
          '#default_value' => $details['remote'],
          '#autocomplete_path' => 'admin/account_sync/autocomplete',
          '#maxlength' => 1024,
        );

The difference is that the path had to not have a leading slash.

jibran’s picture

For Drupal 7 code is something like this.
where field_tags is taxonomy field in node with widget type autocomplete.

   $node=node_load($nid);
    $tags = array();
    foreach ($node->field_tags['und'] as $item) {
      $tags[$item['tid']] = isset($item['taxonomy_term']) ? $item['taxonomy_term'] : taxonomy_term_load($item['tid']);
    }
    $form['tags'] = array(
      '#type' => 'textfield',
      '#default_value' => taxonomy_implode_tags($tags),
      '#title' => 'Add Tags',
      '#autocomplete_path' => 'taxonomy/autocomplete/field_tags',
      '#maxlength' => 1024,
      '#element_validate' => array('taxonomy_autocomplete_validate'),page
        );
JThan’s picture

Do not start your Path with search as in "search/yourlistpath". This will not work because it triggers a Search. Obvious only to those who know ;)

arnoldbird’s picture

I was seeing this error in firebug:

uncaught exception: Syntax error, unrecognized expression: #

I also saw a notice in Drupal referencing line 3707 of forms.inc. This is in Drupal 7.12.

If I add an id to the city field in hook_form, the errors go away...

<?php
  $form['city'] = array(
    '#id' => 'something-unique',
    '#type' => 'textfield',
    '#title' => 'City',
    '#maxlength' => 128,
    '#autocomplete_path' => 'cities/autocomplete',
  );
?>
zhuber’s picture

I was having trouble with this for hours. All of the tutorials I encountered have very specific syntax, which I followed.

My autocomplete fields would not get any results until I read this and added the unique ID.

nehajn’s picture

 
function user_autocomplete_menu() {
 
  $items['user_autocomplete'] = array(
    'title' => 'User Autocomplete',
    'page callback' => 'user_autocomplete_page',
    'access arguments' => array('access content'),
   );
 
 $items['demo-autocomplete-engine'] = array(
    'page callback' => 'demo_autocomplete',
    'access arguments' => array('view published content'),
    'type' => MENU_CALLBACK,
  );  
  return $items;
}

function user_autocomplete_page () {
	return drupal_get_form('user_form');
}

function user_form($form, &$form_state) {
    $form = array();

	$form['user'] = array (
		'#type'		=>	'textfield',
		'#title'	=>	t('Test autocomplete '),
		'#required' =>	 TRUE,
		'#autocomplete_path' => 'user/autocomplete',
	);
        
       $form['codes'] = array(
    '#title' => t('Codes'),
    '#type' => 'textfield',
    '#maxlength' => 60,
    '#autocomplete_path' => 'demo-autocomplete-engine',
 );  
     return $form;    
  }

    
function demo_autocomplete($string = '') {
  $matches = array();
  if ($string) {
    $result = db_select('voucher')->fields('voucher', array('promo_code'))->condition('promo_code', db_like($string) . '%', 'LIKE')->range(0, 10)->execute();
    foreach ($result as $user) {
      $matches[$user->promo_code] = check_plain($user->promo_code);
    }
  }

  drupal_json_output($matches);
}
DrCord’s picture

The #id property appears to be mandatory if you want you user entity reference autocomplete to work at all. Thanks!

jusfeel’s picture

For the record, today is Christmas! And thanks to all the truth seeker before me! It's been hell of hours to have had a blade in my teeth to try to figure out wt..

"Uncaught TypeError: Cannot read property 'form' of undefined" which caused by autocomplete used in a custom form which try to render itself, then the #id must be there plus the form['xxx'] must be the same as the #id

$form['edit-keyword'] = array(
'#id' => 'edit-keyword',
'#type' => 'textfield',
'#default_value' => '',
'#autocomplete_path' => 'n-search/autocomplete',
);

Right after adding the #id, it's a merry Xmas all of a sudden!

bbombachini’s picture

I got this working for logged in users but not for anonymous although I had 'access arguments' => ['view published content'] on hook_menu.
So I've changed to 'access callback' => TRUE and it's working now.

$items['company/autocomplete'] = array(
    'title' => 'Autocomplete for Company Name',
    'page callback' => '_company_autocomplete',
    'access callback' => TRUE,
    'type' => MENU_CALLBACK
  );
  return $items;