简体   繁体   中英

Is there a way to prevent fastclick from firing “active” state on scroll?

I'm using FastClick on a page with large links because I want to bypass the 300ms delay for taps in mobile browsers. I have a “highlight” style for the links' :active states and it is properly firing quickly, thanks to FastClick.

My problem is that – in Mobile Safari at least – it also fires while you're tapping and swiping to scroll the page. This makes it feel like you can't scroll the page without it thinking you're trying to tap the links.

Is there a way to prevent it from firing when someone scrolls?

Maybe you can add the needsclick class to the body?

<body class="needsclick">

...

</body>

Just an idea :)

Nice question! +1

This problem has nothing to do with FastClick, but FastClick does make the solution to your problem more complex. So I will stick to pure JavaScript and raw Touch Events )

On a mobile touch device the implementation of the webview is significantly different from the desktop web browser for reasons specific to the platform. One feature that is important in this case is momentum scrolling in the webview. Momentum Scrolling is a hardware accelerated feature of the device. So when a scrollable area is touched on the screen, the hardware identifies the touch and attaches a 200 millisecond countdown timer to the scrollable area, that when triggered, puts the area into a hardware accelerated scroll. When the hardware is in the state it does not broadcast touch events because they are specific to the hardware accelerated scroll. The timer can be cancelled, and momentum scrolling prevented by using preventDefault on the touch event within the provided 200 milliseconds.

Here is how I approach this problem. I assume you are using native scroll ie overflow:scroll . The method is to attach a touchstart event to the element you want touchable. Once the touchstart event has fired, the event handler attaches touchend , touchmove and touchcancel events to the target element. A setTimout timer is initiated that removes the newly added events after 140ms.

Here are some clips for my production code: I have two event methods for adding and removing events from collections:

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
})();

Then I have a tapify method for tap events on the scroller:

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;
})();

I use global.isIos to identify the target device. Android stops sending touch events to webview ater 200ms.

Then to attach the event to a element or a collection of elements, use :

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

Hope this helps

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM