• zepto.js的事件处理


    能够深入理解zepto对事件的处理,那么整个JS的事件处理就应该差不多合格了,事件处理是JS语言的一个难点。

    1. 首先来看$.event函数

    JS中有很多事件,都是已经定义好了,我们直接调用就可以,例如熟悉的click事件,直接对dom绑定一个事件,点击该dom就能触发这个事件,但是有这样的场景:我点击一个dom,重新打开一个页面。按照常规,可以通过window.open来执行,也可以模拟一个连接,在这个链接上绑定click,之后触发这个click事件。代码如下:

    var a = document.createElement("a");
    a.setAttribute("href", url);
    a.setAttribute("target", "_blank");
    a.setAttribute("id", "openwin");
    document.body.appendChild(a);
    //模拟点击事件
    var m = document.createEvent("MouseEvents"); //FF的处理
    m.initEvent("click", true, true);
    a.dispatchEvent(m);

    在zepto.js的ajax源码中,有很多注册事件,这些事件都是通过下来代码来完成的。

    function triggerAndReturn(context, eventName, data) {
        //步骤1:注册事件
        var event = $.Event(eventName) 
        //步骤2:分发事件 
        $(context).trigger(event, data)
        return !event.defaultPrevented
      }
    在注册和分发事件中,有三个步骤,对于与三个函数:
    document.createEvent()  //新建
    event.initEvent()       //初始化
    this.dispatchEvent()    //元素触发事件

    来看看$.event源码:

    $.Event = function(type, props) {
        if (!isString(type)) props = type, type = props.type
        //click,mousedown,mouseup,mousemove的事件是MouseEvents,其他的是Events。
        var event = document.createEvent(specialEvents[type] || 'Events'), bubbles = true
        //event.initEvent(eventType,canBubble,cancelable) 
        //eventType  字符串值。事件的类型。
        //canBubble 事件是否起泡。
        //cancelable 是否可以用 preventDefault() 方法取消事件。把bubbles从props中过滤出来,单独处理。
        if (props) for (var name in props) (name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name])
        event.initEvent(type, bubbles, true)
        return compatible(event)
      }
    
    /*
      stopImmediatePropagation方法作用在当前节点以及事件链上的所有后续节点上。
      目的是在执行完当前事件处理程序之后,停止当前节点以及所有后续节点的事件处理程序的运行。
      stopPropagation方法作用在后续节点上,目的在执行完绑定到当前元素上的所有事件处理程序之后,停止执行所有后续节点的事件处理程序
    */
    eventMethods = {
      preventDefault: 'isDefaultPrevented',
      stopImmediatePropagation: 'isImmediatePropagationStopped',
      stopPropagation: 'isPropagationStopped'
    }
    function compatible(event, source) {
        if (source || !event.isDefaultPrevented) {
          source || (source = event)
          /*
            给event注册6个函数,默认[isDefaultPrevented,isImmediatePropagationStopped,isPropagationStopped] = false,
            执行preventDefault之后,对应的preventDefault = {return true;}
          */
          $.each(eventMethods, function(name, predicate) {
            var sourceMethod = source[name]
            event[name] = function(){
              this[predicate] = returnTrue
              return sourceMethod && sourceMethod.apply(source, arguments)
            }
            event[predicate] = returnFalse
          })
          //给isDefaultPrevented赋值
          if (source.defaultPrevented !== undefined ? source.defaultPrevented :
              'returnValue' in source ? source.returnValue === false :
              source.getPreventDefault && source.getPreventDefault())
            event.isDefaultPrevented = returnTrue
        }
        return event
      }

    再来看看$.trigger事件:

    function fix(event) {
        if (!('defaultPrevented' in event)) {
          event.defaultPrevented = false
          var prevent = event.preventDefault
          //通过preventDefault取消事件的触发。
          event.preventDefault = function() {
            this.defaultPrevented = true
            prevent.call(this)
          }
        }
      }
    $.fn.trigger = function(event, data){
        if (typeof event == 'string') event = $.Event(event)
        //添加preventDefaulted成员和重载preventDefault事件。
        fix(event)
        event.data = data
        return this.each(function(){
          //是dom节点都会有dispatchEvent事件,不是dom,如果有dispatchEvent事件也会执行。
          if('dispatchEvent' in this) this.dispatchEvent(event)
        })
      }

    还有一个triggerHandler事件,它与trigger有四个不同点:

    1. 它不会引起事件(比如表单提交)的默认行为
    2. trigger() 会操作 jQuery 对象匹配的所有元素,而 .triggerHandler() 只影响第一个匹配元素。
    3. 由 .triggerHandler() 创建的事件不会在 DOM 树中冒泡;如果目标元素不直接处理它们,则不会发生任何事情。
    4. 该方法的返回的是事件处理函数的返回值,而不是具有可链性的 jQuery 对象。此外,如果没有处理程序被触发,则这个方法返回 undefined。

    这个后面我们再讨论。

    2.事件对象

      用JS原生态的绑定事件很easy,而zepto对绑定事件进行重重封装,最明显的莫过于event对象,常规绑定事件。

        //每个element都有一个_zid来判断该element上已经绑定了几个事件。
        var id = zid(element), 
        set = (handlers[id] || (handlers[id] = []))
        eachEvent(events, fn, function(event, fn){
          //如果没有事件委托,add只有element, events, fn三个参数
          //element: 元素节点 events=["click","mouseup"...]
          //fn:函数名或者匿名函数。
          var delegate = getDelegate && getDelegate(fn, event),
            callback = delegate || fn
          var proxyfn = function (event) {
            var result = callback.apply(element, [event].concat(event.data))
            //callback返回为false,阻止默认事件。
            if (result === false) event.preventDefault()
            return result
          }
          /*
            event = {
                e:  事件名称
                ns: 事件命名空间的对象
                data: 参数
            }
           */
          var handler = $.extend(parse(event), 
                {
                    fn: fn, 
                    proxy: proxyfn, 
                    sel: selector, 
                    del: delegate, 
                    i: set.length
                })
          set.push(handler)
          element.addEventListener(handler.e, proxyfn, capture)
        })
      }

    委托绑定事件:

    $.fn.delegate = function(selector, event, callback){
        //委托事件不需要冒泡到父节点,只针对特定元素。
        var capture = false
        if(event == 'blur' || event == 'focus'){
          if($.iswebkit)
            event = event == 'blur' ? 'focusout' : event == 'focus' ? 'focusin' : event
          else
            capture = true
        }
        return this.each(function(i, element){
          add(element, event, callback, selector, function(fn){
            return function(e){
              var evt, 
              match = $(e.target).closest(selector, element).get(0)
              //匹配到特定的元素
              if (match) {
                evt = $.extend(createProxy(e), 
                    {   currentTarget: match,    
                        liveFired: element
                    })
                return fn.apply(match, [evt].concat([].slice.call(arguments, 1)))
              }
            }
          }, capture)
        })
      }

    还有一种绑定事件,只绑定一次,

    $.fn.one = function(event, callback){
        return this.each(function(i, element){
         //没有子元素选择参数
          add(this, event, callback, null, function(fn, type){
            return function(){
              var result = fn.apply(element, arguments)
              //触发之后,删除该事件。
              remove(element, type, fn)
              return result
            }
          })
        })

     

  • 相关阅读:
    vuex之store拆分即多模块状态管理
    vue项目中使用vueX
    vue中父子组件的参数传递和应用
    VUE中使用vue-awesome-swiper
    VUE真实项目中常用的生命周期和参数
    VUE生命周期
    vue+mockjs 模拟数据,请求回调的应用
    Vue项目搭建与部署还有调试插件Vue.js devtools
    tableTD中添加对角斜线
    前端面试题及答案,理论知识
  • 原文地址:https://www.cnblogs.com/liuyinlei/p/5364550.html
Copyright © 2020-2023  润新知