You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

纯JavaScript实现Google Maps多条件地图标记筛选方案

Google Maps 标记多条件组合筛选重构方案

原有实现通过多层嵌套if判断处理筛选逻辑,仅支持2类筛选维度时就已经出现逻辑分支复杂、扩展性差的问题,新增筛选维度需要修改大量分支代码,极易出现判断漏洞。本次重构采用配置化规则+统一校验的思路,彻底消除嵌套if判断,支持最多4类筛选维度的任意组合匹配,未设置有效值的条件自动跳过不参与校验,仅满足所有已设置条件的标记才会显示。

核心设计思路

  • 统一管理所有筛选状态,替代原有分散的独立筛选变量
  • 将每个筛选维度的「有效值判断规则」「标记匹配规则」抽离为独立配置,新增维度仅需新增配置项,无需修改核心筛选逻辑
  • 核心筛选流程固定:遍历标记→逐个校验生效规则→全部规则通过则显示标记,否则隐藏,全程无嵌套分支判断
  • 4类默认支持的筛选维度:
    • Project Type:数组存储选中的项目类型,匹配标记对应类型在选中列表内
    • Region:数组存储选中的所属区域,匹配标记对应区域在选中列表内
    • Name:字符串存储用户输入的名称关键词,匹配标记标题包含关键词(不区分大小写)
    • Value:整数存储用户输入的项目价值阈值,匹配标记项目价值大于等于阈值

重构后完整代码

// 全局标记、地图实例存储
let map;
const markers = [];
// 筛选规则配置:新增筛选维度仅需在此处添加配置,无需修改核心筛选逻辑
const filterConfig = {
  project_type: {
    value: [],
    // 判断当前筛选是否有有效值,无有效值则不参与匹配
    isActive: (val) => Array.isArray(val) && val.length > 0,
    // 单标记匹配规则
    match: (marker, selectedVals) => selectedVals.includes(marker.project_type)
  },
  region: {
    value: [],
    isActive: (val) => Array.isArray(val) && val.length > 0,
    match: (marker, selectedVals) => selectedVals.includes(marker.region)
  },
  name: {
    value: "",
    isActive: (val) => typeof val === "string" && val.trim().length > 0,
    match: (marker, keyword) => marker.project_title.toLowerCase().includes(keyword.trim().toLowerCase())
  },
  value: {
    value: null,
    isActive: (val) => typeof val === "number" && !isNaN(val) && val > 0,
    match: (marker, threshold) => Number(marker.value) >= threshold
  }
};

function initMap() {
  map = new google.maps.Map(document.getElementById("map"), {
    zoom: 5,
    center: { lat: -25.77649954803059, lng: 122.03240276153382 },
    styles: darkMapStyle,
  });

  const infoWindow = new google.maps.InfoWindow({
    content: "",
    disableAutoPan: true,
  });

  // 初始化所有标记
  projectData.forEach(item => {
    const newMarker = new google.maps.Marker({
      position: { lat: item.lat, lng: item.lng },
      title: item.project_title,
    });
    // 挂载筛选需要的所有属性到标记实例,统一转小写避免大小写匹配问题
    newMarker.project_type = item.project_type.toLowerCase();
    newMarker.region = item.region.toLocaleLowerCase();
    newMarker.project_title = item.project_title;
    newMarker.value = item.value;
    markers.push(newMarker);
  });

  // 核心筛选函数:无嵌套if判断
  function filterData() {
    markers.forEach(marker => {
      let isVisible = true;
      // 遍历所有筛选规则
      for (const [, rule] of Object.entries(filterConfig)) {
        const currentFilterValue = rule.value;
        // 无有效值的规则直接跳过
        if (!rule.isActive(currentFilterValue)) continue;
        // 不满足任意一条生效规则则隐藏
        if (!rule.match(marker, currentFilterValue)) {
          isVisible = false;
          break;
        }
      }
      marker.setMap(isVisible ? map : null);
    });
  }

  // 重置所有筛选条件
  function resetFilters() {
    // 重置复选框状态
    document.querySelectorAll(".filter").forEach(item => {
      item.checked = false;
    });
    // 重置文本、数字输入框
    document.getElementById("name-filter").value = "";
    document.getElementById("value-filter").value = "";
    // 重置所有筛选规则的存储值为初始状态
    filterConfig.project_type.value = [];
    filterConfig.region.value = [];
    filterConfig.name.value = "";
    filterConfig.value.value = null;
    // 显示所有标记
    filterData();
  }

  // 重置按钮绑定事件
  document.getElementById("reset").addEventListener("click", resetFilters);

  // 复选框类筛选(项目类型、区域)事件绑定
  document.querySelectorAll(".filter").forEach(item => {
    item.addEventListener("change", () => {
      const filterType = item.getAttribute("data-type");
      const filterValue = item.getAttribute("id").toLowerCase();
      const targetValueList = filterConfig[filterType].value;
      const valueIndex = targetValueList.indexOf(filterValue);

      if (item.checked && valueIndex === -1) {
        targetValueList.push(filterValue);
      }
      if (!item.checked && valueIndex !== -1) {
        targetValueList.splice(valueIndex, 1);
      }
      filterData();
    });
  });

  // 名称关键词输入框事件绑定
  document.getElementById("name-filter").addEventListener("input", (e) => {
    filterConfig.name.value = e.target.value;
    filterData();
  });

  // 价值阈值输入框事件绑定
  document.getElementById("value-filter").addEventListener("input", (e) => {
    const inputVal = parseInt(e.target.value.trim(), 10);
    filterConfig.value.value = isNaN(inputVal) ? null : inputVal;
    filterData();
  });

  // 初始化时重置筛选,显示所有标记
  resetFilters();
}

window.initMap = initMap;

配套HTML参考

需要新增名称、价值两个筛选维度的输入控件,示例结构如下,原有复选框筛选结构保持不变即可,data-type属性对应配置中的key值:

<div class="filter-group">
  <h3>项目名称</h3>
  <input type="text" id="name-filter" placeholder="输入项目名称关键词">
</div>
<div class="filter-group">
  <h3>项目价值阈值</h3>
  <input type="number" id="value-filter" placeholder="输入最低项目价值" min="0">
</div>

扩展说明

  • 新增筛选维度时,仅需在filterConfig中新增对应配置项,明确值存储变量、有效值判断逻辑、标记匹配规则三个部分即可,核心筛选、重置逻辑不需要做任何修改
  • 所有筛选条件默认采用AND匹配逻辑,即必须满足所有已设置的生效条件才会显示对应标记,支持任意筛选条件组合
  • 彻底消除多层嵌套if判断,每个筛选维度的逻辑独立维护,排查问题、修改规则时不需要梳理复杂的分支关系

内容的提问来源于stack exchange,提问作者Paul Wilson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 01:48:15