views:

425

answers:

1

Before I start the question, I know that some of you won't read the question carefully and will think that I'm asking for something as simple as addClass("custom1") to my original #elem, like this:

$("#elem1").autocomplete("source1.php").addClass("custom1");

This won't do it, because I'm not trying to add the class to my target div... I'm trying to add it to the dynamically-generated div that the plugin generates.

Now to my question :) thanks in advance

I have several autocompletes like this:

$("#elem1").autocomplete("source1.php"); 
$("#elem2").autocomplete("source2.php");
$("#elem3").autocomplete("source3.php");

By default, the results for each one are returned in a separate div class called .ac_results that gets added before the body close.

   <div class="ac_results" style="display: none;">
      <ul style="overflow: auto; max-height: 180px;">
        //the results here as li's.. they vary with what you typed
      </ul>
   </div>
   <div class="ac_results" style="display: none;">
      <ul style="overflow: auto; max-height: 180px;">
        //**THESE LIs ARE DIFFERENT FROM THE SET ABOVE**
      </ul>
   </div>
   <div class="ac_results" style="display: none;">
      <ul style="overflow: auto; max-height: 180px;">
        //**THESE LIs ARE EVEN DIFFERENT FROM THE 2 SETS ABOVE**
      </ul>
   </div>
</body>

I have a custom class though for each #elem that I need to add to the divs that the plugin dynamically generates, to make the end result look like this: (the only change is I added custom1 custom2 custom3 classes)

   <div class="ac_results custom1" style="display: none;">
      <ul style="overflow: auto; max-height: 180px;">
        //the results here as li's.. they vary with what you typed
      </ul>
   </div>
   <div class="ac_results custom2" style="display: none;">
      <ul style="overflow: auto; max-height: 180px;">
        //**THESE LIs ARE DIFFERENT FROM THE SET ABOVE**
      </ul>
   </div>
   <div class="ac_results custom3" style="display: none;">
      <ul style="overflow: auto; max-height: 180px;">
        //**THESE LIs ARE EVEN DIFFERENT FROM THE 2 SETS ABOVE**
      </ul>
   </div>
</body>

so I need to either:

  • somehow wipe off the default .ac_results completely and replace it with my own classes (different class for each elem) at each .autocomplete() declareation
  • or keep the ac_results and add my custom classes (different for each autocomplete) after it's rendered

The problem:

  • The html divs the plugin generates all look the same. You can't tell the difference short of examining the content of the li's.

  • The html divs get generated only after you start typing into the autocomplete for that #elem, AND whichever gets used last, gets added to the DOM last before the body close.

    • This means I can't use DOM order to map them.
    • At first-render of html, the DOM will not have any div class="ac_results". If #elem3 was used, one div class="ac_results" gets added for it. If #elem1 was used after, another div class="ac_results" gets added for it, so the div for elem1 is actually after the div for elem3, hence what I said about not being able to use order.

Some extra info

I'm using JQuery Autocomplete 1.1. This link from the jquery issues is a very close file, although not identical http://plugins.jquery.com/files/issues/jquery.autocomplete.js__0.txt.

From the file, I have on my HDD, here's some of the code. I removed most of the lines that aren't important. The most important is resultsClass: "ac_results", that's the class of the div that gets auto-generated.

   $.Autocompleter.defaults = {
        inputClass: "ac_input",
        resultsClass: "ac_results",
        loadingClass: "ac_loading",
        extraParams: {},
    };

The resultClass then gets used later on in a function init(), I'm displaying this intact without removing any lines just in case. Ideally what I want to do is make init accept an extra class.

// 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;
}
+1  A: 

Looking at the autocomplete plugin, the passed in options are merged with the defaults using $.extend. Here is the code

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);

and here are the defaults

$.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
};

Since the CSS class used for the results <div> is not protected inside of the defaults, all we need do is pass in our own CSS class to use for the results <div>. Something like the following

var id = $("#elem1").attr('id');   
$("#elem1")
    .autocomplete("source1.php", {resultsClass: "ac_results " +id});

You'll just need to set the appropriate id to use in the resultsClass CSS class for each autocomplete.

Russ Cam
+1 pretty cool, I'm re-reading it now..
Chris
the key is that the passed in `options` object is added last in the `$.extend`, meaning that properties specified in this object will override the properties in previous objects (i.e. the `$.Autocompleter.defaults` object)
Russ Cam