JavaScript结合ArcGIS地图服务实现的搜索建议框

因项目功能需要,开发一个类似百度的那种搜索建议框,即通过输入的关键字进行匹配提示。不要求进行智能推荐,只要返回包含该关键字的若干条结果就行。效果大致如下:




初步的想法是利用第三方插件来做。进行过如下尝试。

1.使用autocomplete.js(自动完成)

某第三方autocomplete插件是这样写的(省略部分代码):

(function ($) {
  $.fn.autocomplete = function (params) {
      //Selections
      var currentSelection = -1;
      var currentProposals = [];
      //Default parameters
      params = $.extend({
          hints: [],
          //placeholder: 'Search',
          width: 200,
          height: 16,
          showButton: true,
          buttonText: 'Search',
          onSubmit: function (text) {
          },
          onBlur: function () {
          }
      }, params);
	  
	  ...
	  
	}
})
调用方式类似如下:

var xxxValues=['xxxa','xxxb','xxxc']
$('#xxxDiv').autocomplete({
  hints: xxxValues
})

这种方式对于搜索的结果集较小时是适用的,可以先将所有可能的结果查询出来,预先放到数组中,然后进行一次初始化即可。如下:


但对本项目,由于结果数据量太大,这种方式不合适。应当能够根据关键字进行动态查询,从建议库服务中动态获取搜索建议并填充下拉列表。于是另外找可以动态填充的插件。

2.使用typeahead.js(可以根据搜索建议库进行自动补全的插件)

该插件与autocomplete相比,其长处在于可以动态获取搜索建议,可对多个字段进行模糊匹配,及指定展示的字段,效果如下:


然而,由于ArcGIS地图服务的搜索结果(JSON)有自己的组织方式,我们要进行匹配的字段位于结果的features的attributes的指定字段中,如下:


如果不指定展示字段,使用默认的展示方式,结果可能像下面这个样子(忽略样式):


也就是说,对像下面这样的结果:

{
    "displayFieldName": "bsm",
    "fieldAliases": {
        "MC": "MC"
    },
    "fields": [
        {
            "name": "MC",
            "type": "esriFieldTypeString",
            "alias": "MC",
            "length": 254
        }
    ],
    "features": [
        {
            "attributes": {
                "MC": "龙岩市民源汽车发展有限公司"
            }
        },
        {
            "attributes": {
                "MC": "龙岩市民商法律事务所"
            }
        },
        {
            "attributes": {
                "MC": "龙岩市民政局"
            }
        },
        {
            "attributes": {
                "MC": "龙岩市民族与宗教事务局"
            }
        },
        {
            "attributes": {
                "MC": "龙岩市民宗局"
            }
        },
        {
            "attributes": {
                "MC": "龙岩市民爆协会"
            }
        },
        {
            "attributes": {
                "MC": "龙岩市民爆协会培训中心"
            }
        }
    ]
}

它展示的是整个JSON转化成字符串后的结果,而不是指定字段。并且,我们不想因为这样一个简单的功能而引入过多的插件。

于是,转而决定自行实现。


思路如下:

监听搜索关键字输入框的keyup事件,如输入不合法(如为空)时清空搜索建议列表(如果已有的话),否则,调用地图服务的搜索功能,根据输入的关键字进行查询,返回前10条结果并填充列表。选中列表中的任意一条结果时,将结果填充到搜索建议框中,并清空搜索建议列表。

核心代码如下:

1.输入框事件响应部分

      $('#inputFromWhereKeyword').keyup(function () {
        var _this = this
        var divInput = $('#inputFromWhereKeyword')[0]
        if (!divInput) {
          return
        }

        var divList = $('#listSearchedAddress')[0]
        if (!divList) {
          return
        }

        _this.divInput = divInput
        var keyword = divInput.value
        if (!keyword || 0 == keyword.replace(' ', '').length) {
          while(divList.hasChildNodes()){
            divList.removeChild(divList.firstChild)
          }
          return
        }

        Promise
          .all([_this.getAddress(keyword)])
          .then(function (results) {
            var result = results[0]

            if (!result || 0 >= String(result).replace(' ', '').length || null == result) {
              while(divList.hasChildNodes()){
                divList.removeChild(divList.firstChild)
              }
              return
            }

            var listInnerHtml = ''
            result.forEach(function (data, index) {
              var anInnerHtml = '<div style="background-color:rgba(255,255,255,0.8);"><li x=' + data.geometry.x +
                ' y=' + data.geometry.y + '><span>' + (index + 1) + '</span><span>' + data.value + '</span></li></div>'
              if (listInnerHtml) {
                listInnerHtml += anInnerHtml
              }else {
                listInnerHtml = anInnerHtml
              }
            })
            if (listInnerHtml) {
              divList.innerHTML = listInnerHtml
            }
            divList.onclick = function (event) {
              var tmpNode = event.target
              var targetNode = (0 == tmpNode.children.length) ? tmpNode.parentNode : tmpNode
              if ('LI' != targetNode.localName && 'li' != targetNode.localName) {
                return
              }
              var x = targetNode.attributes.x.value
              var y = targetNode.attributes.y.value

              _this.divInput.setAttribute('x', x)
              _this.divInput.setAttribute('y', y)

              _this.divInput.value = targetNode.children[1].innerText

              while(divList.hasChildNodes()){
                divList.removeChild(divList.firstChild)
              }
            }
          })
      }.bind(this))
2.获取搜索建议结果部分
    getAddress: function (keyword) {
      var p = new Promise(function (resolve, reject) {
        if (!keyword || 0 == keyword.trim().length) {
          console.log('resolve with no records')
          resolve(' ')
        }else {
          var targetField = 'MC'
          var query = new Query(this.addressNameServiceLayer)
          query.returnGeometry = true
          query.where = targetField + " like '%" + keyword + "%'"
          query.outFields = ['Shape', targetField]

          var queryTask = new QueryTask(this.addressNameServiceLayer)
          queryTask.execute(query).then(function (result) {
            var values = []
            if (!result || result.features.length <= 0) {
              reject('no records')
            }
            var features = result.features
            if (!features) {
              reject('bad result')
            }
            var len = features.length
            var count = len > 10 ? 10 : len
            var index = 0
            for (;index < count;index++) {
              var aFeature = features[index]
              var aResult = aFeature.attributes[targetField]
              if (aResult) {
                var aValue = {
                  value: aResult,
                  geometry: aFeature.geometry
                }
                values.push(aValue)
              }
            }
            if (0 == values.length) {
              reject('empty result')
            }
            resolve(values)
          })
        }
      }.bind(this))
      return p
    },
最终达到的效果如下:

1.输入关键字时,动态推荐结果



2.选中一条结果后,建议列表隐藏




评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值