You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Google Maps报错map.fitBounds() is not a function问题求助

问题排查:map.fitBounds() is not a function 错误及地图空白问题

问题现象

加载页面时触发map.fitBounds() is not a function错误,地图时而显示空白、时而正常,首次加载通常能正常显示,无法定位代码问题。


原始代码

全局JavaScript部分

let _locations = [];
var elenco;

google.maps.visualRefresh = true;
var slider, infowindow = null;
var bounds = new google.maps.LatLngBounds();
var current = 0;
var map = 0;

var icons = {
    //'default': '/assets/img/marker.png'
};

function showMap() {
    infowindow.close();
    if (!map.slide) {
        return;
    }
    var next, marker;
    if (_locations.length == 0) {
        return
    } else if (_locations.length == 1) {
        next = 0;
    }
    if (_locations.length > 1) {
        do {
            next = Math.floor(Math.random() * _locations.length);
        } while (next == current)
    }
    current = next;
    marker = _locations[next];
    setInfo(marker);
    //infowindow.open(map, marker);
}

function initialize() {
    var bounds = new google.maps.LatLngBounds();
    var mapOptions = {
        zoom: 7,
        center: new google.maps.LatLng(42.4053848, 12.4276547),
        scrollwheel: false,
        mapTypeId: google.maps.MapTypeId.ROADMAP
    };

    map = new google.maps.Map(document.getElementById('map'), mapOptions);
    map.slide = true;

    //setMarkers(map, _locations);
    infowindow = new google.maps.InfoWindow({
        content: "loading..."
    });
    google.maps.event.addListener(infowindow, 'closeclick', function () {
        infowindow.close();
    });
    slider = window.setTimeout(showMap, 3000);
}

function setInfo(marker) {
    var content =
        '<div class="profile-widget" style="width: 100%; display: inline-block;">' +
        '<div class="doc-img">' +
        '<a href="' + marker.profile_link + '" tabindex="0" target="_blank">' +
        '<img class="img-fluid" alt="' + marker.doc_name + '" src="' + marker.image + '">' +
        '</a>' +
        '</div>' +
        '<div class="pro-content">' +
        '<h4 class="text-center">' +
        '<a href="' + marker.profile_link + '" tabindex="0">' + marker.doc_name + '</a>' +
        '</h4>' +
        '<ul class="available-info pt-5">' +
        '<li><i class="fas fa-map-marker-alt"></i> ' + marker.address + ' </li>' +
        '</ul>' +
        '</div>' +
        '</div>';
    infowindow.setContent(content);
}

function setMarkers(map, markers) {
    for (var i = 0; i < markers.length; i++) {
        var item = markers[i];
        var latlng = new google.maps.LatLng(item.lat, item.lng);
        var marker = new google.maps.Marker({
            position: latlng,
            map: map,
            doc_name: item.doc_name,
            address: item.address,
            speciality: item.speciality,
            profile_link: item.profile_link,
            animation: google.maps.Animation.DROP,
            icon: icons[item.icons],
            image: item.image
        });

        bounds.extend(marker.position);
        markers[i] = marker;
        google.maps.event.addListener(marker, "click", function () {
            setInfo(this);
            infowindow.open(map, this);
            window.clearTimeout(slider);
        });
    }
    map.fitBounds(bounds);
    google.maps.event.addListener(map, 'zoom_changed', function () {
        if (map.zoom > 16) map.slide = false;
    });

    // Add a marker clusterer to manage the markers.
    new markerClusterer.MarkerClusterer({ markers, map });
}

google.maps.event.addDomListener(window, 'load', initialize);

jQuery 数据请求部分

$(document).ready(function () {
    $.ajax({
        url: "?handler=ElencoStrutture",
        type: "POST",
        beforeSend: function (xhr) {
            xhr.setRequestHeader("XSRF-TOKEN",
                $('input:hidden[name="__RequestVerificationToken"]').val());
        },
        data: elenco,
        contentType: "application/json; charset=utf-8",
        success: function (data) {
            //console.error(data.ElencoStrutture);
            _locations = data.ElencoStrutture;
            setMarkers(map, _locations);
            slider = window.setTimeout(showMap, 3000);
            //alert("done ELENCO STRUTTURE");
        }
    });
});

问题根源

  1. 执行顺序冲突:$(document).ready会在DOM加载完成后立即触发ajax请求,但initialize函数要等window.load(所有资源包括谷歌地图脚本加载完成)才执行。如果ajax请求先完成,此时map还是初始值0(不是谷歌地图实例),调用map.fitBounds()就会报错。
  2. 全局变量污染:全局定义的bounds与initialize内的局部bounds重名,且setMarkers使用全局bounds,多次调用时bounds会累积旧数据,导致地图视野异常。
  3. 原始数据被篡改:setMarkers里将markers[i] = marker,把原始地址数据替换成了Marker实例,若多次调用setMarkers,会因无法读取item.lat/item.lng导致标记创建失败。

修复方案及修改后代码

修改后完整代码

let _locations = [];
var elenco;

google.maps.visualRefresh = true;
var slider, infowindow = null;
var current = 0;
var map = null; // 初始化为null,语义更清晰

var icons = {
    //'default': '/assets/img/marker.png'
};

function showMap() {
    if (!infowindow || !map?.slide) return;
    infowindow.close();
    
    if (_locations.length === 0) return;
    let next = _locations.length === 1 ? 0 : null;
    
    if (_locations.length > 1) {
        do {
            next = Math.floor(Math.random() * _locations.length);
        } while (next === current);
    }
    
    current = next;
    const marker = _locations[next];
    setInfo(marker);
}

function initialize() {
    const mapOptions = {
        zoom: 7,
        center: new google.maps.LatLng(42.4053848, 12.4276547),
        scrollwheel: false,
        mapTypeId: google.maps.MapTypeId.ROADMAP
    };

    map = new google.maps.Map(document.getElementById('map'), mapOptions);
    map.slide = true;

    infowindow = new google.maps.InfoWindow({ content: "loading..." });
    google.maps.event.addListener(infowindow, 'closeclick', () => infowindow.close());
    
    // 地图初始化完成后再加载数据,保证map实例存在
    loadLocations();
}

function setInfo(marker) {
    const content = `
        <div class="profile-widget" style="width: 100%; display: inline-block;">
            <div class="doc-img">
                <a href="${marker.profile_link}" tabindex="0" target="_blank">
                    <img class="img-fluid" alt="${marker.doc_name}" src="${marker.image}">
                </a>
            </div>
            <div class="pro-content">
                <h4 class="text-center">
                    <a href="${marker.profile_link}" tabindex="0">${marker.doc_name}</a>
                </h4>
                <ul class="available-info pt-5">
                    <li><i class="fas fa-map-marker-alt"></i> ${marker.address}</li>
                </ul>
            </div>
        </div>
    `;
    infowindow.setContent(content);
}

function setMarkers(map, locationData) {
    const bounds = new google.maps.LatLngBounds(); // 每次调用创建新的bounds,避免累积
    const markers = []; // 用新数组存Marker,不修改原始数据

    for (const item of locationData) {
        const latlng = new google.maps.LatLng(item.lat, item.lng);
        const marker = new google.maps.Marker({
            position: latlng,
            map: map,
            doc_name: item.doc_name,
            address: item.address,
            speciality: item.speciality,
            profile_link: item.profile_link,
            animation: google.maps.Animation.DROP,
            icon: icons[item.icons],
            image: item.image
        });

        bounds.extend(marker.position);
        markers.push(marker);
        
        google.maps.event.addListener(marker, "click", function () {
            setInfo(this);
            infowindow.open(map, this);
            window.clearTimeout(slider);
        });
    }

    // 有标记时再调用fitBounds,避免空范围报错
    if (locationData.length > 0) map.fitBounds(bounds);

    google.maps.event.addListener(map, 'zoom_changed', () => {
        if (map.zoom > 16) map.slide = false;
    });

    new markerClusterer.MarkerClusterer({ map, markers });
}

function loadLocations() {
    $.ajax({
        url: "?handler=ElencoStrutture",
        type: "POST",
        beforeSend: function (xhr) {
            xhr.setRequestHeader("XSRF-TOKEN",
                $('input:hidden[name="__RequestVerificationToken"]').val());
        },
        data: elenco,
        contentType: "application/json; charset=utf-8",
        success: function (data) {
            _locations = data.ElencoStrutture;
            setMarkers(map, _locations);
            slider = window.setTimeout(showMap, 3000);
        },
        error: (xhr, status, error) => {
            console.error("加载位置数据失败:", error);
        }
    });
}

google.maps.event.addDomListener(window, 'load', initialize);

关键修复点

  1. 保证地图优先初始化:把ajax请求移到initialize函数内执行,确保调用setMarkers时map已经是谷歌地图实例。
  2. 隔离变量作用域:在setMarkers内部创建bounds,避免全局变量污染和旧数据累积。
  3. 保留原始数据:用新数组存储Marker实例,不修改_locations,防止多次调用时数据类型错误。
  4. 增加边界判断:只有存在标记时才调用fitBounds,避免空范围导致的异常。
  5. 添加错误处理:给ajax请求增加error回调,方便排查数据加载问题。

内容的提问来源于stack exchange,提问作者Fabio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 14:30:59