【问题标题】:How to use default style of Google Maps info windows with content security policy?如何使用具有内容安全策略的谷歌地图信息窗口的默认样式?
【发布时间】:2025-12-03 17:15:02
【问题描述】:

我在具有不允许内联 CSS 的内容安全政策的网站上使用 Google Maps Javascript API。

在实施 CSP 之前,信息窗口的样式很好。实现 CSP 后,信息窗口完全没有样式,因为 Google Maps Javascript API 使用内联 CSS。

我愿意接受任何能让信息窗口看起来像以前一样的解决方案。

我尝试的策略是获取 Google Maps API 添加到每个元素的内联样式,然后通过 jQuery 应用该样式。理论上,这应该可行,因为jQuery's css() method does not use inline CSS:

值得一提的是,如果样式属性是通过 直接用 JavaScript 就没有问题了。例如, jQuery 的 css() 方法很好,因为它更新了样式属性 直接在被子下面。

以下是一个可重现的最小示例。

HTML:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <script src="https://code.jquery.com/jquery-3.4.0.min.js" integrity="sha256-BJeo0qm959uMBGb65z40ejJYGSgR7REI4+CW1fNKwOg=" crossorigin="anonymous"></script>
        <script type="text/javascript" src="/js/test.js"></script>
        <link type="text/css" rel="stylesheet" href="/css/test.css">
    </head>
    <body>
        <div id="map-wrapper">
            <div id="map-canvas"></div>
        </div>
    </body>
</html>

CSS:

#map-canvas {
    height: 100%;
    width: 100%;
}

#map-wrapper {
    height: 400px;
    margin: auto;
    max-width: 95%;
    width: 600px;
}

JavaScript:

//on page load, call the function to initialize the map
$(function($) {
    var script = document.createElement('script');
    script.src = "//maps.googleapis.com/maps/api/js?callback=initialize&key=yourKeyHere";
    document.body.appendChild(script);
});

//function to initialize the map
function initialize() {

    //construct the map
    var map = new google.maps.Map(document.getElementById("map-canvas"), {
        fullscreenControl: false,
        mapTypeId: 'roadmap',
        scaleControl: false,
        streetViewControl: false,
        zoomControl: false,
    });

    //center the map
    var latLng = new google.maps.LatLng('34.4270881', '-117.57501819999999');
    map.setCenter(latLng);
    map.fitBounds(new google.maps.LatLngBounds(latLng));

    //info window
    var infoWindow = new google.maps.InfoWindow(), marker, i;
    marker = new google.maps.Marker({position: latLng, map: map, title: 'marker title'});
    google.maps.event.addListener(marker, 'click', (function(marker, i) { return function() {
        infoWindow.setContent("foobar");
        infoWindow.open(map, marker);
        google.maps.event.addListener(infoWindow, 'domready', applyStyles(document.getElementById("map-wrapper")));
    }})(marker, i));
}

//function to get inline CSS and apply it via jQuery
function applyStyles(div) {

    //log the div
    console.log(div);

    //loop through the inline CSS properties applied by Google Maps API
    for (var propertyNum = 0; propertyNum < div.style.length; propertyNum++) {

        //get property and convert to camelCase
        var property = div.style[propertyNum].replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); })

        //get value
        var value = div.style[property];

        //log the property and value
        console.log(property + " = " + value);

        //apply style via jquery
        $(div).css(property, value);
    }

    //log a line break
    console.log("");

    //do the same for children of this div
    var children = $(div).children();
    for (var child = 0; child < children.length; child++) {
        applyStyles(children[child]);
    }

}

这不起作用。没有任何样式生效。

【问题讨论】:

  • 您何时/如何调用该函数?在 InfoWindow 打开并添加到 DOM 之后(在domready 事件触发之后),它可能无法处理 InfoWindow 内容。请提供一个 minimal reproducible example 来证明您的问题。
  • @geocodezip 我已经编辑了我的问题以表明我在domready 事件触发后调用了该函数。使用console.log()我已经确定该功能在信息窗口打开并添加到DOM后生效。该函数确实获得了 Google 添加到该窗口的内联样式。另请注意,我在 #map-wrapper 及其每个后代上执行此操作,因为信息窗口的样式似乎并不限于信息窗口 div 本身。
  • @geocodezip 我现在已经编辑了我的问题以包含一个最小的可重现示例。

