PAYATHON 2026

如何动态修改 HTML 元素的共享样式,统一调整 leaflet 地图标记的 opacity?

技术老齐

可以用 CSS 自定义属性保存各状态的透明度,再通过原生 JavaScript 的 style.setProperty() 修改这些值。引用对应变量的标记会自动更新,不用逐个修改元素,也不需要 jQuery。

建议继续用 class 表示 found、notfound 等状态,用 CSS 自定义属性保存用户的显示设置。

用 CSS 自定义属性控制透明度

假设地图容器的 ID 是 map:

#map {
  --found-opacity: 0.3;
}

#map .found {
  opacity: var(--found-opacity, 0.3);
}

通过 style.setProperty() 修改透明度:

const mapElement = document.getElementById("map");

mapElement.style.setProperty("--found-opacity", "0.6");

这段代码修改的是地图容器上的自定义属性。普通的 CSS 自定义属性默认由后代元素继承,所以地图内使用 var(--found-opacity) 的元素都会采用新值。之后添加的标记只要带有 found class,也会使用当前设置。

var(--found-opacity, 0.3) 中的 0.3 是变量未定义时的回退值。示例中的数值范围为 0 到 1:0 表示完全透明,1 表示完全不透明,0.3 对应 30% 不透明度。

如果要让设置作用于整个页面,可以把变量定义在 :root 上,再这样修改:

document.documentElement.style.setProperty(
  "--found-opacity",
  "0.6"
);

变量应在定义它的位置修改。如果 #map 自己定义了同名变量,就会优先使用自己的值。

通过滑块实时调整

在地图容器外放置一个滑块:

<label for="found-opacity">已找到物品的不透明度</label>
<input
  id="found-opacity"
  type="range"
  min="0"
  max="1"
  step="0.05"
  value="0.3"
>

等这些 DOM 元素创建后,执行以下代码:

const mapElement = document.getElementById("map");
const opacityInput = document.getElementById("found-opacity");

function updateFoundOpacity() {
  mapElement.style.setProperty(
    "--found-opacity",
    opacityInput.value
  );
}

opacityInput.addEventListener("input", updateFoundOpacity);
updateFoundOpacity();

拖动滑块时,input 事件会持续触发。每次只需更新一个变量,浏览器就会重新计算相关元素的样式。

现有的状态切换代码可以保留,添加、移除 class 的分支可以简化为:

div.classList.toggle("found", setFound);

第二个参数为 true 时添加 class,为 false 时移除。

给互斥状态分别设置样式

增加其他状态时,可以为每个状态设置一个变量:

#map {
  --found-opacity: 0.3;
  --notfound-opacity: 1;
  --notfindable-opacity: 0.1;
}

#map .found {
  opacity: var(--found-opacity);
}

#map .notfound {
  opacity: var(--notfound-opacity);
}

#map .notfindable {
  opacity: var(--notfindable-opacity);
}

各个设置控件修改对应的变量即可:

mapElement.style.setProperty("--found-opacity", "0");
mapElement.style.setProperty("--notfound-opacity", "0.8");
mapElement.style.setProperty("--notfindable-opacity", "0.2");

CSS 不会自动保证这些 class 互斥。切换状态时,应先移除旧状态,再添加新状态。否则,多个规则同时命中,结果就会受 CSS 层叠顺序影响。

对于普通的 Leaflet L.Marker,可以通过 getElement() 获取标记根元素:

const markerStates = ["found", "notfound", "notfindable"];

function applyMarkerState(marker, state) {
  if (!markerStates.includes(state)) {
    throw new Error(`Unknown marker state: ${state}`);
  }

  const element = marker.getElement();

  // 标记尚未加入地图时,可能还没有对应的 DOM 元素。
  if (!element) return;

  element.classList.remove(...markerStates);
  element.classList.add(state);
}

把 class 加在标记根元素上,PNG 图标和包含 SVG 的 L.DivIcon 就能使用同一套规则。根元素内部的 SVG 也会随整个标记一起改变透明度。

业务状态应保存在物品数据中,再同步到 DOM。假设 marker 已经创建,可以这样处理:

const item = {
  state: "notfound"
};

function renderState() {
  applyMarkerState(marker, item.state);
}

marker.on("add", renderState);

marker.on("contextmenu", () => {
  if (item.state === "notfindable") return;

  item.state = item.state === "found" ? "notfound" : "found";
  renderState();
});

renderState();

监听 add 可以在标记加入或重新加入地图时恢复状态。如果调用 setIcon() 更换图标后产生了新的 DOM 元素,也需要再执行一次 renderState()。

如果多个标记代表同一物品,建议按物品 ID 保存它们的引用,在状态变化时同步更新。alt 是替代文本,不适合作为业务对象的唯一标识。直接将含有引号等字符的 alt 值拼接到选择器中,还可能导致选择器失效。

留意 Leaflet 写入的内联样式

如果变量已经改变,标记外观却没有变化,可以检查标记元素上的 style.opacity。Leaflet 的 opacity 选项、setOpacity() 或图标插件可能会写入内联透明度,覆盖普通样式表中的 opacity。

如果由 CSS 统一管理状态透明度,可以在标记根元素上覆盖普通内联样式:

#map .leaflet-marker-icon.found {
  opacity: var(--found-opacity, 0.3) !important;
}

其他状态也可以按需使用相同写法。这里的 !important 让状态样式优先于普通内联透明度。采用这种方式后,应避免再用 setOpacity() 管理同一元素的状态透明度,以免两套设置相互干扰。

也可以完全使用 Leaflet API:

marker.setOpacity(0.3);

如果已经集中管理标记对象,并且希望连同标记阴影一起调整,这种方式比较合适。用户改变全局设置时,需要遍历相应状态的标记并调用 setOpacity(),新标记也要应用当前设置。只修改图标元素的 CSS,不会自动同步独立的阴影元素。

这些做法适用于 DOM 标记。L.CircleMarker、其他 Path 或 Canvas 绘制对象应使用对应的样式 API,不能直接套用 DOM class 的处理方式。

完全透明与隐藏的区别

opacity: 0 会让图标看不见,但元素仍然存在,也仍可能接收鼠标事件。如果隐藏后不应响应指针操作,还需要处理交互,例如切换一个专用 class:

#map .leaflet-marker-icon.is-hidden {
  opacity: 0 !important;
  pointer-events: none;
}

pointer-events: none 只处理指针命中。如果还要阻止键盘聚焦,应另行处理焦点,或通过 Leaflet 从地图中移除该标记。这样隐藏的标记也无法再通过右键恢复,因此需要在设置控件等仍可操作的位置提供恢复入口。

如果确实需要直接修改共享 CSS 规则,可以使用 CSSOM 的 CSSStyleRule.style.setProperty()。不过,查找规则和处理跨域样式表的访问限制会增加代码。对于规则固定、数值由用户调整的需求,CSS 自定义属性更容易维护。

备注:内容仅供参考。