smeijer / leaflet-geosearch

A geocoding/address-lookup library supporting various api providers.
https://smeijer.github.io/leaflet-geosearch/
MIT License
1.02k stars 271 forks source link

Unable to click on map initially after focusing in geosearch input (Chrome & Firefox) #259

Closed darrenklein closed 3 years ago

darrenklein commented 3 years ago

Hello, thank you so much for all of your hard work on this project. I'm currently working with this library in a Vue app, via the vue2-leaflet-geosearch module, and I seem to be encountering a bug that I think is related to the geosearch input (though I'm not quite sure).

The bug is that if I click in the geosearch input (giving it focus) and then click back on the map, the click on the map does not seem to register; however, subsequent clicks register as expected. I tested this on Chrome (87), Firefox (84), and Safari (14)- only Chrome and Firefox seem to be affected, Safari works as expected and does not demonstrate this behavior.

I experimented with adding document.addEventListener("click", (e) => { console.log(e }) and in the case of the missing click, that event was logged as expected and appeared identical to the next click (the one that does register on the map component).

I am using version 3.0.6 of leaflet-geosearch and version 1.7.1 of leaflet.

Here's a slightly stripped-down version of the component (I've left out what I think would be unnecessary bits) -

<template>
  <LMap
    ref="map"
    id="map"
    class="map"
    @click="handleMapClick"
  >
    <LTileLayer :url="mapTileURL" />
    <VGeosearch :options="geosearchOptions" />
    <LMarker v-if="value" :lat-lng="value.coordinates" :icon="icon" />
    <LControlAttribution position="bottomleft" :prefix="mapTileAttribution" />
  </LMap>
</template>

<script>
import L from "leaflet";
import {
  LMap,
  LTileLayer,
  LMarker,
  LControl,
  LControlAttribution
} from "vue2-leaflet";
import { OpenStreetMapProvider } from "leaflet-geosearch";
import VGeosearch from "vue2-leaflet-geosearch";

import "leaflet/dist/leaflet.css";
import "leaflet-geosearch/dist/geosearch.css";

export default {
  name: "MapInput",
  components: {
    LMap,
    LTileLayer,
    LMarker,
    LControl,
    LControlAttribution,
    VGeosearch
  },
  props: {
    value: Object
  },
  created() {
    const iconDimension = 40;

    // Set up constants
    this.mapTileURL = MapConfig.mapTileURL;
    this.mapTileAttribution = MapConfig.mapTileAttribution;
    this.icon = L.icon({
      iconUrl: "./icons/marker.svg",
      iconSize: [iconDimension, iconDimension],
      iconAnchor: [iconDimension / 2, iconDimension]
    });
    this.geosearchOptions = {
      provider: new OpenStreetMapProvider({ params: { countrycodes: ["US"] } }),
      showMarker: false, // we use our own marker
      style: "bar"
    };
  },
  methods: {
    handleMapClick: function(event) {
      // Clicking on various UI elements of the geosearch control can actually be registered on the map.
      // Only update the marker if it's an actual click on the map itself.
      if (event.originalEvent.target.id === "map") {
        const { lat, lng } = event.latlng;
        const point = new Point(lat, lng);

        this.emitInput(point);
      }
    },
    emitInput: function(point) {
      this.$emit("input", point);
    }
  },
  mounted() {
    this.$refs.map.mapObject.on("geosearch/showlocation", ({ location }) => {
      const { x, y } = location;
      const point = new Point(y, x);

      this.emitInput(point);
    });
  }
};
</script>