标签: javascript jquery css google-maps-api-3 content-security-policy


【解决方案1】:

没有任何样式生效。

这不是真的。我通过元素的 style 属性“获取”的样式实际上是通过 jQuery .css() 方法应用的。

我的错误是假设 API 应用的样式仅限于 inline CSS(例如 &lt;div style="background=color: #ffffff"&gt;),而 API 也在注入 internal CSS(例如&lt;style&gt;div {background=color: #ffffff"&gt;}&lt;/style&gt;)。

我注意到相关的divs 都有以gm-style-iw 开头的类名(大概,“gm”表示谷歌地图,“iw”表示信息窗口)。我搜索了注入的样式标签,发现只有一个有这些类名。

.gm-style .gm-style-iw{font-weight:300;font-size:13px;overflow:hidden}.gm-style .gm-style-iw-a{position:absolute;width:9999px;height:0}.gm-style .gm-style-iw-t{position:absolute;width:100%}.gm-style .gm-style-iw-t::after{background:linear-gradient(45deg,rgba(255,255,255,1) 50%,rgba(255,255,255,0) 51%,rgba(255,255,255,0) 100%);box-shadow:-2px 2px 2px 0 rgba(178,178,178,.4);content:"";height:15px;left:0;position:absolute;top:0;transform:translate(-50%,-50%) rotate(-45deg);width:15px}.gm-style .gm-style-iw-c{position:absolute;box-sizing:border-box;overflow:hidden;top:0;left:0;transform:translate(-50%,-100%);background-color:white;border-radius:8px;padding:12px;box-shadow:0 2px 7px 1px rgba(0,0,0,0.3)}.gm-style .gm-style-iw-d{box-sizing:border-box;overflow:auto}.gm-style .gm-style-iw-d::-webkit-scrollbar{width:18px;height:12px;-webkit-appearance:none}.gm-style .gm-style-iw-d::-webkit-scrollbar-track,.gm-style .gm-style-iw-d::-webkit-scrollbar-track-piece{background:#fff}.gm-style .gm-style-iw-c .gm-style-iw-d::-webkit-scrollbar-thumb{background-color:rgba(0,0,0,0.12);border:6px solid transparent;border-radius:9px;background-clip:content-box}.gm-style .gm-style-iw-c .gm-style-iw-d::-webkit-scrollbar-thumb:horizontal{border:3px solid transparent}.gm-style .gm-style-iw-c .gm-style-iw-d::-webkit-scrollbar-thumb:hover{background-color:rgba(0,0,0,0.3)}.gm-style .gm-style-iw-c .gm-style-iw-d::-webkit-scrollbar-corner{background:transparent}.gm-style .gm-iw{color:#2c2c2c}.gm-style .gm-iw b{font-weight:400}.gm-style .gm-iw a:link,.gm-style .gm-iw a:visited{color:#4272db;text-decoration:none}.gm-style .gm-iw a:hover{color:#4272db;text-decoration:underline}.gm-style .gm-iw .gm-title{font-weight:400;margin-bottom:1px}.gm-style .gm-iw .gm-basicinfo{line-height:18px;padding-bottom:12px}.gm-style .gm-iw .gm-website{padding-top:6px}.gm-style .gm-iw .gm-photos{padding-bottom:8px;-ms-user-select:none;-moz-user-select:none;-webkit-user-select:none}.gm-style .gm-iw .gm-sv,.gm-style .gm-iw .gm-ph{cursor:pointer;height:50px;width:100px;position:relative;overflow:hidden}.gm-style .gm-iw .gm-sv{padding-right:4px}.gm-style .gm-iw .gm-wsv{cursor:pointer;position:relative;overflow:hidden}.gm-style .gm-iw .gm-sv-label,.gm-style .gm-iw .gm-ph-label{cursor:pointer;position:absolute;bottom:6px;color:#fff;font-weight:400;text-shadow:rgba(0,0,0,0.7) 0 1px 4px;font-size:12px}.gm-style .gm-iw .gm-stars-b,.gm-style .gm-iw .gm-stars-f{height:13px;font-size:0}.gm-style .gm-iw .gm-stars-b{position:relative;background-position:0 0;width:65px;top:3px;margin:0 5px}.gm-style .gm-iw .gm-rev{line-height:20px;-ms-user-select:none;-moz-user-select:none;-webkit-user-select:none}.gm-style.gm-china .gm-iw .gm-rev{display:none}.gm-style .gm-iw .gm-numeric-rev{font-size:16px;color:#dd4b39;font-weight:400}.gm-style .gm-iw.gm-transit{margin-left:15px}.gm-style .gm-iw.gm-transit td{vertical-align:top}.gm-style .gm-iw.gm-transit .gm-time{white-space:nowrap;color:#676767;font-weight:bold}.gm-style .gm-iw.gm-transit img{width:15px;height:15px;margin:1px 5px 0 -20px;float:left}

我只是简单地复制并粘贴了 CSS 并将其添加到外部样式表中。一个更理想的解决方案是以编程方式获取 CSS 并通过 jQuery 应用它,但这目前适用于我的目的。

但即使我这样做了,信息窗口右上角的“x”按钮也没有出现,因为图像源是数据 URI,这是强 CSP 所不允许的。

<img src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224px%22%20height%3D%2224px%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22%23000000%22%3E%0A%20%20%20%20%3Cpath%20d%3D%22M19%206.41L17.59%205%2012%2010.59%206.41%205%205%206.41%2010.59%2012%205%2017.59%206.41%2019%2012%2013.41%2017.59%2019%2019%2017.59%2013.41%2012z%22%2F%3E%0A%20%20%20%20%3Cpath%20d%3D%22M0%200h24v24H0z%22%20fill%3D%22none%22%2F%3E%0A%3C%2Fsvg%3E%0A" style="pointer-events: none; display: block; width: 13px; height: 13px; margin: 12px;">

为了解决这个问题,我暂时禁用了 CSP,以便截取 x 按钮的屏幕截图。我实际上截了两张截图,一张是普通符号,一张是出现在mouseover 上的深色版本。我将这些保存为 GIF 并将它们存储在服务器上。

我发现domready 事件在信息窗口中没有按预期工作。我的解决方法如下:

marker.addListener('click', function() {
    infoWindow.setContent("<div class='info-window'>foobar</div>");
    infoWindow.open(map, marker);

    //check to see if the info window has loaded yet
    checkForInfoWindow();
});

function checkForInfoWindow() {
    var checkForInfoWindow = setInterval(function() {
        if ($(".info-window").length) {
            styleInfoWindow();
            clearInterval(checkForInfoWindow);
        }
    }, 1);
}

function styleInfoWindow() {

    //style the "x" button
    var button = $(".info-window").parent().parent().parent().children("button");
    $(button).html("<img src='/images/x.gif'>");
    $(button).mouseover(function() {
        $(button).html("<img src='/images/x-hover.gif'>");
    });
    $(button).mouseout(function() {
        $(button).html("<img src='/images/x.gif'>");
    });

    //get divs with a class name beginning with "gm-style-iw"
    var divs = $("div[class^='gm-style-iw']");

    //for each div
    for (var div = 0; div < divs.length; div++) {

        //for each inline css property
        for (var propertyNum = 0; propertyNum < divs[div].style.length; propertyNum++) {

            //get property and convert to camelCase
            var property = divs[div].style[propertyNum].replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); })

            //get value
            var value = divs[div].style[property];

            //apply style
            divs[div].style[property] = value;
        }

    }

}

但是“x”按钮的位置不太对,因为当有一个垂直滚动条时,“x”按钮实际上是在它上面。我通过将以下内容添加到外部 CSS 解决了这个问题:

.gm-style-iw-d{margin-top: 1rem;}

瞧!信息窗口看起来就像我实施 CSP 之前的样子。

【讨论】: