admin管理员组

文章数量:1406945

I use Leaflet.Control.Search for searching markers by GeoJSON features and it works OK. Now I have to type first letters to find marker, but I want to choose them from drop down list with all markers in it. Is it way to do it?

I use Leaflet.Control.Search for searching markers by GeoJSON features and it works OK. Now I have to type first letters to find marker, but I want to choose them from drop down list with all markers in it. Is it way to do it?

Share Improve this question asked Mar 3, 2016 at 12:53 AntonAnton 4732 gold badges10 silver badges20 bronze badges 3
  • Is that a feature of this control? If not, you'll have to modify the source to add it yourself. – Squiggle Commented Mar 3, 2016 at 13:11
  • I'm searching markers by their names, included in my GeoJSON like string values. Now I have to type letters «c», «i» etc. to find the place with name «City» for example. After I enter some characters, the plugin gives me short list with names corresponding my query. This is a plugin works by default. My purpose is choosing the name from entire drop down list with all names instead of typing a marker name. – Anton Commented Mar 3, 2016 at 13:51
  • But is this a feature of the ponent? Refer to the documentation provided to see if this is possible. If it isn't, you'll have to modify the ponent yourself to acplish this... – Squiggle Commented Mar 3, 2016 at 15:24
Add a ment  | 

2 Answers 2

Reset to default 4

If you want a dropdown list with all your markers, you are better off creating a custom control rather than trying to modify Leaflet.Control.Search. Creating a control with a select element that contains all your markers is a little plicated, but certainly far simpler than tweaking the code of someone else's finished project.

Start by creating an empty control:

var selector = L.control({
  position: 'topright'
});

To put content within the control, you can use the control's .onAdd method. Create a container div for the control using L.DomUtil.create, which in this context will automatically assign the class leaflet-control, allowing any content within the div to display on the map and behave like a control should behave. Then create a select element within the div. Give it a default option if you want. Most importantly, give it an id, so that you can refer to it later:

selector.onAdd = function(map) {
  var div = L.DomUtil.create('div', 'mySelector');
  div.innerHTML = '<select id="marker_select"><option value="init">(select item)</option></select>';
  return div;
};

Now that the control knows what to do when added to the map, go ahead and add it:

selector.addTo(map);

To add all your markers as options in the selector, you can use the .eachLayer method, which iterates through all the markers in the group and calls a function for each. For each layer, create an option element and append it to the select element, using the id assigned above. Assuming that you have created a GeoJSON layer called markerLayer, which has a property called STATION that you want to use as the option text, it would look like this:

markerLayer.eachLayer(function(layer) {
  var optionElement = document.createElement("option");
  optionElement.innerHTML = layer.feature.properties.STATION;
  optionElement.value = layer._leaflet_id;
  L.DomUtil.get("marker_select").appendChild(optionElement);
});

Here, we're relying on the fact that each layer gets assigned a unique internal id number, _leaflet_id, when created. We set each option's value attribute to the corresponding layer's _leaflet_id, so that when the option is selected, we have a way to access the marker.

Finally, to get the control to actually do something when you select one of the options, add some event listeners, using the id of the selector element:

var marker_select = L.DomUtil.get("marker_select");
L.DomEvent.addListener(marker_select, 'click', function(e) {
  L.DomEvent.stopPropagation(e);
});
L.DomEvent.addListener(marker_select, 'change', changeHandler);

The click listener with the stopPropagation method is to prevent clicks on the selector from propagating through to the map if they overlap with the map pane, which could immediately unselect the layer you are trying to highlight. The change listener will run a handler function, which you can set to do anything you want. Here, I've set it to open the popup for a marker when its corresponding option is selected:

function changeHandler(e) {
  if (e.target.value == "init") {
    map.closePopup();
  } else {
    markerLayer.getLayer(e.target.value).openPopup();
  }
}

And that's it! Here is an example fiddle with all this working together:

http://jsfiddle/nathansnider/ev3kojon/

EDIT:

If you are using the MarkerCluster plugin, you can modify the change handler function to work with clustered markers using the .zoomToShowLayer method:

function changeHandler(e) {
  if (e.target.value == "init") {
    map.closePopup();
  } else {
    var selected = markerLayer.getLayer(e.target.value);
    markerClusterLayer.zoomToShowLayer(selected, function() {
      selected.openPopup();
    })
  }
}

Example:

http://jsfiddle/nathansnider/oqk6u0sL/

(I also updated the original code and example to use the .getLayer method rather than ._layers[e.target.value], because this is a cleaner way to access the marker based on its id)

There would be a way to slightly modify Leaflet-search plugin so that it shows the full list of markers when user clicks on the magnifier button (i.e. when user expands the search control).

As if the search was triggered for 0 typed letter.

Using option minLength: 0 without modifying the plugin code does not trigger a search without typing unfortunately.

L.Control.Search.mergeOptions({
  minLength: 0 // Show full list when no text is typed.
});

L.Control.Search.include({

  _handleKeypress: function(e) {

    switch (e.keyCode) {
      case 27: //Esc
        this.collapse();
        break;
      case 13: //Enter
        if (this._countertips == 1)
          this._handleArrowSelect(1);
        this._handleSubmit(); //do search
        break;
      case 38: //Up
        this._handleArrowSelect(-1);
        break;
      case 40: //Down
        this._handleArrowSelect(1);
        break;
      case 37: //Left
      case 39: //Right
      case 16: //Shift
      case 17: //Ctrl
        //case 32://Space
        break;
      case 8: //backspace
      case 46: //delete
        this._autoTypeTmp = false;
        if (this._collapsing) { // break only if collapsing.
          break;
        }
      default: //All keys

        this._doSearch(); // see below
    }
  },

  // externalized actual search process so that we can trigger it after control expansion.
  _doSearch: function() {
    if (this._input.value.length)
      this._cancel.style.display = 'block';
    else
      this._cancel.style.display = 'none';

    if (this._input.value.length >= this.options.minLength) {
      var that = this;

      clearTimeout(this.timerKeypress);
      this.timerKeypress = setTimeout(function() {

        that._fillRecordsCache();

      }, this.options.delayType);
    } else
      this._hideTooltip();
  },

  expand: function(toggle) {
    toggle = typeof toggle === 'boolean' ? toggle : true;
    this._input.style.display = 'block';
    L.DomUtil.addClass(this._container, 'search-exp');
    if (toggle !== false) {
      this._input.focus();
      this._map.on('dragstart click', this.collapse, this);
    }
    this.fire('search_expanded');
    this._doSearch(); // Added to trigger a search when expanding the control.
    return this;
  },

  collapse: function() {
    this._hideTooltip();
    this._collapsing = true; // added to prevent showing tooltip when collapsing
    this.cancel();
    this._collapsing = false; // added to prevent showing tooltip when collapsing
    this._alert.style.display = 'none';
    this._input.blur();
    if (this.options.collapsed) {
      this._input.style.display = 'none';
      this._cancel.style.display = 'none';
      L.DomUtil.removeClass(this._container, 'search-exp');
      if (this.options.hideMarkerOnCollapse) {
        this._markerLoc.hide();
      }
      this._map.off('dragstart click', this.collapse, this);
    }
    this.fire('search_collapsed');
    return this;
  }

});

Include this code in your JavaScript before instantiating L.Control.Search.

Demo: http://jsfiddle/ve2huzxw/190/

The big drawback of this solution is that the list of markers is built within the map container. As such, if it is too big, it will be cropped on the bottom, whereas a true select (drop-down) input would "overflow" beyond the container, as in nathansnider's solution.

本文标签: javascriptSearching markers with LeafletControlSearch from drop down listStack Overflow