Drupal 7 tip: How to change the jquery autocomplete callback on event

Posted by: 
Dominique De Cooman

In the following post I ll explain how to change the autocomplete callback on an event. We needed to select schools. These schools are tagged with a taxonomy term division. In our search form we have a textfield that does an autocomplete callback and returns suggestions for schools while typing. This is just core functionality. Underneath we have a select that selects the division our schools are in. Now we need to limit our autocomplete results to that selected division. To do that we need to dynamically change the jquery autocomplete callback on the event of changing the division.

<?php  
    $form
['school_name'] = array(
      
'#type' => 'textfield',
      
'#placeholder' => t('School name'),
      
'#size' => 25,
      
'#default_value' => isset($_GET['search_api_views_fulltext']) ? $_GET['search_api_views_fulltext'] : '',
      
'#autocomplete_path' => 'so_search/school_name/autocomplete/' $division,      
    );
    
    
$divisions so_search_get_divisions();
    
$form['divisions'] = array(
      
'#type' => 'select',
      
'#title' => '',
      
'#options' => $divisions,
    );

    
drupal_add_js(drupal_get_path('module''so_search') . '/js/dynamicautocomplete.js');
    
drupal_add_js(array('dynamicAutocomplete' => array('autocompleteUrl' => 'http://' $_SERVER['HTTP_HOST'] . '/so_search/school_name/autocomplete/')), 'setting');

?>

This contains a basic form definition and attaches the javascript.

<?php
function _so_search_schoolname_autocomplete($tid 0$string) {
  
$matches = array();
  
$query db_select('node''n');
  
$query->fields('n', array('title''nid'));
  
$query->condition('type''school');
  
$query->condition('title''%' check_plain($string) . '%''LIKE');

  
//If a division is set limit list on division
  
if ($tid) {
    
$query->innerJoin('taxonomy_index''ti''ti.nid = n.nid');
    
$query->condition('ti.tid'$tid);
  }
  
$query->range(018);
  
$return $query->execute();

  
// add matches to $matches 
  
foreach ($return as $row) {
    
$matches[$row->title] = check_plain($row->title);
  }

  
// return for JS
  
drupal_json_output($matches);
}
?>

This is the callback for the autocomplete. It is just here to give you an example.

This is jquery needed to make it work.

    (function ($) {
 
    /**
     * Attaches the autocomplete behavior to all required fields.
     */
    Drupal.behaviors.dynamicAutocomplete = {
      attach: function (context, settings) {
        $('#edit-divisions').change(function(i) {
          var str = $('#edit-divisions_child a.selected').attr('id');
          var n = str.replace("edit-divisions_msa_",""); 
          Drupal.behaviors.dynamicAutocompleteAttach(n);
        });
      }
    };
 
    Drupal.behaviors.dynamicAutocompleteAttach = function (division) {    
      $('#edit-school-name').unbind().val('');
      $('#edit-school-name').each(function(i) {
      //Get the (hidden) *-autocomplete input element
      var input_autocomplete = $('#edit-school-name-autocomplete');
      // Update autocomplete url
      input_autocomplete.val(Drupal.settings.dynamicAutocomplete.autocompleteUrl + division);
      // Mark as not processed.
      input_autocomplete.removeClass('autocomplete-processed');
      //Call autocomplete
 
    });  
      Drupal.behaviors.autocomplete.attach(document);
    };
 
    })(jQuery);

What we do is we take the edit-divisions element and we attach a jquery change event handler. We get the proper division from the id and we give it to our function that will unbind the existing callback. Next we will get the hidden autocomplete element and we will set it with our new url containing the new value. Now we have to mark the element as unprocessed. The only thing left to do is call the Drupal.behaviors.autocomplete.attach callback and it will reprocess all the elements and pickup the new callback.

Using this flow you should be able to change the callback of any autocomplete form element on any event.

Add new comment