19
var autocomplete = new google.maps.places.Autocomplete(input, {
  types:  ['geocode']
});

How do I remove it now?

There's "autocomplete.unbindAll()" function but it doesn't remove the HTML for the dropdown box. I have a page that does a lot of ajaxy stuff and because of this dropdown boxes keep being added even tho there's only one autocomplete at a given time on the page :|

thelolcat
  • 10,995
  • 21
  • 60
  • 102

7 Answers7

34

Assuming this is how things have been initialised

var autocomplete = new google.maps.places.Autocomplete(input, {types: ['geocode']});
var autocompleteLsr = google.maps.event.addListener(autocomplete, 'place_changed', function() { ... });

google maps APIs also provide removeListener and clearInstanceListeners for removal as well. https://developers.google.com/maps/documentation/javascript/reference#event

also as an optional step, you should remove this $(".pac-container") as well

google.maps.event.removeListener(autocompleteLsr);
google.maps.event.clearInstanceListeners(autocomplete);
$(".pac-container").remove();
Prabhu
  • 967
  • 7
  • 14
  • 5
    That doesn't help when you have multiple autocompletes on one page though, as all `pac-container`s will be removed when you want to remove only one of them. – JoannaFalkowska Feb 27 '17 at 13:15
  • You can use adding class with ```z-index: 1000000; position: relative;``` to your form to hide .pac-containers – Vyshnia Apr 01 '22 at 13:44
  • `clearInstanceListeners()` didn't work when I passed it the autocomplete instance. I had to pass it the same input field I'd passed to the `google.maps.places.Autocomplete` constructor instead. – tvanc Jul 20 '22 at 14:26
  • @JoannaFalkowska you can make every .cap-container unique by adding some attribute right after initialization – Roman86 Jun 09 '23 at 03:06
  • I also added a solution with code – Roman86 Jun 09 '23 at 05:04
11

Even though it's an old post, I would like to answer it now as it would help someone.

Removing the listeners or unbindall() on autocomplete instance will not remove the auto suggestions from occurring and removing the ".pac-container" from DOM is not the clean solution.

The new instance of autocomplete means the input element is hooked with the autocomplete instance and so we have to clear the listeners for input element and not the autocomplete instance.

google.maps.event.clearInstanceListeners(input);

Mohan Kumar
  • 6,008
  • 6
  • 28
  • 36
2

Hiding the Google Autocomplete dropdown suggestions

To hide the suggestions:

 $(".pac-container").css("visibility", "hidden");

To show the suggestions:

 $(".pac-container").css("visibility", "visible");

To trigger hidden or visible based on jquery event(s):

 $("#location").on("keydown click keyup keypress blur change focus", function(){

        if(!$('#geoAutocomplete').is(":checked")){
            $(".pac-container").css("visibility", "hidden");
        }else{
            $(".pac-container").css("visibility", "visible");
        }

    });

Note: I have a checkbox with the id geoAutocomplete for the user to turn the autocomplete on and off.

Darren Murphy
  • 1,076
  • 14
  • 12
  • $(".pac-container").remove() was too aggressive - it didn't allowed me to turn the autocomplete back on without refreshing the page. – Darren Murphy Jul 15 '20 at 10:35
1

Wherever you are now calling autocomplete.unbindAll() instead (also?) call input.parentNode.removeChild(input)? That's essentially what goes on in the excellent answer to What is the correct way to completely remove GoogleMaps Autocomplete?

However, unlike that question, you complain of 'multiple dropdown boxes'.

It seems like your real problem is that you're invoking new google.maps.places.Autocomplete(...) more than once, against different input nodes. Can't you avoid that? Where you are 'creating' a new node, just pull the 'initialized' node to the location in the document where you want to use it now, using the JS DOM API. Hide it if you want it out of sight temporarily.

It does seem a little more difficult if you're using a binding framework, but you've got to get that call to new Autocomplete out of whatever it's doing, somehow.

Community
  • 1
  • 1
David Bullock
  • 6,112
  • 3
  • 33
  • 43
1

Try this instead. Google places library autocomplete with minimum characters

const autocompleteInput = document.getElementById('ID');

//initialize map after 2 chars are typed
$('#ID').keyup(function(e) {
    if ($(this).val().length < 3) {
        if (autocompleteInput) {

            google.maps.event.clearInstanceListeners(autocompleteInput);
            $(".pac-container").remove();
        }
        e.stopImmediatePropagation()
    } else {
        const autocomplete = new google.maps.places.Autocomplete(autocompleteInput, {
            fields: ["address_components", "geometry", "name"],
            types: ["address"],
        });
        autocomplete.addListener('place_changed', function() {
            const place = autocomplete.getPlace();
            if (!place.geometry) {
                // User entered the name of a Place that was not suggested and
                // pressed the Enter key, or the Place Details request failed.
                alert('No details available for input: \'' + place.name + '\'');
                return;
            }
            fillInAddress(place);
            google.maps.event.clearInstanceListeners(autocompleteInput);
        });
    }
});

function fillInAddress(place) { // optional parameter
    const addressNameFormat = {
        'locality': 'long_name'
    };
    const getAddressComp = function(type) {
        for (const component of place.address_components) {
            if (component.types[0] === type) {
                return component[addressNameFormat[type]];
            }
        }
        return '';
    };
    $("#City").val(getAddressComp('locality'));
}
});

