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

如何在ObservableHQ中创建适配大量选中项的可搜索多选框?

在ObservableHQ中实现紧凑型可搜索多选框

核心思路

通过自定义viewof组件,将大量选中项折叠为数量摘要(如“已选中60项”),仅在需要时展开查看详情,同时集成搜索过滤功能,适配Observable的响应式环境,避免直接操作DOM导致的兼容性问题。

实现代码

viewof selectedItems = {
  const options = Array.from({length: 100}, (_, i) => `选项${i+1}`); // 模拟100个选项
  const container = document.createElement("div");
  container.style.maxWidth = "400px";
  
  // 搜索框
  const searchInput = document.createElement("input");
  searchInput.type = "text";
  searchInput.placeholder = "搜索选项...";
  searchInput.style.width = "100%";
  searchInput.style.padding = "8px";
  searchInput.style.boxSizing = "border-box";
  container.appendChild(searchInput);
  
  // 选中状态摘要
  const summary = document.createElement("div");
  summary.style.margin = "8px 0";
  summary.style.padding = "8px";
  summary.style.border = "1px solid #eee";
  summary.style.borderRadius = "4px";
  summary.style.cursor = "pointer";
  let isExpanded = false;
  
  // 选项列表容器
  const listContainer = document.createElement("div");
  listContainer.style.maxHeight = "200px";
  listContainer.style.overflowY = "auto";
  listContainer.style.display = "none";
  container.appendChild(listContainer);
  
  // 全选复选框
  const selectAll = document.createElement("label");
  selectAll.style.display = "block";
  selectAll.style.padding = "4px 8px";
  selectAll.innerHTML = `<input type="checkbox"> 全选`;
  listContainer.appendChild(selectAll);
  
  // 渲染选项列表
  const renderOptions = (filter = "") => {
    listContainer.innerHTML = "";
    listContainer.appendChild(selectAll);
    const filtered = options.filter(opt => opt.toLowerCase().includes(filter.toLowerCase()));
    filtered.forEach(opt => {
      const label = document.createElement("label");
      label.style.display = "block";
      label.style.padding = "4px 8px";
      label.innerHTML = `<input type="checkbox" value="${opt}" ${selected.has(opt) ? "checked" : ""}> ${opt}`;
      label.querySelector("input").addEventListener("change", updateSelected);
      listContainer.appendChild(label);
    });
  };
  
  // 选中集合
  const selected = new Set(options); // 默认全选
  const updateSelected = () => {
    selected.clear();
    // 收集选中项
    Array.from(listContainer.querySelectorAll("input[type='checkbox']:not(:first-child):checked")).forEach(el => selected.add(el.value));
    // 更新全选状态
    selectAll.querySelector("input").checked = selected.size === options.length;
    // 更新摘要
    summary.textContent = selected.size === 0 ? "未选中任何选项" : 
      selected.size > 10 ? `已选中${selected.size}项(点击展开查看)` : 
      `已选中:${Array.from(selected).join(", ")}`;
    // 触发Observable的更新
    container.dispatchEvent(new CustomEvent("input"));
  };
  
  // 初始更新
  updateSelected();
  
  // 搜索事件
  searchInput.addEventListener("input", () => {
    renderOptions(searchInput.value);
    // 保持选中状态
    Array.from(listContainer.querySelectorAll("input[type='checkbox']:not(:first-child)")).forEach(el => {
      if (selected.has(el.value)) el.checked = true;
    });
  });
  
  // 摘要点击展开/折叠
  summary.addEventListener("click", () => {
    isExpanded = !isExpanded;
    listContainer.style.display = isExpanded ? "block" : "none";
    if (isExpanded) renderOptions(searchInput.value);
  });
  
  // 全选事件
  selectAll.querySelector("input").addEventListener("change", (e) => {
    const checkboxes = listContainer.querySelectorAll("input[type='checkbox']:not(:first-child)");
    checkboxes.forEach(el => el.checked = e.target.checked);
    updateSelected();
  });
  
  // 初始渲染
  renderOptions();
  
  container.appendChild(summary);
  
  // 返回值
  Object.defineProperty(container, "value", {get: () => Array.from(selected)});
  return container;
}

关键特性说明

  • 紧凑显示:当选中项超过10个时,自动折叠为数量摘要,点击可展开查看具体选项
  • 搜索过滤:实时过滤选项列表,同时保留已选中状态
  • 全选功能:支持一键全选/取消全选,适配过滤后的列表
  • Observable友好:通过viewof和自定义input事件实现响应式更新,避免直接DOM操作的兼容性问题

内容的提问来源于stack exchange,提问作者Jakub.Novotny

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 10:46:16