LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

使用 HTML + JavaScript 实现分页下拉选择器(附完整代码)

admin
2025年12月17日 13:15 本文热度 1346
在现代Web应用中,用户界面的设计直接影响着用户的体验质量。当下拉选择框的数据量较大时,传统的滚动条方式不仅效率低下,而且难以快速定位目标选项。为此,我们需要一款带有分页功能的下拉选择器组件,它不仅能有效管理大量选项,还能提供流畅的搜索体验。本文将详细介绍如何使用 HTML、CSS 和 JavaScript 实现分页下拉选择器。

效果演示

这个组件集成了多项实用功能:包括选项搜索、分页导航、高亮选项等特性,适用于需要处理大量选项的选择场景。用户通过点击选择框打开面板,然后可通过搜索框筛选选项,利用分页按钮浏览不同页面的内容,最终点击所需选项完成选择。

页面结构

整个组件由以下几个核心部分组成:

选择框

选择框显示当前选中值,默认为“请选择选项...”占位。
<div class="select-header" id="selectHeader">  <span id="selectedText">请选择选项...</span></div>

下拉面板

下拉面板包含搜索框、选项列表和分页控件。
<div class="select-panel" id="selectPanel">  <div class="search">    <input type="text" id="searchInput" placeholder="搜索选项...">  </div>  <div class="options" id="options"></div>  <div class="pagination">    <button id="prevBtn">上一页</button>    <span class="pagination-info" id="pageInfo">1/1页 (0项)</span>    <button id="nextBtn">下一页</button>  </div></div>

核心功能实现

事件绑定机制

通过 bindEvents() 方法统一注册各类交互事件,其中使用了防抖(debounce)技术优化搜索性能,避免频繁触发过滤操作。
function bindEvents() {  selectHeader.addEventListener('click', togglePanel);  document.addEventListener('click'(e) => {    if (!selectContainer.contains(e.target)) hidePanel();  });  searchInput.addEventListener('input'debounce(filterOptions, 300));  prevBtn.addEventListener('click'() => goToPage(state.currentPage - 1));  nextBtn.addEventListener('click'() => goToPage(state.currentPage + 1));}

选项渲染

renderOptions() 函数负责根据当前页码计算应显示的选项范围,并动态创建DOM节点展示这些选项。
function renderOptions() {  optionsEl.innerHTML = '';  if (state.filteredOptions.length === 0) {    optionsEl.innerHTML = '<div class="no-results">未找到匹配的选项</div>';    return;  }  var start = (state.currentPage - 1) * state.pageSize;  var end = start + state.pageSize;  var pageOptions = state.filteredOptions.slice(start, end);  pageOptions.forEach(option => {    var el = document.createElement('div');    el.className = 'option';    el.textContent = option.text;    if (state.selectedOption?.value === option.value) {      el.classList.add('selected');    }    el.addEventListener('click'() => selectOption(option));    optionsEl.appendChild(el);  });}

分页更新

每当选项发生变化时都会调用 updatePagination() 函数来刷新分页信息,同时控制前后页按钮的可用状态。
function updatePagination() {  var total = state.filteredOptions.length;  var totalPages = Math.ceil(total / state.pageSize) || 1;  pageInfo.textContent = `${state.currentPage}/${totalPages}页 (${total}项)`;  prevBtn.disabled = state.currentPage <= 1;  nextBtn.disabled = state.currentPage >= totalPages || totalPages === 0;}

搜索过滤功能

搜索过程模拟了网络延迟效果,用户输入关键字后自动触发过滤逻辑,重置到第一页并更新视图。
async function filterOptions(e) {  var searchTerm = e.target.value;  setLoading(true);  try {    await new Promise(resolve => setTimeout(resolve, 200));    state.filteredOptions = searchTerm ?      state.allOptions.filter(opt => opt.text.toLowerCase().includes(searchTerm.toLowerCase())) :      [...state.allOptions];    state.currentPage = 1;  } finally {    setLoading(false);    renderOptions();    updatePagination();  }}

扩展建议

  • 远程数据加载:支持从服务器获取选项数据

  • 键盘导航支持:添加方向键切换选项功能,回车键确认选择操作

  • 多选模式:提供复选框形式允许多个选项被选中,显示已选项目计数和标签

  • 自定义模板:允许用户自定义选项显示模板,支持图标、描述等丰富内容

完整代码

