Event Debug
Runnable Leaflet example: Event Debug. 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
<!--
示例:事件调试
要点:批量监听 Map 事件、事件对象关键字段、日志面板限行与暂停
依赖: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>事件调试</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 label{display:inline-flex;gap:4px;align-items:center;cursor:pointer}
#map{flex:1 1 auto;min-height:0}
.logbox{position:absolute;right:12px;top:12px;width:330px;max-height:calc(100% - 24px);
z-index:700;background:rgba(22,24,29,.92);color:#e9edf2;border-radius:6px;
display:flex;flex-direction:column;font:11px/1.6 Consolas,monospace}
.logbox h4{margin:0;padding:7px 10px;font:600 11px/1.4 sans-serif;color:#9fb0c7;
border-bottom:1px solid rgba(255,255,255,.12)}
.logbox .list{overflow:auto;padding:4px 10px 8px}
.logbox .row{display:flex;gap:8px;padding:1px 0;white-space:nowrap;overflow:hidden;
text-overflow:ellipsis;border-bottom:1px dashed rgba(255,255,255,.06)}
.logbox .t{color:#6b7a8d}
.logbox .n{color:#63b3ff;min-width:78px}
.logbox .d{color:#a6b4c4;overflow:hidden;text-overflow:ellipsis}
</style>
</head>
<body>
<div class="bar">
<button id="pause">暂停记录</button>
<button id="clear">清空</button>
<label><input type="checkbox" id="moves"> 记录 move</label>
<span id="count">0 条</span>
</div>
<div id="map">
<div class="logbox">
<h4>事件流</h4>
<div class="list" id="list"></div>
</div>
</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: 12 });
var base = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}', {
maxZoom: 19,
attribution: 'Tiles © Esri'
}).addTo(map);
var marker = L.marker([39.9147, 116.4106], {
icon: L.divIcon({ className: '', html: '<div style="width:14px;height:14px;border-radius:50%;background:#c2410c;border:2px solid #fff"></div>', iconSize: [14, 14], iconAnchor: [7, 7] })
}).bindPopup('王府井 · 点击这个标记会触发 popupopen').addTo(map);
var list = document.getElementById('list');
var countEl = document.getElementById('count');
var paused = false;
var withMoves = false;
var total = 0;
var NAMES = [
'click', 'preclick', 'dblclick', 'mousedown', 'mouseup', 'contextmenu',
'movestart', 'moveend', 'zoomstart', 'zoomend',
'dragstart', 'dragend', 'layeradd', 'layerremove',
'popupopen', 'popupclose', 'resize'
];
function pad(n) { return n < 10 ? '0' + n : String(n); }
function describe(e) {
var parts = [];
if (e.latlng) parts.push('ll ' + e.latlng.lat.toFixed(4) + ',' + e.latlng.lng.toFixed(4));
if (e.containerPoint) parts.push('px ' + Math.round(e.containerPoint.x) + ',' + Math.round(e.containerPoint.y));
if (e.center) parts.push('c ' + e.center.lat.toFixed(3) + ',' + e.center.lng.toFixed(3));
if (e.zoom !== undefined && typeof e.zoom === 'number') parts.push('z' + e.zoom);
if (e.layer) parts.push('layer#' + L.Util.stamp(e.layer));
if (e.originalEvent && e.originalEvent.button !== undefined) parts.push('btn' + e.originalEvent.button);
return parts.join(' · ');
}
function push(name, e) {
if (paused) return;
total += 1;
countEl.textContent = total + ' 条';
var d = new Date();
var row = document.createElement('div');
row.className = 'row';
row.innerHTML = '<span class="t">' + pad(d.getHours()) + ':' + pad(d.getMinutes()) +
':' + pad(d.getSeconds()) + '</span><span class="n">' + name + '</span>' +
'<span class="d">' + describe(e) + '</span>';
list.insertBefore(row, list.firstChild);
while (list.childElementCount > 40) {
list.removeChild(list.lastChild);
}
row.title = name + ' ' + describe(e);
console.log('[leaflet]', name, describe(e));
}
NAMES.forEach(function (name) {
map.on(name, function (e) { push(name, e); });
});
map.on('move', function (e) {
if (withMoves) push('move', e);
});
document.getElementById('moves').addEventListener('change', function () {
withMoves = this.checked;
if (withMoves) {
push('move', { center: map.getCenter(), zoom: map.getZoom() });
}
});
document.getElementById('pause').addEventListener('click', function () {
paused = !paused;
this.textContent = paused ? '继续记录' : '暂停记录';
this.classList.toggle('on', paused);
});
document.getElementById('clear').addEventListener('click', function () {
list.innerHTML = '';
total = 0;
countEl.textContent = '0 条';
});
/* 初始触发一次,确认日志通道正常 */
setTimeout(function () {
push('load', { center: map.getCenter(), zoom: map.getZoom() });
document.getElementById('count').textContent = '1 条';
}, 400);
})();
</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.