使用Leaflet.Control.Search从下拉列表中搜索标记

时间:2016-03-03 12:53:16

标签: javascript search leaflet

我使用Leaflet.Control.Search按GeoJSON功能搜索标记,它可以正常工作。现在我必须输入第一个字母来查找标记,但我想从下拉列表中选择它们,其中包含所有标记。它是这样做的吗?

2 个答案:

答案 0 :(得分:2)

如果您想要一个包含所有标记的下拉列表,最好创建一个自定义控件,而不是尝试修改Leaflet.Control.Search。使用包含所有标记的select元素创建一个控件有点复杂,但肯定比调整其他人已完成项目的代码简单得多。

首先创建一个空控件:

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

要将内容放在控件中,您可以使用控件的.onAdd方法。使用L.DomUtil.create为控件创建容器div,在此上下文中将自动分配类leaflet-control,允许div中的任何内容显示在地图上,其行为就像控件应该表现的那样。然后在div中创建一个select元素。如果需要,请为其提供默认选项。最重要的是,给它一个id,以便以后可以参考:

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

现在控件知道在添加到地图时要做什么,请继续添加:

selector.addTo(map);

要在选择器中添加所有标记作为选项,您可以使用.eachLayer method,它遍历组中的所有标记并为每个标记调用一个函数。对于每个图层,使用上面指定的option创建select元素并将其附加到id元素。假设您已经创建了一个名为markerLayer的GeoJSON图层,它具有一个名为STATION的属性,您希望将其用作选项文本,它将如下所示:

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

在此,我们依赖于每个图层在创建时都会分配一个唯一的内部ID编号_leaflet_id这一事实。我们将每个选项的value属性设置为相应的图层_leaflet_id,以便在选择该选项时,我们可以访问该标记。

最后,为了让控件在您选择其中一个选项时实际执行某些操作,请使用选择器元素的id添加一些事件侦听器:

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

使用click方法的stopPropagation侦听器是为了防止点击选择器传播到地图(如果它们与地图窗格重叠),这可能会立即取消选择您要突出显示的图层。 change侦听器将运行处理函数,您可以将其设置为执行任何操作。在这里,我已将其设置为在选择相应选项时打开标记的弹出窗口:

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

那就是它!以下是所有这些合作的例子:

http://jsfiddle.net/nathansnider/ev3kojon/

修改

如果您使用的是MarkerCluster插件,则可以使用.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();
    })
  }
}

示例:

http://jsfiddle.net/nathansnider/oqk6u0sL/

(我还更新了原始代码和示例以使用.getLayer方法而不是._layers[e.target.value],因为这是一种基于其ID访问标记的更简洁方法。

答案 1 :(得分:1)

有一种方法可以稍微修改Leaflet-search插件,以便在用户点击放大镜按钮时显示完整的标记列表(即当用户扩展搜索控件时)。

好像为0输入的字母触发了搜索。

在不修改插件代码的情况下使用选项minLength: 0不会在不打字的情况下触发搜索。

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

});

在实例化L.Control.Search之前,在JavaScript中包含此代码。

演示:http://jsfiddle.net/ve2huzxw/190/

此解决方案的一大缺点是标记列表是在地图容器中构建的。因此,如果它太大,它将在底部被裁剪,而真正的选择(下拉)输入将“溢出”超出容器,如在nathansnider的解决方案中。