如果 CustomEvent 在元素上触发,则取消“单击”DOM 事件

Posted

技术标签:

【中文标题】如果 CustomEvent 在元素上触发,则取消“单击”DOM 事件【英文标题】:Cancel 'Click' DOM event if CustomEvent fires on element 【发布时间】:2019-05-21 02:14:48 【问题描述】:

我正在使用 Cordova 构建一个应用程序,并且与许多应用程序一样,我正在尝试实现在按住某个元素后发生的长按事件。

我正在使用https://github.com/john-doherty/long-press-event,它会在按住一个元素 1.5 秒后触发一个名为“长按”的自定义事件。

我有一个元素我想同时放置一个“点击”监听器和一个“长按”监听器,类似于许多移动应用程序,例如照片库或电子邮件,它们在点击和长按之间的反应不同-新闻发布会。

长按事件确实会触发,但“Click”事件也每次都会触发,我不知道应该如何或何时尝试阻止它触发。我已经尝试了stopDefault()stopPropogation() 的几个位置,但均无济于事。

带有监听器的 html

<div class="grid-col grid-col--1">
    <div class="grid-item bd bdrs-4 bdw-1 bdc-grey-400">
        <img class="portfolio-img lightbox-img" src="https://glamsquad.sgp1.cdn.digitaloceanspaces.com/GlamSquad/artist/1/portfolio/2019-05-16-06-07-370bc89b7bfe9769740c1f68f7e103340a94aaaeaa5d6f139f841e3c022ad309de.png">
    </div>
    <div class="grid-item bd bdrs-4 bdw-1 bdc-grey-400">
        <img class="portfolio-img lightbox-img" src="https://glamsquad.sgp1.cdn.digitaloceanspaces.com/GlamSquad/artist/1/portfolio/2019-05-16-06-07-38d8d03cc6edef043d25e9099b883cd235c823a267ab03b9e740934f06c4f87e2f.png">
    </div>
</div>

当 JS 代码正在侦听对 lightbox-img 的点击或对投资组合图像的长按时

$(document).on('long-press', '.portfolio-img', (e) => 
    e.preventDefault();
    e.stopPropagation();
    console.log('Portfolio long press event.');
 );
$(document).on('click', '.lightbox-img', imageClick);

是否有任何实际的方法来触发长按事件但让它取消或停止点击事件的发生?

【问题讨论】:

我认为下面的@archer 回答有效。您可以通过在这两个功能中添加控制台日志来尝试了解点击调用时间的差异。长按事件会有时间延迟。 【参考方案1】:

执行此操作的一种方法是从您的单击元素中禁用pointer-events,从您的long-press 事件触发的那一刻开始,直到文档上的下一个mouseup 事件触发。

最好的方法可能是从您的库中获取,所以这里是这个库的一个分支,它现在在 CustomEvent 上公开了一个 preventDefaultClick() 方法:

(function (window, document) 

    'use strict';

    var timer = null;

    // check if we're using a touch screen
    var isTouch = (('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0));

    // switch to touch events if using a touch screen
    var mouseDown = isTouch ? 'touchstart' : 'mousedown';
    var mouseOut = isTouch ? 'touchcancel' : 'mouseout';
    var mouseUp = isTouch ? 'touchend' : 'mouseup';
    var mouseMove = isTouch ? 'touchmove' : 'mousemove';

    // wheel/scroll events
    var mouseWheel = 'mousewheel';
    var wheel = 'wheel';
    var scrollEvent = 'scroll';

    // patch CustomEvent to allow constructor creation (IE/Chrome)
    if (typeof window.CustomEvent !== 'function') 

        window.CustomEvent = function(event, params) 

            params = params ||  bubbles: false, cancelable: false, detail: undefined ;

            var evt = document.createEvent('CustomEvent');
            evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
            return evt;
        ;

        window.CustomEvent.prototype = window.Event.prototype;
    

    // listen to mousedown event on any child element of the body
    document.addEventListener(mouseDown, function(e) 
        var el = e.target;
        // get delay from html attribute if it exists, otherwise default to 1500
        var longPressDelayInMs = parseInt(el.getAttribute('data-long-press-delay') || '1500', 10);
        // start the timer
        timer = setTimeout(fireLongPressEvent.bind(el, e), longPressDelayInMs);
    );

    // clear the timeout if the user releases the mouse/touch
    document.addEventListener(mouseUp, function() 
        clearTimeout(timer);
    );

    // clear the timeout if the user leaves the element
    document.addEventListener(mouseOut, function() 
        clearTimeout(timer);
    );

    // clear if the mouse moves
    document.addEventListener(mouseMove, function() 
        clearTimeout(timer);
    );

    // clear if the Wheel event is fired in the element
    document.addEventListener(mouseWheel, function() 
        clearTimeout(timer);
    );

    // clear if the Scroll event is fired in the element
    document.addEventListener(wheel, function() 
        clearTimeout(timer);
    );

    // clear if the Scroll event is fired in the element
    document.addEventListener(scrollEvent, function() 
        clearTimeout(timer);
    );

    /**
     * Fires the 'long-press' event on element
     * @returns void
     */
    function fireLongPressEvent() 
        var evt = new CustomEvent('long-press',  bubbles: true, cancelable: true );
        // Expose a method to prevent the incoming click event
        var el = this;
        evt.preventDefaultClick = function() 
          // disable all pointer-events
          el.style["pointer-events"] = "none";
          // reenable at next mouseUp
          document.addEventListener(mouseUp, e => 
            el.style["pointer-events"] = "all";
          , once: true);
        ;
        // fire the long-press event
        this.dispatchEvent(evt);

        clearTimeout(timer);
    

(window, document));

btn.addEventListener('click', e => console.log('clicked'));
btn.addEventListener('long-press', e => 
  console.log('long-press');
  e.preventDefaultClick(); // prevents the incoming 'click' event
);
&lt;button data-long-press-delay="500" id="btn"&gt;click me&lt;/button&gt;

但是,如果像我一样,你的鼠标在每次滑动时都会触发一堆事件,那么你可能更喜欢这个演示,其中禁用了滚轮等超时触发器:

(function (window, document) 

    'use strict';

    var timer = null;

    // check if we're using a touch screen
    var isTouch = (('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0));

    // switch to touch events if using a touch screen
    var mouseDown = isTouch ? 'touchstart' : 'mousedown';
    var mouseOut = isTouch ? 'touchcancel' : 'mouseout';
    var mouseUp = isTouch ? 'touchend' : 'mouseup';
    var mouseMove = isTouch ? 'touchmove' : 'mousemove';

    // wheel/scroll events
    var mouseWheel = 'mousewheel';
    var wheel = 'wheel';
    var scrollEvent = 'scroll';

    // patch CustomEvent to allow constructor creation (IE/Chrome)
    if (typeof window.CustomEvent !== 'function') 

        window.CustomEvent = function(event, params) 

            params = params ||  bubbles: false, cancelable: false, detail: undefined ;

            var evt = document.createEvent('CustomEvent');
            evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
            return evt;
        ;

        window.CustomEvent.prototype = window.Event.prototype;
    

    // listen to mousedown event on any child element of the body
    document.addEventListener(mouseDown, function(e) 
        var el = e.target;
        // get delay from html attribute if it exists, otherwise default to 1500
        var longPressDelayInMs = parseInt(el.getAttribute('data-long-press-delay') || '1500', 10);
        // start the timer
        timer = setTimeout(fireLongPressEvent.bind(el, e), longPressDelayInMs);
    );

    // clear the timeout if the user releases the mouse/touch
    document.addEventListener(mouseUp, function() 
        clearTimeout(timer);
    );

    // clear the timeout if the user leaves the element
    document.addEventListener(mouseOut, function() 
        clearTimeout(timer);
    );

    // clear if the mouse moves
    document.addEventListener(mouseMove, function() 
//        clearTimeout(timer);
    );

    // clear if the Wheel event is fired in the element
    document.addEventListener(mouseWheel, function() 
//        clearTimeout(timer);
    );

    // clear if the Scroll event is fired in the element
    document.addEventListener(wheel, function() 
//        clearTimeout(timer);
    );

    // clear if the Scroll event is fired in the element
    document.addEventListener(scrollEvent, function() 
//        clearTimeout(timer);
    );

    /**
     * Fires the 'long-press' event on element
     * @returns void
     */
    function fireLongPressEvent() 
        var evt = new CustomEvent('long-press',  bubbles: true, cancelable: true );
        // Expose a method to prevent the incoming click event
        var el = this;
        evt.preventDefaultClick = function() 
          // disable all pointer-events
          el.style["pointer-events"] = "none";
          // reenable at next mouseUp
          document.addEventListener(mouseUp, e => 
            el.style["pointer-events"] = "all";
          , once: true);
        ;
        // fire the long-press event
        this.dispatchEvent(evt);

        clearTimeout(timer);
    

(window, document));

btn.addEventListener('click', e => console.log('clicked'));
btn.addEventListener('long-press', e => 
  console.log('long-press');
  e.preventDefaultClick(); // prevents the incoming 'click' event
);
&lt;button data-long-press-delay="500" id="btn"&gt;click me&lt;/button&gt;

【讨论】:

谢谢!它还可以在 e.originalEvent.preventDefaultClick(); 处访问该函数; @Musa 仅当您使用 jQuery 附加侦听器时。在我的 sn-p 中,evt 上没有 jQuery 和 originalEvent 你在纯javascript中是正确的,它可以直接工作【参考方案2】:

传播不是这里的问题 - 长点击事件和常规点击事件实际上是同一件事,所以它们都会触发。它们被触发是因为您先按下鼠标然后再抬起鼠标。两个事件之间的等待时间较长这一事实并不能阻止触发常规点击。

处理这个问题的最简单方法是设置一个标志,指示长按事件是否已被触发,像这样......

var longClickTriggered = false;

$(document).on('long-press', '.portfolio-img', (e) => 
    longClickTriggered = true;
    //e.preventDefault();   // these 2 lines are probably not needed now
    //e.stopPropagation();
    console.log('Portfolio long press event.');
);

$(document).on('click', '.lightbox-img', (e) => 
    if (!longClickTriggered) 
        imageClick();
    
    longClickTriggered = false;
);

我把这几行注释掉了...

e.preventDefault();
e.stopPropagation();

因为我相信它们只是在您试图阻止 click 事件处理程序触发时才出现的。

【讨论】:

【参考方案3】:

您可以使用布尔值以及 mousedownmouseup 侦听器

var timeoutId = 0, isHold = false;

$('#ele').on('mousedown', function() 
  timeoutId = setTimeout(onEleHold, 1000);
).on('mouseup', function() 
  if (!isHold) 
    onEleClick();
  
  isHold = false;
  clearTimeout(timeoutId);
  timeoutId = 0;
);

function onEleHold() 
  console.log('hold');
  isHold = true;


function onEleClick() 
  console.log('click');
#ele 
  background: black;
  width: 100px;
  height: 20px;
  color: white;
  cursor: pointer;
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="ele">Click Here</div>

【讨论】:

以上是关于如果 CustomEvent 在元素上触发,则取消“单击”DOM 事件的主要内容,如果未能解决你的问题,请参考以下文章

移动端touchstartouchmovetouchend 事件如果页面有滚动时不让触发 touchend 事件。

bootstrap源码分析之tab(选项卡)

CustomEvent自定义事件

js事件(Event)知识整理

js事件(Event)知识整理

如果两个依赖路径改变为可观察的,则Knockout在单个动作上计算多次触发