git地址:https://gitee.com/ironpro/hjdemo/blob/master/select-paginate/index.html
<!DOCTYPE html><html lang="zh-CN"><head>  <meta charset="utf-8">  <meta name="viewport" content="width=device-width, initial-scale=1.0">  <title>分页下拉选择器</title>  <style>      * {          margin0;          padding0;          box-sizing: border-box;      }      body {          background#f5f5f5;          min-height100vh;          padding20px;      }      .container {          max-width600px;          margin0 auto;          background: white;          border-radius15px;          box-shadow0 20px 40px rgba(0,0,0,0.1);          overflow: hidden;      }      .header {          background#007bff;          color: white;          padding20px;          text-align: center;      }      .header h1 {          font-size24px;          font-weight500;      }      .main {          padding30px;          min-height500px;      }      .select-container {          position: relative;          width100%;          max-width320px;          margin0 auto;      }      .select-header {          padding14px 16px;          border2px solid #e1e5e9;          background-color#ffffff;          cursor: pointer;          display: flex;          justify-content: space-between;          align-items: center;          border-radius8px;          transition: all 0.3s ease;          box-shadow0 1px 3px rgba(0000.05);      }      .select-header:hover {          border-color#007bff;          box-shadow0 2px 6px rgba(01232550.15);      }      .select-header.active {          border-color#007bff;          box-shadow0 2px 6px rgba(01232550.25);      }      .select-header::after {          content"▼";          font-size14px;          color#6c757d;          transition: transform 0.3s ease;      }      .select-header.active::after {          transformrotate(180deg);      }      .select-panel {          position: absolute;          top100%;          left0;          right0;          border1px solid #e1e5e9;          background-color#fff;          z-index1000;          max-height300px;          box-shadow0 4px 14px rgba(0000.15);          border-radius8px;          margin-top5px;          opacity0;          visibility: hidden;          transformtranslateY(-10px);          transition: all 0.3s ease;      }      .select-panel.show {          opacity1;          visibility: visible;          transformtranslateY(0);      }      .search {          padding14px;          border-bottom1px solid #f0f0f0;      }      .search input {          box-sizing: border-box;          width100%;          padding10px 14px;          border1px solid #e1e5e9;          border-radius6px;          font-size14px;          transition: border-color 0.3s ease, box-shadow 0.3s ease;      }      .search input:focus {          outline: none;          border-color#007bff;          box-shadow0 0 0 3px rgba(01232550.1);      }      .options {          max-height180px;          overflow-y: auto;      }      .options::-webkit-scrollbar {          width6px;      }      .options::-webkit-scrollbar-track {          background#f8f9fa;          border-radius3px;      }      .options::-webkit-scrollbar-thumb {          background#c1c9d2;          border-radius3px;      }      .options::-webkit-scrollbar-thumb:hover {          background#a6b1bb;      }      .option {          padding10px 16px;          cursor: pointer;          transition: background-color 0.2s ease;          border-left3px solid transparent;      }      .option:hover {          background-color#f8f9fa;          border-left-color#007bff;      }      .option.selected {          background-color#e7f3ff;          border-left-color#007bff;          color#004085;          font-weight500;      }      .pagination {          display: flex;          justify-content: space-between;          align-items: center;          padding14px;          border-top1px solid #f0f0f0;          background-color#fafbfc;          border-radius0 0 8px 8px;      }      .pagination button {          background#ffffff;          border1px solid #e1e5e9;          cursor: pointer;          padding6px 14px;          border-radius4px;          font-size13px;          transition: all 0.2s ease;          min-width70px;      }      .pagination button:hover:not(:disabled) {          background-color#007bff;          color: white;          border-color#007bff;      }      .pagination button:disabled {          color#adb5bd;          cursor: not-allowed;          background-color#f8f9fa;      }      .pagination-info {          color#6c757d;          font-size13px;          font-weight500;      }      .no-results {          padding20px;          text-align: center;          color#6c757d;          font-style: italic;      }      .loading {          padding20px;          text-align: center;          color#6c757d;      }      .loading::after {          content"";          display: inline-block;          width20px;          height20px;          border2px solid #e1e5e9;          border-top2px solid #007bff;          border-radius50%;          animation: spin 1s linear infinite;          margin-left10px;          vertical-align: middle;      }      @keyframes spin {          0% { transformrotate(0deg); }          100% { transformrotate(360deg); }      }  </style></head><body><div class="container">  <div class="header">    <h1>分页下拉选择器</h1>  </div>  <div class="main">    <div class="select-container" id="selectContainer">      <div class="select-header" id="selectHeader">        <span id="selectedText">请选择选项...</span>      </div>      <div class="select-panel" id="selectPanel">        <div class="search">          <input type="text" id="searchInput" placeholder="搜索选项...">        </div>        <div class="options" id="options"></div>        <div class="pagination">          <button id="prevBtn">上一页</button>          <span class="pagination-info" id="pageInfo">1/1页 (0项)</span>          <button id="nextBtn">下一页</button>        </div>      </div>    </div>  </div></div>
<script>  var selectContainer = document.getElementById('selectContainer');  var selectHeader = document.getElementById('selectHeader');  var selectPanel = document.getElementById('selectPanel');  var optionsEl = document.getElementById('options');  var searchInput = document.getElementById('searchInput');  var prevBtn = document.getElementById('prevBtn');  var nextBtn = document.getElementById('nextBtn');  var pageInfo = document.getElementById('pageInfo');  var selectedText = document.getElementById('selectedText');
  var state = {    allOptions: [],    filteredOptions: [],    selectedOptionnull,    currentPage1,    pageSize6  };
  function init(options = []) {    state.allOptions = options;    state.filteredOptions = [...options];    bindEvents();    renderOptions();    updatePagination();  }
  function bindEvents() {    selectHeader.addEventListener('click', togglePanel);    document.addEventListener('click'(e) => {      if (!selectContainer.contains(e.target)) hidePanel();    });    searchInput.addEventListener('input'debounce(filterOptions, 300));    prevBtn.addEventListener('click'() => goToPage(state.currentPage - 1));    nextBtn.addEventListener('click'() => goToPage(state.currentPage + 1));  }
  function togglePanel() {    selectPanel.classList.contains('show') ? hidePanel() : showPanel();  }
  function showPanel() {    selectPanel.classList.add('show');    selectHeader.classList.add('active');    searchInput.focus();  }
  function hidePanel() {    selectPanel.classList.remove('show');    selectHeader.classList.remove('active');  }
  function debounce(func, wait) {    var timeout;    return function executedFunction(...args) {      var later = () => {        clearTimeout(timeout);        func(...args);      };      clearTimeout(timeout);      timeout = setTimeout(later, wait);    };  }
  async function filterOptions(e) {    var searchTerm = e.target.value;    setLoading(true);    try {      await new Promise(resolve => setTimeout(resolve, 200));      state.filteredOptions = searchTerm ?        state.allOptions.filter(opt => opt.text.toLowerCase().includes(searchTerm.toLowerCase())) :        [...state.allOptions];      state.currentPage = 1;    } finally {      setLoading(false);      renderOptions();      updatePagination();    }  }
  function setLoading(loading) {    if (loading) {      optionsEl.innerHTML = '<div class="loading">搜索中...</div>';      prevBtn.disabled = true;      nextBtn.disabled = true;    }  }
  function renderOptions() {    optionsEl.innerHTML = '';    if (state.filteredOptions.length === 0) {      optionsEl.innerHTML = '<div class="no-results">未找到匹配的选项</div>';      return;    }    var start = (state.currentPage - 1) * state.pageSize;    var end = start + state.pageSize;    var pageOptions = state.filteredOptions.slice(start, end);    pageOptions.forEach(option => {      var el = document.createElement('div');      el.className = 'option';      el.textContent = option.text;      if (state.selectedOption?.value === option.value) {        el.classList.add('selected');      }      el.addEventListener('click'() => selectOption(option));      optionsEl.appendChild(el);    });  }
  function updatePagination() {    var total = state.filteredOptions.length;    var totalPages = Math.ceil(total / state.pageSize) || 1;    pageInfo.textContent = `${state.currentPage}/${totalPages}页 (${total}项)`;    prevBtn.disabled = state.currentPage <= 1;    nextBtn.disabled = state.currentPage >= totalPages || totalPages === 0;  }
  function goToPage(page) {    var totalPages = Math.ceil(state.filteredOptions.length / state.pageSize) || 1;    if (page >= 1 && page <= totalPages) {      state.currentPage = page;      renderOptions();      updatePagination();    }  }
  function selectOption(option) {    state.selectedOption = option;    selectedText.textContent = option.text;    hidePanel();    selectContainer.dispatchEvent(new CustomEvent('optionSelected', { detail: option }));  }
  var sampleOptions = [    { value'1'text'HTML' },    { value'2'text'CSS' },    { value'3'text'JavaScript' },    { value'4'text'PHP' },    { value'5'text'Java' },    { value'6'text'Python' },    { value'7'text'Vue' },    { value'8'text'TypeScript' },    { value'9'text'Go' },    { value'10'text'Rust' },    { value'11'text'C++' },    { value'12'text'C#' },    { value'13'text'Ruby' },    { value'14'text'Swift' },    { value'15'text'Kotlin' },  ];
  selectContainer.addEventListener('optionSelected'(e) => {    console.log('选中的选项:', e.detail);  });
  init(sampleOptions);</script></body></html>


阅读原文:原文链接


该文章在 2025/12/17 17:08:32 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2026 ClickSun All Rights Reserved