I would expect that after any click on the map, the handleMapClick method would be called - however, if the user has first clicked inside the geosearch input, the next click on the map will not call handleMapClick (again, subsequent clicks are fine, there's just one missing click). Furthermore, clicking inside the geosearch input and then clicking anywhere else on the page (not on the map) will still result in this bug, even though the geosearch input has lost focus.

I think this is the most detailed description of the issue that I can offer at the moment, but any insight into how I might address the problem would be much appreciated - and please let me know if there's any more information I can provide to help debug this, or if this issue seems like it's actually a problem with the vue2-leaflet package.

Again, many thanks for your time and hard work.

ipaidimarla commented 3 years ago

Yes, I am also facing this issue. I am unable to perform zoomin/zoomout and pan

darrenklein commented 3 years ago

@ipaidimarla Interesting - I'm able to pan and zoom, just can't click - but these might be related issues. Which browser(s) are you trying this on?

smeijer commented 3 years ago

Is it related to vue, or are you able to reproduce this with plain html/js?

darrenklein commented 3 years ago

@smeijer Let me try to cook up a non-Vue implementation later today and see if I can replicate. Thanks for taking a peek!

smeijer commented 3 years ago

Here is a pen that you can use as base.

https://codepen.io/smeijer/pen/poEMdJB?editors=0010

darrenklein commented 3 years ago

@smeijer Thank you so much for setting that up for me - indeed, it seems I can replicate the bug in the pen, tested on Chrome. I - added a click listener to the map like so:

/* Initialize basic map */
/* Initialize basic map */
const map = L.map("map").setView([52.0852378, 5.3846249], 9);
L.tileLayer("http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {}).addTo(map);

const { GeoSearchControl, OpenStreetMapProvider } = window.GeoSearch;

//  add search control
map.addControl(new GeoSearchControl({
  provider: new OpenStreetMapProvider(),
  style: "bar",
  showMarker: true,
  marker: {
    draggable: true
  },
  autoClose: true,
  keepResult: true
}))

map.on("click", () => {
  console.log("ok")
})

Anytime you click in the geosearch bar, the next click on the map will not register.

Incidentally, in my initial report, I noted that this bug didn't seem to affect Safari - I tested the pen in Safari and, though there were some issues loading the map tiles there, I spotted an interesting behavior... a "normal" click on the map would log "ok" to the console twice; but if I first clicked in the geosearch control and then clicked the map, "ok" was logged once. Hmmm...

Actually, taking a look at the Leaflet repo, I see that the double-event in Safari is a known issue there - https://github.com/Leaflet/Leaflet/issues/7255

darrenklein commented 3 years ago

Aha! Perhaps this will shed some light on the subject - I tried this approach -

const m = document.getElementById("map")

m.addEventListener("click", () => {
  console.log("ok")
})

and that resolved the issue in Chrome; under Safari, the "ok" was logged twice (so yeah, that may be a separate issue, but at least the behavior is consistent).

So it seems like there's something about binding a click listener to the L.map instance - the DOM element detects the click, but the instance doesn't seem to catch it.

Ah, but then the issue is that we don't get the latlng attribute in the event that the callback receives... so maybe this doesn't make for a solution, but hopefully gets us closer to figuring out what's going on.

darrenklein commented 3 years ago

After some further investigation, I'm not so sure that this issue is necessarily related to leaflet-geosearch, rather I think it's an issue with leaflet itself. I found that I was able to replicate this behavior in a situation where I had a leaflet map as an element in a form - when the form is submitted, the form content is reset but the user remains on the same page - following that submission, the first click on the map would not register.

Regardless of the root cause of the issue, it does manifest when using leaflet-geosearch, but I did come up with a hacky little workaround... just call a .click() event on the map element.

For example, I'm using this in a Vue app -

const mapElement = this.$refs.map.$el;
const geosearchInputElement = mapElement.querySelector(
  ".leaflet-control-geosearch form"
);

// when the geosearch input field or reset "x" loses focus,
// synthesize a click event on the map DOM element so that the next "real" click will register correctly.
geosearchInputElement.addEventListener("focusout", () => {
  mapElement.click();
});

Closing this issue, but hope this is helpful to someone out there!

smeijer commented 3 years ago

Sorry, I somehow missed your messages from Jan 27. Thanks for the follow-up.