Popup Position
Runnable Leaflet example: Popup Position. Open the live demo, read the complete source, and copy the file into your project. No build step or API key.
single HTML file · CDN dependencies onlyinterface may contain Chinese labels
Source
<!--
示例:Popup 定位
要点:autoPan / keepInView / offset / maxWidth 对边缘气泡的影响、每次按新选项重建气泡
依赖:Leaflet 1.9.4 · jsDelivr CDN
-->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta name="robots" content="noindex,follow">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Popup 定位</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.css">
<style>
html,body{height:100%;margin:0}
body{display:flex;flex-direction:column;overflow:hidden;
font:13px/1.6 -apple-system,"Segoe UI","PingFang SC",sans-serif}
.bar{flex:0 0 auto;padding:8px 12px;background:#f6f7f9;border-bottom:1px solid #e3e6ea;
display:flex;gap:8px;align-items:center;flex-wrap:wrap;font-size:12px;color:#58606e}
.bar button{font:inherit;font-size:12px;padding:4px 10px;border-radius:4px;cursor:pointer;
border:1px solid #ccd1d9;background:#fff;color:#16181d}
.bar button:hover{background:#eef0f3}
.bar button.on{background:#1f5fd0;border-color:#1f5fd0;color:#fff}
.bar b{color:#16181d;font-weight:600;font-variant-numeric:tabular-nums}
.bar .sep{width:1px;height:16px;background:#dfe3e9}
#map{flex:1 1 auto;min-height:0}
.pin{width:14px;height:14px;border-radius:50%;background:#c2410c;border:2px solid #fff;
box-shadow:0 0 0 1px rgba(0,0,0,.28)}
.pop{font-size:12px;line-height:1.6;color:#3b4350}
.pop h4{margin:0 0 4px;font-size:13px;color:#16181d}
.pop .kv{color:#58606e}
</style>
</head>
<body>
<div class="bar">
<button id="pan" class="on">autoPan:开</button>
<button id="keep">keepInView:关</button>
<button id="width">宽度 240</button>
<button id="offset">偏移 -10</button>
<button data-act="open">打开气泡</button>
<span class="sep"></span>
<b id="readout">autoPan ✓ · keepInView ✗ · 240px · [0,-10]</b>
</div>
<div id="map"></div>
<script src="https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
(function () {
'use strict';
var map = L.map('map', { center: [39.907359, 116.391263], zoom: 13 });
L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}', {
maxZoom: 19,
attribution: 'Tiles © Esri'
}).addTo(map);
/* 落点贴住右边缘:气泡必定越界,正好用来看 autoPan 的效果 */
var edge = map.containerPointToLatLng([map.getSize().x - 36, map.getSize().y / 2]);
L.marker(edge, {
icon: L.divIcon({
className: '',
html: '<div class="pin"></div>',
iconSize: [14, 14],
iconAnchor: [7, 7]
})
}).addTo(map);
var opts = {
autoPan: true,
keepInView: false,
maxWidth: 240,
minWidth: 120,
offset: [0, -10],
closeButton: true
};
var popup = null;
var content =
'<div class="pop"><h4>王府井步行街监测点</h4>' +
'<div class="kv">编号 A-0327 · 客流 2.4 万/日</div>' +
'<div class="kv">设备状态正常,最近上报 8 分钟前</div></div>';
function readout() {
document.getElementById('readout').textContent =
'autoPan ' + (opts.autoPan ? '✓' : '✗') +
' · keepInView ' + (opts.keepInView ? '✓' : '✗') +
' · ' + opts.maxWidth + 'px' +
' · [' + opts.offset[0] + ',' + opts.offset[1] + ']';
}
function open() {
if (popup) map.closePopup(popup);
/* 气泡选项在打开时读取,改完必须重建 */
popup = L.popup({
autoPan: opts.autoPan,
keepInView: opts.keepInView,
maxWidth: opts.maxWidth,
minWidth: opts.minWidth,
offset: opts.offset,
closeButton: opts.closeButton,
autoPanPadding: [20, 20]
})
.setLatLng(edge)
.setContent(content)
.openOn(map);
}
document.getElementById('pan').addEventListener('click', function () {
opts.autoPan = !opts.autoPan;
this.textContent = 'autoPan:' + (opts.autoPan ? '开' : '关');
this.classList.toggle('on', opts.autoPan);
readout();
open();
});
document.getElementById('keep').addEventListener('click', function () {
opts.keepInView = !opts.keepInView;
this.textContent = 'keepInView:' + (opts.keepInView ? '开' : '关');
this.classList.toggle('on', opts.keepInView);
readout();
open();
});
var wide = true;
document.getElementById('width').addEventListener('click', function () {
wide = !wide;
opts.maxWidth = wide ? 240 : 110;
this.textContent = '宽度 ' + opts.maxWidth;
readout();
open();
});
var far = true;
document.getElementById('offset').addEventListener('click', function () {
far = !far;
opts.offset = far ? [0, -10] : [0, -48];
this.textContent = '偏移 ' + opts.offset[1];
readout();
open();
});
document.querySelector('[data-act="open"]').addEventListener('click', open);
map.on('popupopen', function () {
console.log('气泡已打开,中心', map.getCenter().lng.toFixed(4));
});
/* 打开一次,直接看到边缘定位效果 */
open();
})();
</script>
</body>
</html>
Language: the code and this page are in English; the full tutorial and API notes for this example are in Chinese. The demo itself is the authoritative version — it runs exactly as shown.