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

如何阻止长按函数在首次触发后对后续li元素生效?

问题解决:长按列表项后停止长按逻辑触发

需求说明

  • 长按<li>元素时,切换该元素的active类,并将其父<ul>的data-edit属性设为true
  • 当data-edit="true"时,点击任意<li>即可直接切换active状态,无需长按

现有代码

HTML

<ul class="container" data-edit="false">
    <li class="item" tabindex="0"></li>
    <li class="item" tabindex="0"></li>
    <li class="item" tabindex="0"></li>
</ul>

JavaScript

function longPress() {
  const lists = document.querySelectorAll("[data-edit=false]");
  const delay = 800;

  lists.forEach((list) => {
    const listItems = list.querySelectorAll("li");

    listItems.forEach((listItem) => {
      listItem.addEventListener("mouseup", () => {
        clearTimeout(pressTimer);
      });

      listItem.addEventListener("mousedown", () => {
        pressTimer = setTimeout(() => {
          listItem.classList.toggle("active");
          list.setAttribute("data-edit", "true");
        }, delay);
      });
    });
  });
}

存在问题

长按某个<li>后,父<ul>的data-edit已设为true,但之前绑定的长按事件并未移除,导致后续长按其他<li>时仍会触发切换逻辑,不符合编辑模式下无需长按的需求。

解决方案

方法1:事件委托+动态解绑(推荐)

通过事件委托将事件绑定到<ul>上,触发长按后直接移除该<ul>的长按事件,同时添加编辑模式的点击事件:

function initListEdit() {
  const delay = 800;
  let pressTimer;

  document.querySelectorAll('[data-edit=false]').forEach(list => {
    // 定义长按相关事件处理函数
    const handleMouseDown = (e) => {
      const targetLi = e.target.closest('li');
      // 不是li元素或已进入编辑模式,直接返回
      if (!targetLi || list.dataset.edit === 'true') return;

      pressTimer = setTimeout(() => {
        targetLi.classList.toggle('active');
        list.dataset.edit = 'true';
        
        // 移除长按相关事件,避免后续触发
        list.removeEventListener('mousedown', handleMouseDown);
        list.removeEventListener('mouseup', handleMouseUp);
        list.removeEventListener('mouseleave', handleMouseUp);
        
        // 添加编辑模式的点击切换事件
        list.addEventListener('click', (e) => {
          const li = e.target.closest('li');
          if (li) li.classList.toggle('active');
        });
      }, delay);
    };

    const handleMouseUp = () => {
      clearTimeout(pressTimer);
    };

    // 绑定事件
    list.addEventListener('mousedown', handleMouseDown);
    list.addEventListener('mouseup', handleMouseUp);
    // 补充鼠标离开事件,防止移开后仍触发长按
    list.addEventListener('mouseleave', handleMouseUp);
  });
}

// 初始化调用
initListEdit();

方法2:解绑单个列表项的事件

如果保留原有的列表项绑定方式,可在触发长按后移除所有<li>的长按事件,再添加点击事件:

function longPress() {
  const lists = document.querySelectorAll("[data-edit=false]");
  const delay = 800;

  lists.forEach((list) => {
    let pressTimer;
    const listItems = list.querySelectorAll("li");

    // 定义可复用的事件处理函数
    const handleMouseUp = () => {
      clearTimeout(pressTimer);
    };

    const handleMouseDown = (e) => {
      const listItem = e.target;
      pressTimer = setTimeout(() => {
        listItem.classList.toggle("active");
        list.setAttribute("data-edit", "true");
        
        // 移除所有列表项的长按相关事件
        listItems.forEach(item => {
          item.removeEventListener('mouseup', handleMouseUp);
          item.removeEventListener('mousedown', handleMouseDown);
          item.removeEventListener('mouseleave', handleMouseUp);
        });
        
        // 添加编辑模式的点击切换事件
        listItems.forEach(item => {
          item.addEventListener('click', () => {
            item.classList.toggle('active');
          });
        });
      }, delay);
    };

    // 绑定初始事件
    listItems.forEach((listItem) => {
      listItem.addEventListener("mouseup", handleMouseUp);
      listItem.addEventListener("mousedown", handleMouseDown);
      listItem.addEventListener("mouseleave", handleMouseUp);
    });
  });
}

核心思路

  1. 触发首次长按后,立即移除原来的长按事件绑定,阻止后续长按逻辑执行
  2. 同步添加编辑模式的点击事件,满足点击切换状态的需求
  3. 补充mouseleave事件处理,避免用户长按过程中移开鼠标仍触发长按逻辑

内容的提问来源于stack exchange,提问作者Nicola Friuli

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 12:20:40