JavaScript结合ArcGIS地图服务实现的搜索建议框
2017-09-19 09:14
537 查看
因项目功能需要,开发一个类似百度的那种搜索建议框,即通过输入的关键字进行匹配提示。不要求进行智能推荐,只要返回包含该关键字的若干条结果就行。效果大致如下:
![](https://img-blog.csdn.net/20170919092323700?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYV9kZXY=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
![](https://img-blog.csdn.net/20170919092336910?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYV9kZXY=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
初步的想法是利用第三方插件来做。进行过如下尝试。
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
})
这种方式对于搜索的结果集较小时是适用的,可以先将所有可能的结果查询出来,预先放到数组中,然后进行一次初始化即可。如下:
![](https://img-blog.csdn.net/20170919093144172?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYV9kZXY=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
但对本项目,由于结果数据量太大,这种方式不合适。应当能够根据关键字进行动态查询,从建议库服务中动态获取搜索建议并填充下拉列表。于是另外找可以动态填充的插件。
2.使用typeahead.js(可以根据搜索建议库进行自动补全的插件)
该插件与autocomplete相比,其长处在于可以动态获取搜索建议,可对多个字段进行模糊匹配,及指定展示的字段,效果如下:
![](https://img-blog.csdn.net/20170919094000560?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYV9kZXY=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
然而,由于ArcGIS地图服务的搜索结果(JSON)有自己的组织方式,我们要进行匹配的字段位于结果的features的attributes的指定字段中,如下:
![](https://img-blog.csdn.net/20170919094551554?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYV9kZXY=/font/5a6L5L2T/fontsize/40<br/>4000<br/>0/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
如果不指定展示字段,使用默认的展示方式,结果可能像下面这个样子(忽略样式):
![](https://img-blog.csdn.net/20170919094227058?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYV9kZXY=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
也就是说,对像下面这样的结果:
{
"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.获取搜索建议结果部分
1.输入关键字时,动态推荐结果
![](https://img-blog.csdn.net/20170919095830534?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYV9kZXY=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
![](https://img-blog.csdn.net/20170919095844037?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYV9kZXY=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
2.选中一条结果后,建议列表隐藏
初步的想法是利用第三方插件来做。进行过如下尝试。
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.选中一条结果后,建议列表隐藏
相关文章推荐
- ArcGIS API For Javascript之地图基本加载与显示,地图切换、缩放、定位、比例尺、鹰眼图、坐标显示、查询搜索功能实现
- ArcGIS.Server.9.3和ArcGIS API for JavaScript实现基本的地图功能(一)
- ArcGIS API for JavaScript实现地图双屏联动
- (三)ArcGIS API For Javascript之调用动态地图服务
- Flex4.0+Blaseds+Spring+Hibernate实现地图服务发布平台(并结合GEOTOOLS)
- Arcgis for Javascript API下类似于百度搜索A、B、C、D marker的实现方式
- Flex4.0+Blaseds+Spring+Hibernate实现地图服务发布平台(并结合GEOTOOLS)
- Arcgis for Javascript API下类似于百度搜索A、B、C、D marker的实现方式
- ArcGIS API For Javascript GP工具 Printing Tools导出地图或者打印地图(一):GP 服务Printing Tools
- ArcGIS.Server.9.3和ArcGIS API for JavaScript实现基本的地图功能(一)
- ArcGIS API for JavaScript实现地图常用工具条 距离测量,面积测量
- ArcGIS.Server.9.3和ArcGIS API for JavaScript地图实现Toorbar功能(四)
- Arcgis for Javascript API下类似于百度搜索A、B、C、D marker的实现方式
- ArcGIS.Server.9.3和ArcGIS API for JavaScript实现基本的地图功能
- GIS.arcgis for javascript简单接口调用实现地图展现
- Arcgis API for JavaScript在地图上实现手机定位信息的追踪显示
- ArcGIS.Server.9.3和ArcGIS API for JavaScript地图实现Toorbar功能(四)
- ArcGIS API for JavaScript实现地图双屏联动
- 百度地图SDK-----百度地图在线建议查询,结合AutoCompleteTextView实现搜索下拉列表。
- ArcGIS API For Javascript之调用动态地图服务+属性、空间查询