2017年5月

事件委托

核心思路:监听被委托人相关事件,通过当前taget判断当前是否是委托人,是的就执行相关回调。

<ul>
  <li class='target'>
      <span>test</span>
 </li>
</ul>

点击class为 target 的li可以 alert 出其所含内容(如上方代码点击弹出“test”)
在ul动态新增li元素的情况下,也要有相同效果
使用原生js实现,不使用jQuery

    var ul = document.querySelector('ul');
    ul.addEventListener('click', function (e) {
        var target = e.target,
            f = function (t) { console.log(t);
                var className = t.className;
                if (t.tagName === 'LI' && className && className.indexOf('target') >= 0) {
                    alert(t.innerHTML)
                } else if(t != ul) {
                    f(t.parentNode)
                }
            };
        f(target)
    }, false)

Ajax请求防抖实现

现在有一个按钮 $btn,我们给它绑定了点击事件,让它点一次就发一次POST请求去后端提交统计数据:

 var $btn = $('#btn');
    $btn.click(function(){
        $.ajax({ url: "a.php", type: 'POST', success: function(){
            console.log(ret);
        }});
    });

但是如果用户在很短的时间内点了好几下按钮(比如在1s里快速地点了5下),会向服务器发送很多条数据,这样并发量大的情况下服务器可能吃不消。
而且有时候我们也希望当前一条请求结束了(服务器返回数据了)之后才可以发起第二条请求。

所以请你封装一个叫 $.smartAjax 方法来做防抖处理,它可以有一个参数smartType用于确定防抖方式——如果其值为 0 则要求在 500ms 内只允许发出一条请求;
如果参数smarType值为 1 则表示只允许在前一条收到服务器响应(包括失败的响应)后才可以继续发送新请求。

示例:

    var $btn1 = $('#btn1');
    var $btn2 = $('#btn2');

    $btn1.click(function(){
        //每500ms最多只会发出一条请求
        $.smartAjax({ smartType: 0, url: "a.php", type: 'POST', success: function(){
            console.log(ret);
        }});
    });
    $btn2.click(function(){
        //只有当前一条请求收到服务器响应了,才能再发出新的请求
        $.smartAjax({ smartType: 1, url: "a.php", type: 'POST', success: function(){
            console.log(ret);
        }});
    });

    $.smartAjax = ....   //实现它

实现

$.smartAjax = (function () {
      var _time_start = 0,
          _flag = 1; //用于判断是否可以发送请求

      return function (opt) {
        var noop = function () {},
            validFun = function (f) {
              return typeof f === 'function' ? f : null
            },
            smartType = opt && opt.smartType,
            processResponse = function (callback) {
              ['success', 'error'].forEach(function (resFunName) {
                var resFun = validFun(opt[resFunName]) || noop;
                opt[resFunName] = function () {
                  callback();
                  resFun.call(this, arguments)
                }
              });
            },
            typeCallback = {
              0: function () {
                _flag && (_time_start = Date.now());
                if(_flag==0 && Date.now() - _time_start > 300) {
                  _flag = 1;
                  _time_start = Date.now()
                }
              },
              1: function () {
                if(_time_start) { //防止先调用smartType=0再调用smartType=1会失效
                  _flag = 1;
                  _time_start = 0
                }
                processResponse(function(){
                  _flag = 1;
                })
              }
            };
        var hasSmartType = typeCallback[smartType];
        hasSmartType && typeCallback[smartType]();
        if(_flag) {
          hasSmartType && (_flag = 0);
          return $.ajax(opt);
        } else {
          return $.Deferred()
        }
      }
    })();

hammerjs移动端的触摸手势js库

简介

hammer.js是一个多点触摸手势库,能够为网页添加多点触摸事件,免去自己监听底层touchstart、touchmove、touchend事件。hammer.js不但支持触摸事件的浏览器,在桌面浏览器也能将鼠标点击事件当做触摸,方便开发人员进行调试。

特点

