Features Under Pointer
Runnable MapLibre GL JS example: Features Under Pointer. 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
<!--
示例:查询指针下的要素
核心逻辑:mousemove 里 queryRenderedFeatures 按点查询,结果写入侧栏并同步高亮
数据:页面内构造的 6 个分区多边形,部分刻意重叠
依赖:MapLibre GL JS 4 · jsDelivr CDN · demotiles 样式
-->
<!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/maplibre-gl@4/dist/maplibre-gl.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;color:#16181d}
.bar{flex:0 0 auto;display:flex;gap:14px;align-items:center;padding:8px 12px;
background:#f6f7f9;border-bottom:1px solid #e3e6ea;font-size:12px;color:#58606e;flex-wrap:wrap}
.bar b{color:#16181d;font-weight:600}
.wrap{flex:1 1 auto;min-height:0;display:flex}
#map{flex:1 1 auto;min-width:0}
.side{flex:0 0 230px;border-left:1px solid #e3e6ea;background:#fff;padding:12px;overflow:auto;
font-size:12px;color:#3a4250}
.side h3{margin:0 0 8px;font-size:13px;color:#16181d}
.item{padding:7px 9px;border-radius:5px;background:#f4f6f9;margin-bottom:6px}
.item b{display:block;font-size:12.5px;color:#16181d}
.item span{color:#77818f}
.empty{color:#98a2b3;font-size:12px}
</style>
</head>
<body>
<div class="bar">
<span>分区 <b>6</b></span>
<span>指针下 <b id="n">0</b> 个</span>
<span>重叠处会同时返回多个要素</span>
</div>
<div class="wrap">
<div id="map"></div>
<div class="side">
<h3>指针下的要素</h3>
<div id="list"><div class="empty">把指针移到色块上</div></div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/maplibre-gl@4/dist/maplibre-gl.js"></script>
<script>
(function () {
'use strict';
if (typeof maplibregl === 'undefined') {
document.body.innerHTML =
'<div style="padding:24px;color:#b42318">MapLibre 加载失败,请检查能否访问 cdn.jsdelivr.net</div>';
return;
}
function block(lng, lat, w, h) {
return [[
[lng - w, lat - h], [lng + w, lat - h],
[lng + w, lat + h], [lng - w, lat + h],
[lng - w, lat - h]
]];
}
var zones = [
{ id: 1, name: '华北片区', q: [116, 39, 5, 3.4] },
{ id: 2, name: '华东片区', q: [120, 32, 5.5, 3.2] },
{ id: 3, name: '华南片区', q: [113, 24, 4.5, 2.8] },
{ id: 4, name: '西南片区', q: [104, 29, 6, 3.6] },
{ id: 5, name: '重叠片区 A', q: [109, 34, 4, 2.6] },
{ id: 6, name: '重叠片区 B', q: [111, 35, 4, 2.6] }
];
var geojson = {
type: 'FeatureCollection',
features: zones.map(function (z) {
return {
type: 'Feature',
properties: { id: z.id, name: z.name },
geometry: { type: 'Polygon', coordinates: block.apply(null, z.q) }
};
})
};
var map = new maplibregl.Map({
container: 'map',
style: 'https://demotiles.maplibre.org/style.json',
center: [110, 33],
zoom: 3.4,
attributionControl: { compact: true, customAttribution: 'MapLibre demo tiles' }
});
map.addControl(new maplibregl.NavigationControl(), 'top-right');
var listEl = document.getElementById('list');
var nEl = document.getElementById('n');
map.on('load', function () {
map.addSource('zones', {
type: 'geojson',
data: geojson,
promoteId: 'id'
});
map.addLayer({
id: 'zones',
type: 'fill',
source: 'zones',
paint: {
'fill-color': [
'case',
['boolean', ['feature-state', 'hover'], false],
'#ff922b',
'#4dabf7'
],
'fill-opacity': 0.35
}
});
map.addLayer({
id: 'zones-line',
type: 'line',
source: 'zones',
paint: {
'line-color': '#1c4e80',
'line-width': 1
}
});
});
var hovered = null;
function render(features) {
nEl.textContent = features.length;
if (!features.length) {
listEl.innerHTML = '<div class="empty">把指针移到色块上</div>';
return;
}
listEl.innerHTML = features.map(function (f) {
return '<div class="item"><b>' + f.properties.name + '</b>' +
'<span>要素 id ' + f.properties.id + '</span></div>';
}).join('');
}
map.on('mousemove', function (e) {
if (!map.getLayer('zones')) return;
var hit = map.queryRenderedFeatures(e.point, { layers: ['zones'] });
var next = hit.length ? hit[0].properties.id : null;
if (next !== hovered) {
if (hovered !== null) {
map.setFeatureState({ source: 'zones', id: hovered }, { hover: false });
}
hovered = next;
if (hovered !== null) {
map.setFeatureState({ source: 'zones', id: hovered }, { hover: true });
}
}
map.getCanvas().style.cursor = hit.length ? 'pointer' : '';
render(hit);
});
map.on('mouseout', function () {
if (hovered !== null) {
map.setFeatureState({ source: 'zones', id: hovered }, { hover: false });
hovered = null;
}
render([]);
});
map.on('error', function (e) {
if (e.error) console.warn('地图错误:' + e.error.message);
});
})();
</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.