【问题标题】:Is there a way to prevent fastclick from firing “active” state on scroll?有没有办法防止 fastclick 在滚动时触发“活动”状态?
【发布时间】:2016-04-26 21:53:46
【问题描述】:

我在带有大链接的页面上使用FastClick,因为我想绕过移动浏览器中点击的 300 毫秒延迟。我为链接的:active 状态设置了“突出显示”样式,并且由于 FastClick,它可以正确快速触发。

我的问题是——至少在 Mobile Safari 中——当你点击和滑动滚动页面时它也会触发。这会让您感觉无法滚动页面,但它会认为您正在尝试点击链接。

有没有办法防止它在有人滚动时触发?

【问题讨论】:

    标签: javascript css mobile-safari fastclick.js fastclick


    【解决方案1】:

    也许您可以将needsclick 类添加到正文中?

    <body class="needsclick">
    
    ...
    
    </body>
    

    只是一个想法:)

    【讨论】:

    • 谢谢……这实际上会阻止 FastClick 用于控件。
    【解决方案2】:

    好问题! +1

    此问题与 FastClick 无关,但 FastClick 确实使您的问题的解决方案更加复杂。所以我会坚持纯 JavaScript 和原始触摸事件)

    由于特定于平台的原因,在移动触摸设备上,webview 的实现与桌面 Web 浏览器有很大不同。 在这种情况下,一个重要的特性是 webview 中的动量滚动。 Momentum Scrolling 是设备的硬件加速功能。因此,当触摸屏幕上的可滚动区域时,硬件会识别该触摸并将 200 毫秒的倒计时计时器附加到可滚动区域,当触发时,该区域会进入硬件加速滚动状态。当硬件处于该状态时,它不会广播触摸事件,因为它们特定于硬件加速滚动。在提供的 200 毫秒内,可以通过在触摸事件上使用 preventDefault 来取消计时器并防止动量滚动。

    这是我解决这个问题的方法。我假设您使用的是本机滚动,即overflow:scroll。 方法是将touchstart 事件附加到您想要触摸的元素。 一旦触发了touchstart 事件,事件处理程序就会将touchendtouchmovetouchcancel 事件附加到目标元素。启动一个 setTimout 计时器,在 140 毫秒后删除新添加的事件。

    以下是我的生产代码的一些剪辑: 我有两种用于从集合中添加和删除事件的事件方法:

    var eventify = (function () {
        function eventify(type, el, callback, phase) {
            phase = phase || false;
            if ((el.constructor.toString().contains('NodeList')) || (el.constructor.toString().contains('HTMLCollection'))) {
                [].forEach.call(el, function (element) {
                    if (!element.hasEvent(type)) {
                        element.addEvent(type);
                        HTMLElement.prototype.addEventListener.apply(element, [type, callback, phase]);
                    }
                });
            } else {
                if (!el.hasEvent(type)) {
                    el.addEvent(type);
                    HTMLElement.prototype.addEventListener.apply(el, [type, callback, phase]);
                }
            }
            return callback;
        }
    
        return eventify
    })();
    
    var uneventify = (function () {
        function uneventify(type, el, callback) {
            if ((el.constructor.toString().contains('NodeList')) || (el.constructor.toString().contains('HTMLCollection'))) {
                [].forEach.call(el, function (element) {
                    if (element.hasEvent(type)) {
                        element.removeEvent(type);
                        HTMLElement.prototype.removeEventListener.apply(element, [type, callback]);
                    }
                });
            } else {
                if (el.hasEvent(type)) {
                    el.removeEvent(type);
                    HTMLElement.prototype.removeEventListener.apply(el, [type, callback]);
                }
            }
        }
    
        return uneventify
    })();
    

    然后我有一个用于滚动条上的点击事件的 tapify 方法:

    var tapify = (function () {
        function tapify(el, callback) {
            eventify('touchstart', el, function (e) {
                var that = this;
                var start = e.pageY;
                var target = e.target;
                function dynamicEvents() {
                    var endfn = eventify('touchend', target, function (evt) {
                        e.preventDefault();
                        e.stopImmediatePropagation();
                        evt.preventDefault();
                        evt.stopImmediatePropagation();
                        uneventify('touchmove', target, movefn);
                        uneventify('touchend', target, endfn);
                        uneventify('touchcancel', target, cancelfn);
                        callback && callback(target);
                    });
                    var cancelfn = eventify('touchcancel', target, function (evt) {
                        e.preventDefault();
                        e.stopImmediatePropagation();
                        evt.preventDefault();
                        evt.stopImmediatePropagation();
                        uneventify('touchmove', target, movefn);
                        uneventify('touchend', target, endfn);
                        uneventify('touchcancel', target, cancelfn);
                        callback && callback(target);
                    });
                    var movefn = eventify('touchmove', target, function (evt) {
                        var distance = start - evt.pageY;
                        if (distance > 20) {
                            uneventify('touchend', target, endfn);
                            uneventify('touchcancel', target, cancelfn);
                            uneventify('touchmove', el, movefn);
                        }
                    });
                    setTimeout(function () {
                        uneventify('touchmove', target, movefn);
                        uneventify('touchend', target, endfn);
                        uneventify('touchcancel', target, cancelfn);
                    }, 140);
                }
                if (global.isIos) setTimeout(function () {
                    dynamicEvents();
                }, 60);
                else dynamicEvents();
            }, false);
        }
        return tapify;
    })();
    

    我使用 global.isIos 来识别目标设备。 Android 在 200 毫秒后停止向 webview 发送触摸事件。

    然后要将事件附加到一个元素或元素集合,请使用:

    tapify(document.querySelectorAll('button'), function (e) {
          //your event handler here!! 
    });
    

    希望对你有帮助

    【讨论】:

    • 可以使用相同的方法在本机滚动表面上滑动和拖动事件。
    • 感谢您的回复!现在我希望尝试一些与 FastClick 兼容的东西......作为一个 JS 新手,我承认你的回答让我头疼,但我没有看到它如何与 FastClick 连接。但如果我有勇气,我会尝试将你的一些代码添加到我的项目中。
    • 我建议您仅在需要它的元素上而不是 document.body 上附加 FastClick
    猜你喜欢
    • 2021-09-08
    • 2015-11-01
    • 2016-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-10
    相关资源
    最近更新 更多