AbdurrahmanY
  • 809
  • 13
  • 22
  • A good answer will always include an explanation why this would solve the issue, so that the OP and any future readers can learn from it. – Tyler2P Dec 13 '21 at 19:19
0

I faced the same problem. Here is what I did, working just fine, multiple inputs supported

Solution code

version

interface DestroyablePACOptions {
  input: HTMLInputElement;
  dropdownTimeoutMs ? : number;
  options ? : google.maps.places.AutocompleteOptions;
  onPlaceChange ? : () => void;
}

export interface DestroyablePACWrapper {
  instance: google.maps.places.Autocomplete;
  destroy: () => void;
}

let instanceIndex = 0;

export function destroyableAutocomplete({
  input,
  options,
  dropdownTimeoutMs = 500,
  onPlaceChange,
}: DestroyablePACOptions): DestroyablePACWrapper {
  const instance = new google.maps.places.Autocomplete(input, options);

  // capturing the new dropdown that we want to destroy then
  let dropdown: HTMLElement | null = null;
  // seems like we need to wait for some time until PAC creates the dropdown, not sure what could be the perfect timeout
  setTimeout(
    () => {
      // looking for a new dropdown (we will add the DOM attribute to make the solution compatible with multiple inputs/dropdowns)
      dropdown = document.querySelector('.pac-container:not([dpac-captured])');

      if (dropdown !== null) {
        // marking it so our next calls could skip it as already handled
        dropdown.setAttribute('dpac-captured', '');
      }
    },
    // every timeout is 1ms longer than the previous one just to make sure that simultaneous calls won't conflict around DOM
    dropdownTimeoutMs + instanceIndex++,
  );

  // attaching the 'place_changed' handler (optional) so we could take care of its removal later.
  // We will also return the PAC instance, so you can still do it on your own,
  // but in this case - don't forget to unsubscribe your own listeners when calling the .destroy()
  let placeChangedListener: google.maps.MapsEventListener | null = null;
  if (typeof onPlaceChange === 'function') {
    placeChangedListener = instance.addListener('place_changed', onPlaceChange);
  }

  return {
    instance,
    destroy() {
      // removing the listener (if we created one)
      placeChangedListener ? .remove();
      // cleaning up the instance (maybe it will also remove all the other/custom listeners, hopefully, but not sure)
      google.maps.event.clearInstanceListeners(instance);
      // removing the DOM dropdown
      dropdown ? .remove();
    },
  };
}

version

let instanceIndex = 0;

function destroyableAutocomplete({
  input,
  options,
  dropdownTimeoutMs = 500,
  onPlaceChange,
}) {
  const instance = new google.maps.places.Autocomplete(input, options);

  // capturing the new dropdown that we want to destroy then
  let dropdown = null;
  // seems like we need to wait for some time until PAC creates the dropdown, not sure what could be the perfect timeout
  setTimeout(
    () => {
      // looking for a new dropdown (we will add the DOM attribute to make the solution compatible with multiple inputs/dropdowns)
      dropdown = document.querySelector('.pac-container:not([dpac-captured])');

      if (dropdown !== null) {
        // marking it so our next calls could skip it as already handled
        dropdown.setAttribute('dpac-captured', '');
      }
    },
    // every timeout is 1ms longer than the previous one just to make sure that simultaneous calls won't conflict around DOM
    dropdownTimeoutMs + instanceIndex++,
  );

  // attaching the 'place_changed' handler (optional) so we could take care of its removal later.
  // We will also return the PAC instance, so you can still do it on your own,
  // but in this case - don't forget to unsubscribe your own listeners when calling the .destroy()
  let placeChangedListener = null;
  if (typeof onPlaceChange === 'function') {
    placeChangedListener = instance.addListener('place_changed', onPlaceChange);
  }

  return {
    instance,
    destroy() {
      // removing the listener (if we created one)
      placeChangedListener?.remove();
      // cleaning up the instance (maybe it will also remove all the other/custom listeners, hopefully, but not sure)
      google.maps.event.clearInstanceListeners(instance);
      // removing the DOM dropdown
      dropdown?.remove();
    },
  };
}

Call example:

const dpac = destroyableAutocomplete({
  input: document.getElementById("inp"),
  options: {
    // some options, just for example
    // https://developers.google.com/maps/documentation/javascript/reference/places-widget?authuser=1#AutocompleteOptions
    fields: ["formatted_address", "name"], //, "geometry", "icon", "icon_background_color", "icon_mask_base_uri"],
    strictBounds: false,
    types: ["geocode"], // geocode, address, establishment, see https://developers.google.com/maps/documentation/javascript/supported_types?authuser=1
    componentRestrictions: {
      country: ["us", "ca"],
    },
  },
});

// ...

setTimeout(() => {
  dpac.destroy();
}, 1000);
Roman86
  • 1,990
  • 23
  • 22
-1

I found out when you use $(".pac-container").remove(); the autocomplete won't show again until you refresh or change page. So it doesn't work well with SPA. I found that you could just use this and it works well:

autocomplete.addListener('place_changed', function () {
    $('.pac-item').remove();
  });
karoluS
  • 2,980
  • 2
  • 23
  • 44