不需要依赖任何其他的框架,并且整个框架非常小。

事件架构

hammer.js主要对触屏的六大事件进行监听。

1.pan事件:在指定的dom区域内,一个手指放下并移动时间,即触屏中的拖动事件。这个事件在触屏中比较常用。该事件还可以对以下事件进行监听并处理:
panstart拖动开始、panmove拖动过程、panend拖动结束、pancancel拖动取消、panleft向左拖动、panright向右拖动、panup向上拖动、pandown向下拖动。
2.pinch事件:在指定区域内两个手指(默认)或多个手指相对移动(越来越近)或相对移动(越来越远)事件。该事件分别对以下事件进行监听:
pinchstart多点触控开始、pinchmove多点触控过程、pinchend多点触控结束、pinchcancel多点触控取消、pinchin多点触控手指靠近、pinchout多点触控手指越来越远。
3.press事件:在指定的dom区域内按压,该事件不能包含移动,最小按压时间为500毫秒。常用于手机上的“复制、粘贴”等功能。该事件对以下事件进行监听处理:
pressup按压事件离开时触发
4.rotate事件:在指定的dom区域内,当两个手指或更多手指呈圆形旋转的时候触发。该事件对以下事件进行监听:
rotatestart旋转开始、rotatemove旋转过程、rotateend旋转结束、rotatecancel旋转取消。
5.swipe事件:一个手指在屏幕上滑动,即滑动事件。该事件对以下事件进行监听:
Swipeleft:向左滑动、Swiperight:向右滑动、Swipeup:向上滑动、Swipedown:向下滑动。
6.tap事件:在指定dom事件区域内,一个手指轻拍或点击时触发该事件(类似PC端的click)。该事件最大点击时间为250毫秒,如果超过250毫秒则按Press事件进行处理。

注意点

tap和click在安卓系统上可以同时触发,但click事件会有几百毫秒的延迟判断是否是点击事件,即tap事件先触发,过一段时间在触发click事件。

使用方法

<script>
//创建一个新的hammer对象并且在初始化时,指定要处理的dom元素
var hammertime=new Hammer(document.getElementById('test'))
//为该dom元素添加指定的事件,如触屏移动事件
hammertime.on('pan',function(e){
    console.log(e)
})
</script>
rotate,pinch需要手动添加事件监听程序。

//如
 var hammertime = new Hammer(document.getElementById("test"));
 hammertime.add(new Hammer.rotate());
 hammertime.on("rotate",function(){.......})

JS实现网站点击事件的统计功能

JS实现网站点击事件的统计功能。

/** 
 * 点击事件上报,分为立即上报和延时上报,延时上报通过cookie存储。 
 *  
 * 一、配置参数,主要用于定义上报的一些配置信息。通过在外部定义_clickc对象重置参数。 
 *     参数名称       类型          默认值             说明 
 *     selector:      string        '_click_rp'        点击触发的选择器,支持ID、class 
 *     prefix:        string        '_rp_'             需要上报的参数属性名前缀,如_rp_type,表示要上报type参数的值 
 *     cookie:        string        '_click_rp'        延迟上报时的cookie名称 
 *     domain:        string        '.skye.com'    cookie存储的域名(可以通过使用的网站来获得) 
 *     delay:         boolean       false              是否延迟上报,延迟上报通过cookie实现 
 *     delay_attr:    string        _delay             标签中指定是否延迟上报,优先级最高,ture延时,其他不延时。 
 *      
 * 二、外部参数,主要用于定义上报的参数。通过在外部定义_clickq数组增加参数。 
 * 
 * 三、标签参数,使用前缀_rp_定义,上报的时候会将所有_rp_开头的参数上报。参数的格式分为两种,1种纯字符,1中回调函数。 
 * <a class="_click_rp" href="" _rp_a="aa" _rp_b="bb">a</a>,表示上报时的参数为a=aa&b=bb 
 * 1,纯字符,直接定义字符,表示需要上传参数的值。 
 * 2,回调函数,以javascript:开头。只需定义函数体,在函数体中返回参数的值。 
 * 如,<a href="/qa_question/press.html" id="ques_search_btn" class="_click_rp" _rp_act="javascript:if($('#ques_search_btn').text()=='提问'){return 'act_qa_ques';}else{return 'act_search';}"><span>提问</span></a> 
 * 
 * 四、延时上报,分为三种优先级,如下由高到低 
 * 1,标签属性_delay是否指定为true,如果是表示延迟上报。 
 * 2,是否为特定标签,如a标签本窗口打开(target等于"_self"或空),submit按钮。 
 * 3,配置参数中指定的delay参数。 
 *  
 * 五、支持:需要依赖jQuery插件。 
 *  
 * 六、使用案列 
 * 1,引入JS 
 * var _clickq = _clickq || []; 
 * _clickq.push(['param1', 'value1']); 
 * var _clickc = {selector:'_click_rps'}; 
 * (function() { 
 *  var click = document.createElement("script"); 
 *  click.src = "//cache.skye.com/js/lib/stat/click.js"; 
 *  var s = document.getElementsByTagName("script")[0];  
 *  s.parentNode.insertBefore(click, s); 
 * })(); 
 *  
 * 2,定义选择器和上传参数 
 * 如,<a class="_click_rp" href="" _rp_a="aa" _rp_b="bb">a</a> 
 */  
