Google Maps API JS:多选框生成标记时信息窗内容均为最后一项问题
问题原因分析
你遇到的这个问题是JavaScript闭包作用域导致的经典陷阱。在你的for循环里,用var location存储当前选项文本,但var声明的变量是函数级作用域的——当$.getJSON的异步回调执行时,整个循环已经跑完了,location变量最后会被更新为循环的最后一个值,所以所有标记的信息窗都会显示最后一项的内容。
解决方案
方法1:使用let代替var(ES6+推荐方案)
let声明的变量是块级作用域的,每次循环都会创建一个独立的location变量,回调函数会绑定当前循环迭代的那个值:
var locations = document.getElementById('waypoints'); for (let x = 0; x < locations.length; x++) { // x也改用let,保证每次循环的独立性 let location = locations.options[x].text; window.alert(location); $.getJSON( 'http://maps.googleapis.com/maps/api/geocode/json?address=' + location + '&sensor=false', null, function(data) { var p = data.results[0].geometry.location; var latlng = new google.maps.LatLng(p.lat, p.lng); var contentString = location; // 现在绑定的是当前循环的location值 var infowindow = new google.maps.InfoWindow({ content: contentString }); var marker = new google.maps.Marker({ position: latlng, animation: google.maps.Animation.DROP, map: map, }); marker.addListener('click', function() { infowindow.open(map, marker); }); gmarkers.push(marker); } ); }
方法2:用立即执行函数表达式(IIFE)创建闭包
如果需要兼容ES6之前的环境,可以用IIFE把每次循环的location值封装成独立作用域:
var locations = document.getElementById('waypoints'); var x; for (x = 0; x < locations.length; x++) { var location = locations.options[x].text; window.alert(location); // 用IIFE保存当前循环的location值 (function(currentLocation) { $.getJSON( 'http://maps.googleapis.com/maps/api/geocode/json?address=' + currentLocation + '&sensor=false', null, function(data) { var p = data.results[0].geometry.location; var latlng = new google.maps.LatLng(p.lat, p.lng); var contentString = currentLocation; // 使用闭包保存的独立值 var infowindow = new google.maps.InfoWindow({ content: contentString }); var marker = new google.maps.Marker({ position: latlng, animation: google.maps.Animation.DROP, map: map, }); marker.addListener('click', function() { infowindow.open(map, marker); }); gmarkers.push(marker); } ); })(location); // 传入当前循环的location值 }
方法3:改用forEach循环
forEach的回调函数会为每个元素创建独立作用域,天然避免作用域共享问题:
var locations = document.getElementById('waypoints'); // 把options集合转换成数组后使用forEach Array.from(locations.options).forEach(function(option) { var location = option.text; window.alert(location); $.getJSON( 'http://maps.googleapis.com/maps/api/geocode/json?address=' + location + '&sensor=false', null, function(data) { var p = data.results[0].geometry.location; var latlng = new google.maps.LatLng(p.lat, p.lng); var contentString = location; var infowindow = new google.maps.InfoWindow({ content: contentString }); var marker = new google.maps.Marker({ position: latlng, animation: google.maps.Animation.DROP, map: map, }); marker.addListener('click', function() { infowindow.open(map, marker); }); gmarkers.push(marker); } ); });
额外小提示
顺便提一句:你的Thymeleafselect标签里,th:field="${deliveries}"用于表单绑定的话,要确保后端接收的是对应option值的集合(也就是街道地址的集合),避免表单提交时出现数据不匹配的问题,不过这部分和当前JS问题无关,只是个小提醒~
内容的提问来源于stack exchange,提问作者Marios
相关产品推荐
相关产品推荐

