博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
周末读fastclick.js源码有感
阅读量:6392 次
发布时间:2019-06-23

本文共 26464 字,大约阅读时间需要 88 分钟。

看之前需要知道:

// 创建一个事件对象,就好像你绑定事件回调函数里获得的那个事件对象一样var myEvent = document.createEvent('Event')// 初始化这个事件对象,为它提高需要的“特性”myEvent.initEvent('myEventName', true, true)// 按照常见的方式绑定对该事件的监听,这里的 element 可以是任何一个元素,你懂的element.addEventListener('myEventName', function(evt) {  // 这里,evt 就是你创建的事件对象了,不过它会更丰富一些——这些是浏览器为你做的})// 然后这样触发它element.dispatchEvent(myEvent)你必须要去阅读 createEvent initEvent dispatchEvent 这些方法的文档来完整认识它们(文档可以去 MDN 找),上面介绍的只是基本用法。$.trigger() 所封装的也就是它们(及其相关的兼容性处理等等)。不过这些方法已经有点“过时”了,特别像 initEvent 这样的方法更是已经被(新的 DOM 标准)废弃了。以下是更“现代”的做法,不过老样子:兼容性会是问题:var myEvent = new Event('myEventName')element.dispatchEvent(myEvent)绑定回调是一样的我就不重复了。另外“现代”的做法里触发内建事件要用对应的接口,比如说:var event = new MouseEvent('click', {  'view': window,  'bubbles': true,  'cancelable': true});其实除了具体方法的文档外,更重要的是先理解整个事件对象模型体系,这是一个非常非常庞大的体复制代码
;(function () {    'use strict';    //构造函数    function FastClick(layer, options) {        var oldOnClick;        options = options || {};        /**         * Whether a click is currently being tracked.         *         * @type boolean         */        this.trackingClick = false;        /**         * Timestamp for when click tracking started.         *         * @type number         */        this.trackingClickStart = 0;        /**         * The element being tracked for a click.         *         * @type EventTarget         */        this.targetElement = null;        /**         * X-coordinate of touch start event.         *         * @type number         */        this.touchStartX = 0;        /**         * Y-coordinate of touch start event.         *         * @type number         */        this.touchStartY = 0;        //主要hack iOS4下的一个怪异问题        this.lastTouchIdentifier = 0;        /**         * 用于区分是click还是Touchmove,若出点移动超过该值则视为touchmove         */        this.touchBoundary = options.touchBoundary || 10;        /**         * 绑定了FastClick的元素,常规是body         */        this.layer = layer;        /**         * The minimum time between tap(touchstart and touchend) events         *         * @type number         */        this.tapDelay = options.tapDelay || 200;        /**         * The maximum time for a tap         *         * @type number         */        this.tapTimeout = options.tapTimeout || 700;        //如果是属于不需要处理的元素类型,则直接返回        if (FastClick.notNeeded(layer)) {            return;        }        //语法糖,兼容一些用不了 Function.prototype.bind 的旧安卓        //所以后面不走 layer.addEventListener('click', this.onClick.bind(this), true);        function bind(method, context) {            return function() { return method.apply(context, arguments); };        }        var methods = ['onMouse', 'onClick', 'onTouchStart', 'onTouchMove', 'onTouchEnd', 'onTouchCancel'];        var context = this;        for (var i = 0, l = methods.length; i < l; i++) {            context[methods[i]] = bind(context[methods[i]], context);        }        //安卓则做额外处理        if (deviceIsAndroid) {            layer.addEventListener('mouseover', this.onMouse, true);            layer.addEventListener('mousedown', this.onMouse, true);            layer.addEventListener('mouseup', this.onMouse, true);        }        layer.addEventListener('click', this.onClick, true);        layer.addEventListener('touchstart', this.onTouchStart, false);        layer.addEventListener('touchmove', this.onTouchMove, false);        layer.addEventListener('touchend', this.onTouchEnd, false);        layer.addEventListener('touchcancel', this.onTouchCancel, false);        // 兼容不支持 stopImmediatePropagation 的浏览器(比如 Android 2)        if (!Event.prototype.stopImmediatePropagation) {            layer.removeEventListener = function(type, callback, capture) {                var rmv = Node.prototype.removeEventListener;                if (type === 'click') {                    rmv.call(layer, type, callback.hijacked || callback, capture);                } else {                    rmv.call(layer, type, callback, capture);                }            };            layer.addEventListener = function(type, callback, capture) {                var adv = Node.prototype.addEventListener;                if (type === 'click') {                    //留意这里 callback.hijacked 中会判断 event.propagationStopped 是否为真来确保(安卓的onMouse事件)只执行一次                    //在 onMouse 事件里会给 event.propagationStopped 赋值 true                    adv.call(layer, type, callback.hijacked || (callback.hijacked = function(event) {                            if (!event.propagationStopped) {                                callback(event);                            }                        }), capture);                } else {                    adv.call(layer, type, callback, capture);                }            };        }        // 如果layer直接在DOM上写了 onclick 方法,那我们需要把它替换为 addEventListener 绑定形式        if (typeof layer.onclick === 'function') {            oldOnClick = layer.onclick;            layer.addEventListener('click', function(event) {                oldOnClick(event);            }, false);            layer.onclick = null;        }    }    /**     * Windows Phone 8.1 fakes user agent string to look like Android and iPhone.     *     * @type boolean     */    var deviceIsWindowsPhone = navigator.userAgent.indexOf("Windows Phone") >= 0;    /**     * Android requires exceptions.     *     * @type boolean     */    var deviceIsAndroid = navigator.userAgent.indexOf('Android') > 0 && !deviceIsWindowsPhone;    /**     * iOS requires exceptions.     *     * @type boolean     */    var deviceIsIOS = /iP(ad|hone|od)/.test(navigator.userAgent) && !deviceIsWindowsPhone;    /**     * iOS 4 requires an exception for select elements.     *     * @type boolean     */    var deviceIsIOS4 = deviceIsIOS && (/OS 4_\d(_\d)?/).test(navigator.userAgent);    /**     * iOS 6.0-7.* requires the target element to be manually derived     *     * @type boolean     */    var deviceIsIOSWithBadTarget = deviceIsIOS && (/OS [6-7]_\d/).test(navigator.userAgent);    /**     * BlackBerry requires exceptions.     *     * @type boolean     */    var deviceIsBlackBerry10 = navigator.userAgent.indexOf('BB10') > 0;    //判断元素是否要保留穿透功能    FastClick.prototype.needsClick = function(target) {        switch (target.nodeName.toLowerCase()) {            // disabled的input            case 'button':            case 'select':            case 'textarea':                if (target.disabled) {                    return true;                }                break;            case 'input':                // file组件必须通过原生click事件点击才有效                if ((deviceIsIOS && target.type === 'file') || target.disabled) {                    return true;                }                break;            case 'label':            case 'iframe':            case 'video':                return true;        }        //元素带了名为“bneedsclick”的class也返回true        return (/\bneedsclick\b/).test(target.className);    };    //判断给定元素是否需要通过合成click事件来模拟聚焦    FastClick.prototype.needsFocus = function(target) {        switch (target.nodeName.toLowerCase()) {            case 'textarea':                return true;            case 'select':                return !deviceIsAndroid; //iOS下的select得走穿透点击才行            case 'input':                switch (target.type) {                    case 'button':                    case 'checkbox':                    case 'file':                    case 'image':                    case 'radio':                    case 'submit':                        return false;                }                return !target.disabled && !target.readOnly;            default:                //带有名为“bneedsfocus”的class则返回true                return (/\bneedsfocus\b/).test(target.className);        }    };    //合成一个click事件并在指定元素上触发    FastClick.prototype.sendClick = function(targetElement, event) {        var clickEvent, touch;        // 在一些安卓机器中,得让页面所存在的 activeElement(聚焦的元素,比如input)失焦,否则合成的click事件将无效        if (document.activeElement && document.activeElement !== targetElement) {            document.activeElement.blur();        }        touch = event.changedTouches[0];        // 合成(Synthesise) 一个 click 事件        // 通过一个额外属性确保它能被追踪(tracked)        clickEvent = document.createEvent('MouseEvents');        clickEvent.initMouseEvent(this.determineEventType(targetElement), true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);        clickEvent.forwardedTouchEvent = true; // fastclick的内部变量,用来识别click事件是原生还是合成的        targetElement.dispatchEvent(clickEvent); //立即触发其click事件    };    FastClick.prototype.determineEventType = function(targetElement) {        //安卓设备下 Select 无法通过合成的 click 事件被展开,得改为 mousedown        if (deviceIsAndroid && targetElement.tagName.toLowerCase() === 'select') {            return 'mousedown';        }        return 'click';    };    //设置元素聚焦事件    FastClick.prototype.focus = function(targetElement) {        var length;        // 组件建议通过setSelectionRange(selectionStart, selectionEnd)来设定光标范围(注意这样还没有聚焦        // 要等到后面触发 sendClick 事件才会聚焦)        // 另外 iOS7 下有些input元素(比如 date datetime month) 的 selectionStart 和 selectionEnd 特性是没有整型值的,        // 导致会抛出一个关于 setSelectionRange 的模糊错误,它们需要改用 focus 事件触发        if (deviceIsIOS && targetElement.setSelectionRange && targetElement.type.indexOf('date') !== 0 && targetElement.type !== 'time' && targetElement.type !== 'month') {            length = targetElement.value.length;            targetElement.setSelectionRange(length, length);        } else {            //直接触发其focus事件            targetElement.focus();        }    };    /**     * 检查target是否一个滚动容器里的子元素,如果是则给它加个标记     */    FastClick.prototype.updateScrollParent = function(targetElement) {        var scrollParent, parentElement;        scrollParent = targetElement.fastClickScrollParent;        // Attempt to discover whether the target element is contained within a scrollable layer. Re-check if the        // target element was moved to another parent.        if (!scrollParent || !scrollParent.contains(targetElement)) {            parentElement = targetElement;            do {                if (parentElement.scrollHeight > parentElement.offsetHeight) {                    scrollParent = parentElement;                    targetElement.fastClickScrollParent = parentElement;                    break;                }                parentElement = parentElement.parentElement;            } while (parentElement);        }        // 给滚动容器加个标志fastClickLastScrollTop,值为其当前垂直滚动偏移        if (scrollParent) {            scrollParent.fastClickLastScrollTop = scrollParent.scrollTop;        }    };    /**     * @param {EventTarget} targetElement     * @returns {Element|EventTarget}     */    FastClick.prototype.getTargetElementFromEventTarget = function(eventTarget) {        // 一些较老的浏览器,target 可能会是一个文本节点,得返回其DOM节点        if (eventTarget.nodeType === Node.TEXT_NODE) {            return eventTarget.parentNode;        }        return eventTarget;    };    FastClick.prototype.onTouchStart = function(event) {        var targetElement, touch, selection;        // 多指触控的手势则忽略        if (event.targetTouches.length > 1) {            return true;        }        targetElement = this.getTargetElementFromEventTarget(event.target); //一些较老的浏览器,target 可能会是一个文本节点,得返回其DOM节点        touch = event.targetTouches[0];        if (deviceIsIOS) { //IOS处理            // 若用户已经选中了一些内容(比如选中了一段文本打算复制),则忽略            selection = window.getSelection();            if (selection.rangeCount && !selection.isCollapsed) {                return true;            }            if (!deviceIsIOS4) { //是否IOS4                //怪异特性处理——若click事件回调打开了一个alert/confirm,用户下一次tap页面的其它地方时,新的touchstart和touchend                //事件会拥有同一个touch.identifier(新的 touch event 会跟上一次触发alert点击的 touch event 一样),                //为避免将新的event当作之前的event导致问题,这里需要禁用默认事件                //另外chrome的开发工具启用'Emulate touch events'后,iOS UA下的 identifier 会变成0,所以要做容错避免调试过程也被禁用事件了                if (touch.identifier && touch.identifier === this.lastTouchIdentifier) {                    event.preventDefault();                    return false;                }                this.lastTouchIdentifier = touch.identifier;                // 如果target是一个滚动容器里的一个子元素(使用了 -webkit-overflow-scrolling: touch) ,而且满足:                // 1) 用户非常快速地滚动外层滚动容器                // 2) 用户通过tap停止住了这个快速滚动                // 这时候最后的'touchend'的event.target会变成用户最终手指下的那个元素                // 所以当快速滚动开始的时候,需要做检查target是否滚动容器的子元素,如果是,做个标记                // 在touchend时检查这个标记的值(滚动容器的scrolltop)是否改变了,如果是则说明页面在滚动中,需要取消fastclick处理                this.updateScrollParent(targetElement);            }        }        this.trackingClick = true; //做个标志表示开始追踪click事件了        this.trackingClickStart = event.timeStamp; //标记下touch事件开始的时间戳        this.targetElement = targetElement;        //标记touch起始点的页面偏移值        this.touchStartX = touch.pageX;        this.touchStartY = touch.pageY;        // this.lastClickTime 是在 touchend 里标记的事件时间戳        // this.tapDelay 为常量 200 (ms)        // 此举用来避免 phantom 的双击(200ms内快速点了两次)触发 click        // 反正200ms内的第二次点击会禁止触发点击的默认事件        if ((event.timeStamp - this.lastClickTime) < this.tapDelay) {            event.preventDefault();        }        return true;    };    //判断是否移动了    //this.touchBoundary是常量,值为10    //如果touch已经移动了10个偏移量单位,则应当作为移动事件处理而非click事件    FastClick.prototype.touchHasMoved = function(event) {        var touch = event.changedTouches[0], boundary = this.touchBoundary;        if (Math.abs(touch.pageX - this.touchStartX) > boundary || Math.abs(touch.pageY - this.touchStartY) > boundary) {            return true;        }        return false;    };    FastClick.prototype.onTouchMove = function(event) {        //不是需要被追踪click的事件则忽略        if (!this.trackingClick) {            return true;        }        // 如果target突然改变了,或者用户其实是在移动手势而非想要click        // 则应该清掉this.trackingClick和this.targetElement,告诉后面的事件你们也不用处理了        if (this.targetElement !== this.getTargetElementFromEventTarget(event.target) || this.touchHasMoved(event)) {            this.trackingClick = false;            this.targetElement = null;        }        return true;    };    //找到label标签所映射的组件,方便让用户点label的时候直接激活该组件    FastClick.prototype.findControl = function(labelElement) {        // 有缓存则直接读缓存着的        if (labelElement.control !== undefined) {            return labelElement.control;        }        // 获取指向的组件        if (labelElement.htmlFor) {            return document.getElementById(labelElement.htmlFor);        }        // 没有for属性则激活页面第一个组件(labellable 元素)        return labelElement.querySelector('button, input:not([type=hidden]), keygen, meter, output, progress, select, textarea');    };    FastClick.prototype.onTouchEnd = function(event) {        var forElement, trackingClickStart, targetTagName, scrollParent, touch, targetElement = this.targetElement;        if (!this.trackingClick) {            return true;        }        // 避免 phantom 的双击(200ms内快速点了两次)触发 click        // 我们在 ontouchstart 里已经做过一次判断了(仅仅禁用默认事件),这里再做一次判断        if ((event.timeStamp - this.lastClickTime) < this.tapDelay) {            this.cancelNextClick = true; //该属性会在 onMouse 事件中被判断,为true则彻底禁用事件和冒泡            return true;        }        //this.tapTimeout是常量,值为700        //识别是否为长按事件,如果是(大于700ms)则忽略        if ((event.timeStamp - this.trackingClickStart) > this.tapTimeout) {            return true;        }        // 得重置为false,避免input事件被意外取消        // 例子见 https://github.com/ftlabs/fastclick/issues/156        this.cancelNextClick = false;        this.lastClickTime = event.timeStamp; //标记touchend时间,方便下一次的touchstart做双击校验        trackingClickStart = this.trackingClickStart;        //重置 this.trackingClick 和 this.trackingClickStart        this.trackingClick = false;        this.trackingClickStart = 0;        // iOS 6.0-7.*版本下有个问题 —— 如果layer处于transition或scroll过程,event所提供的target是不正确的        // 所以咱们得重找 targetElement(这里通过 document.elementFromPoint 接口来寻找)        if (deviceIsIOSWithBadTarget) { //iOS 6.0-7.*版本            touch = event.changedTouches[0]; //手指离开前的触点            // 有些情况下 elementFromPoint 里的参数是预期外/不可用的, 所以还得避免 targetElement 为 null            targetElement = document.elementFromPoint(touch.pageX - window.pageXOffset, touch.pageY - window.pageYOffset) || targetElement;            // target可能不正确需要重找,但fastClickScrollParent是不会变的            targetElement.fastClickScrollParent = this.targetElement.fastClickScrollParent;        }        targetTagName = targetElement.tagName.toLowerCase();        if (targetTagName === 'label') { //是label则激活其指向的组件            forElement = this.findControl(targetElement);            if (forElement) {                this.focus(targetElement);                //安卓直接返回(无需合成click事件触发,因为点击和激活元素不同,不存在点透)                if (deviceIsAndroid) {                    return false;                }                targetElement = forElement;            }        } else if (this.needsFocus(targetElement)) { //非label则识别是否需要focus的元素            //手势停留在组件元素时长超过100ms,则置空this.targetElement并返回            //(而不是通过调用this.focus来触发其聚焦事件,走的原生的click/focus事件触发流程)            //这也是为何文章开头提到的问题中,稍微久按一点(超过100ms)textarea是可以把光标定位在正确的地方的原因            //另外iOS下有个意料之外的bug——如果被点击的元素所在文档是在iframe中的,手动调用其focus的话,            //会发现你往其中输入的text是看不到的(即使value做了更新),so这里也直接返回            if ((event.timeStamp - trackingClickStart) > 100 || (deviceIsIOS && window.top !== window && targetTagName === 'input')) {                this.targetElement = null;                return false;            }            this.focus(targetElement);            this.sendClick(targetElement, event);  //立即触发其click事件,而无须等待300ms            //iOS4下的 select 元素不能禁用默认事件(要确保它能被穿透),否则不会打开select目录            //有时候 iOS6/7 下(VoiceOver开启的情况下)也会如此            if (!deviceIsIOS || targetTagName !== 'select') {                this.targetElement = null;                event.preventDefault();            }            return false;        }        if (deviceIsIOS && !deviceIsIOS4) {            // 滚动容器的垂直滚动偏移改变了,说明是容器在做滚动而非点击,则忽略            scrollParent = targetElement.fastClickScrollParent;            if (scrollParent && scrollParent.fastClickLastScrollTop !== scrollParent.scrollTop) {                return true;            }        }        // 查看元素是否无需处理的白名单内(比如加了名为“needsclick”的class)        // 不是白名单的则照旧预防穿透处理,立即触发合成的click事件        if (!this.needsClick(targetElement)) {            event.preventDefault();            this.sendClick(targetElement, event);        }        return false;    };    FastClick.prototype.onTouchCancel = function() {        this.trackingClick = false;        this.targetElement = null;    };    //用于决定是否允许穿透事件(触发layer的click默认事件)    FastClick.prototype.onMouse = function(event) {        // touch事件一直没触发        if (!this.targetElement) {            return true;        }        if (event.forwardedTouchEvent) { //触发的click事件是合成的            return true;        }        // 编程派生的事件所对应元素事件可以被允许        // 确保其没执行过 preventDefault 方法(event.cancelable 不为 true)即可        if (!event.cancelable) {            return true;        }        // 需要做预防穿透处理的元素,或者做了快速(200ms)双击的情况        if (!this.needsClick(this.targetElement) || this.cancelNextClick) {            //停止当前默认事件和冒泡            if (event.stopImmediatePropagation) {                event.stopImmediatePropagation();            } else {                // 不支持 stopImmediatePropagation 的设备(比如Android 2)做标记,                // 确保该事件回调不会执行(见126行)                event.propagationStopped = true;            }            // 取消事件和冒泡            event.stopPropagation();            event.preventDefault();            return false;        }        //允许穿透        return true;    };    //click事件常规都是touch事件衍生来的,也排在touch后面触发。    //对于那些我们在touch事件过程没有禁用掉默认事件的event来说,我们还需要在click的捕获阶段进一步    //做判断决定是否要禁掉点击事件(防穿透)    FastClick.prototype.onClick = function(event) {        var permitted;        // 如果还有 trackingClick 存在,可能是某些UI事件阻塞了touchEnd 的执行        if (this.trackingClick) {            this.targetElement = null;            this.trackingClick = false;            return true;        }        // 依旧是对 iOS 怪异行为的处理 —— 如果用户点击了iOS模拟器里某个表单中的一个submit元素        // 或者点击了弹出来的键盘里的“Go”按钮,会触发一个“伪”click事件(target是一个submit-type的input元素)        if (event.target.type === 'submit' && event.detail === 0) {            return true;        }        permitted = this.onMouse(event);        if (!permitted) { //如果点击是被允许的,将this.targetElement置空可以确保onMouse事件里不会阻止默认事件            this.targetElement = null;        }        //没有多大意义        return permitted;    };    //销毁Fastclick所注册的监听事件。是给外部实例去调用的    FastClick.prototype.destroy = function() {        var layer = this.layer;        if (deviceIsAndroid) {            layer.removeEventListener('mouseover', this.onMouse, true);            layer.removeEventListener('mousedown', this.onMouse, true);            layer.removeEventListener('mouseup', this.onMouse, true);        }        layer.removeEventListener('click', this.onClick, true);        layer.removeEventListener('touchstart', this.onTouchStart, false);        layer.removeEventListener('touchmove', this.onTouchMove, false);        layer.removeEventListener('touchend', this.onTouchEnd, false);        layer.removeEventListener('touchcancel', this.onTouchCancel, false);    };    //是否没必要使用到 Fastclick 的检测    FastClick.notNeeded = function(layer) {        var metaViewport;        var chromeVersion;        var blackberryVersion;        var firefoxVersion;        // 不支持触摸的设备        if (typeof window.ontouchstart === 'undefined') {            return true;        }        // 获取Chrome版本号,若非Chrome则返回0        chromeVersion = +(/Chrome\/([0-9]+)/.exec(navigator.userAgent) || [,0])[1];        if (chromeVersion) {            if (deviceIsAndroid) { //安卓                metaViewport = document.querySelector('meta[name=viewport]');                if (metaViewport) {                    // 安卓下,带有 user-scalable="no" 的 meta 标签的 chrome 是会自动禁用 300ms 延迟的,所以无需 Fastclick                    if (metaViewport.content.indexOf('user-scalable=no') !== -1) {                        return true;                    }                    // 安卓Chrome 32 及以上版本,若带有 width=device-width 的 meta 标签也是无需 FastClick 的                    if (chromeVersion > 31 && document.documentElement.scrollWidth <= window.outerWidth) {                        return true;                    }                }                // 其它的就肯定是桌面级的 Chrome 了,更不需要 FastClick 啦            } else {                return true;            }        }        if (deviceIsBlackBerry10) { //黑莓,和上面安卓同理,就不写注释了            blackberryVersion = navigator.userAgent.match(/Version\/([0-9]*)\.([0-9]*)/);            if (blackberryVersion[1] >= 10 && blackberryVersion[2] >= 3) {                metaViewport = document.querySelector('meta[name=viewport]');                if (metaViewport) {                    if (metaViewport.content.indexOf('user-scalable=no') !== -1) {                        return true;                    }                    if (document.documentElement.scrollWidth <= window.outerWidth) {                        return true;                    }                }            }        }        // 带有 -ms-touch-action: none / manipulation 特性的 IE10 会禁用双击放大,也没有 300ms 时延        if (layer.style.msTouchAction === 'none' || layer.style.touchAction === 'manipulation') {            return true;        }        // Firefox检测,同上        firefoxVersion = +(/Firefox\/([0-9]+)/.exec(navigator.userAgent) || [,0])[1];        if (firefoxVersion >= 27) {            metaViewport = document.querySelector('meta[name=viewport]');            if (metaViewport && (metaViewport.content.indexOf('user-scalable=no') !== -1 || document.documentElement.scrollWidth <= window.outerWidth)) {                return true;            }        }        // IE11 推荐使用没有“-ms-”前缀的 touch-action 样式特性名        if (layer.style.touchAction === 'none' || layer.style.touchAction === 'manipulation') {            return true;        }        return false;    };    FastClick.attach = function(layer, options) {        return new FastClick(layer, options);    };    if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {        // AMD. Register as an anonymous module.        define(function() {            return FastClick;        });    } else if (typeof module !== 'undefined' && module.exports) {        module.exports = FastClick.attach;        module.exports.FastClick = FastClick;    } else {        window.FastClick = FastClick;    }}());复制代码

JQuery 实现:

/*! * jQuery fastclick Plugin * http://www.zurb.com/playground/ * Ported from Ryan Fioravanti's fast button article * http://code.google.com/mobile/articles/fast_buttons.html * * Copyright 2011, ZURB * Released under the MIT License */(function ($) {    $.event.special.fastclick = {	  add: function (handleObj) {          var $this = $(this),          startX,          startY,          coordinates = [];		  		  function onTouchStart(event) {        event.stopPropagation();        $(this).bind('touchend', onClick);        $('body').bind('touchmove', onTouchMove);        startX = event.originalEvent.touches[0].clientX;        startY = event.originalEvent.touches[0].clientY;      }            function pop() {        coordinates.splice(0, 2);      }      function preventGhostClick(x, y) {        coordinates.push(x, y);        window.setTimeout(pop, 2500);      }      function onClick(event) {        event.stopPropagation();        reset();        handleObj.handler.apply(this, arguments);        if (event.originalEvent.type == 'touchend') {          preventGhostClick(startX, startY);        }      }      function onTouchMove(event) {        if (Math.abs(event.originalEvent.touches[0].clientX - startX) > 10 ||            Math.abs(event.originalEvent.touches[0].clientY - startY) > 10) {          reset();        }      }      function reset() {        $this.unbind('touchend');        $('body').unbind('touchmove');      }            function clickBusterOnClick(event) {        for (var i = 0; i < coordinates.length; i += 2) {          var x = coordinates[i];          var y = coordinates[i + 1];          if (Math.abs(event.originalEvent.clientX - x) < 25 && Math.abs(event.originalEvent.clientY - y) < 25) {            event.stopPropagation();            event.preventDefault();          }        }      }		  		  $this.bind('touchstart', onTouchStart);      $this.bind('click', onClick);		  $(document).bind('click', clickBusterOnClick);		}	};}(jQuery));复制代码

js原生创建模拟事件和自定义事件的方法

  1. 模拟鼠标事件

    MDN上已经说得很清楚,尽管为了保持向后兼容MouseEvent.initMouseEvent()仍然可用,但是呢,我们应该使用MouseEvent().
    我们使用如下页面做测试

     
    Button
    复制代码

    打开一下这个页面,并且在打开控制台的情况下,你就可以看到控制台打印了一句话,证明模拟成功了.

转载地址:http://fcpha.baihongyu.com/

你可能感兴趣的文章
ARM QT实现多点触摸【转】
查看>>
Weblogic项目部署教程
查看>>
Gradle -- buildScript块与allprojects块及根级别的repositories区别
查看>>
远程SSH连接服务与基本排错
查看>>
Objective-C学习笔记(十九)——对象方法和类方法的相互调用
查看>>
win10 WmiPrvSE.exe WMI Provider 占用CPU过高的问题
查看>>
hdu 4945 2048(DP)
查看>>
论文阅读:CNN-RNN: A Unified Framework for Multi-label Image Classification
查看>>
开篇有益-解析微软微服务架构eShopOnContainers(一)
查看>>
IE新发现
查看>>
quick check
查看>>
游戏人生(一),我的lua之旅:那些坑爹的CCBReaderLoad
查看>>
Debug时含有的子元素,在代码里获取不到的问题
查看>>
UVA 11020 - Efficient Solutions(set)
查看>>
RStudio版本号管理 整合Git
查看>>
使用 PHPMailer 发送邮件
查看>>
文件系统管理 之 Linux 创建文件系统及挂载文件系统流程详解
查看>>
CSS选择器学习小结
查看>>
什么叫贸工技发展模式?什么叫技工贸发展模式?
查看>>
MyEclipse for Spring 10.0: GWT 2.1 and Spring Scaffolding
查看>>