(function () {  
    // 默认参数  
    var options = {  
        selector: '_click_rp',  
        prefix: '_rp_',  
        cookie: '_click_rp',  
        domain: '.skye.com',  
        delay: false,  
        delay_attr: '_delay'  
    };  
  
    var params = {};  
    var _params = {};  
    var clickObj = null; // 当前点击对象  
  
    // 获得对象  
    var getObject = function(selector) {  
        if (typeof(selector) == 'object') {  
            return selector;  
        } else {  
            var obj = $('#'+selector);  
            if (obj.length) {  
                return obj;  
            }  
            obj = $('.'+selector);  
            if (obj.length) {  
                return obj;  
            }  
            return null;  
        }  
    }  
    // 获得选择器  
    var getSelector = function(selector) {  
        return '#' + selector + ',.' + selector;  
    }  
    // 操作cookie函数  
    var getCookie = function(c_name) {  
        if (document.cookie.length>0) {  
            c_start = document.cookie.indexOf(c_name + "=")  
            if (c_start!=-1) {   
                c_start=c_start + c_name.length+1   
                c_end=document.cookie.indexOf(";",c_start)  
                if (c_end==-1) c_end=document.cookie.length  
                return unescape(document.cookie.substring(c_start,c_end))  
            }  
        }  
        return "";  
    }  
    var setCookie = function(c_name,value,expiredays,path,domain) {  
        var exdate = new Date()  
        exdate.setDate(exdate.getDate()+expiredays)  
        var cookie = c_name+ "=" +escape(value)+((expiredays==null) ? "" : ";expires="+exdate.toGMTString());  
        if (path) cookie = cookie + ";path=" + path;  
        if (domain) cookie = cookie + ";domain=" + domain;  
        document.cookie = cookie;  
    }  
  
    // 获得标签中的参数  
    var getAttrParam = function() {  
        if ( clickObj ) {  
            var attrs = clickObj.get(0).attributes;  
            $.each(attrs, function(i) {  
                var name = attrs[i].name;  
                if ( name.indexOf(options.prefix) == 0 ) {  
                    name = name.replace(options.prefix, '');  
                    var value = attrs[i].value;  
                    if ( value.indexOf('javascript:') == 0 ) {  
                        // 执行js获得参数值  
                        value = value.replace('javascript:', '');  
                        eval('var valFun = function() {'+ value +'};');  
                        value = valFun();  
                    }  
                    params[name] = value;  
                }  
            });  
        }  
    }  
    // 获得默认参数  
    var getDefaultParam = function() {  
        if(document) {  
            params.url = document.URL || '';  
            params.referrer = document.referrer || '';  
        }  
        // 时间  
        var date = new Date();  
        params.ltime = date.getTime() / 1000;  
  
        // 时间戳  
        params.t = date.getTime();  
    }  
    var getParamStr = function() {  
        getAttrParam();  
        getDefaultParam();  
        // 合并配置参数  
        for(var key in _params) {  
            params[key] = _params[key];  
        }  
        //拼接参数串  
        var args = '';   
        for(var i in params) {  
            if(args != '') {  
                args += '&';  
            }     
            args += i + '=' + encodeURIComponent(params[i]);  
        }  
        return args;  
    }  
  
    // 清空参数  
    var clearParam = function() {  
        params = {};  
    }  
  
    // 是否立即上报,如果跳转页面,则计入延时上报  
    var getIsDelay = function() {  
        if ( clickObj ) {  
            // 有具体指定  
            if ( clickObj.attr(options.delay_attr) == 'true' ) {  
                return true;  
            }  
            // 特定标签  
            // a标签  
            if ( clickObj.is('a') ) {  
                if ( clickObj.attr('href').indexOf('javascript:') == 0 ) {  
                    return false;  
                }  
                if ( clickObj.attr('target') && clickObj.attr('target') != '_self' ) {  
                    return false;  
                }  
                return true;  
            }  
            // submit按钮  
            if ( (clickObj.is('input') || clickObj.is('button')) && clickObj.attr('type') == 'submit' ) {  
                return true;  
            }  
        }  
        return options.delay;  
    }  
      
    // 加入cookie,下次上报  
    var setDelayCookie = function() {  
        // 获得参数  
        var args = getParamStr();  
        var cookieStr = getCookie(options.cookie);  
        if ( cookieStr == '' ) {  
            cookieStr = args;  
        } else {  
            cookieStr = cookieStr + ',' + args;  
        }  
        setCookie(options.cookie, cookieStr, 7, '/', options.domain);  
        clearParam();  
    }  
  
    // 上报cookie  
    var rpCookie = function() {  
        // 获得cookie,循环操作  
        var cookieStr = getCookie(options.cookie);  
        if ( cookieStr ) {  
            var cookieArr = cookieStr.split(',');  
            for(var key in cookieArr){    
                rpClick(cookieArr[key]);  
            }    
            setCookie(options.cookie, '', 7, '/', options.domain);  
        }  
    }  
  
    // 上报  
    var rpClick = function(args) {  
        if ( args == undefined ) {  
            args = getParamStr();  
        }  
        var img = new Image(1, 1);  
        img.src = 'http://data.skye.com/stat/click?' + args;  
        console.info(img.src);  
        clearParam();  
    }  
  
    // js上报函数  
    var rpComm = function(obj) {  
        console.info('click');  
        clickObj = obj;  
        if ( getIsDelay() ) {  
            setDelayCookie();  
        } else {  
            rpClick();  
        }  
    }  
  
    //解析外部配置  
    if(_clickc) {  
        for(var i in _clickc) {  
            options[i] = _clickc[i];  
        }     
    }  
      
    //解析外部参数  
    if(_clickq) {  
        for(var i in _clickq) {  
            _params[_clickq[i][0]] = _clickq[i][1];  
        }  
    }  
  
    // 提供外部js函数  
    $.rpComm = function(obj) {  
        rpComm(obj);  
    }  
    $.fn.rpComm = function() {  
        rpComm($(this));  
    }  
  
    // cookie中的上报  
    rpCookie();  
  
    // 初始化信息  
    var _time = new Date().valueOf();  
    var selector = getSelector(options.selector);  
    $('body').delegate(selector,'click',function() {  
        // 连续点击限制  
        if(new Date().valueOf() - _time < 300) {  
            return;  
        }  
        _time = new Date().valueOf();  
        rpComm($(this));  
    });  
})();