成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

Zepto源碼學(xué)習(xí)Event模塊

Jeff / 2554人閱讀

摘要:再看這個(gè)源碼的過(guò)程中,因?yàn)閷?duì)事件類型的不充分,導(dǎo)致學(xué)習(xí)起來(lái)有些費(fèi)勁,所以在講這個(gè)板塊之前先對(duì)一些事件進(jìn)行了解。尋找元素上所有的句柄,我們?cè)谥疤岬竭^(guò)這是真的回調(diào)函數(shù),如果有,則終止遍歷。參考文章與為何這般糾纏不清讀源碼之模塊文檔

為什么要看Zepto的源碼,因?yàn)楣居玫氖沁@個(gè)。。。。
再看這個(gè)源碼的過(guò)程中,因?yàn)閷?duì)事件類型的不充分,導(dǎo)致學(xué)習(xí)起來(lái)有些費(fèi)勁,所以在講這個(gè)板塊之前先對(duì)一些事件進(jìn)行了解。

了解基本event信息 事件分發(fā)

下面是觸發(fā)點(diǎn)擊事件的代碼,我們?cè)趇nner上添加點(diǎn)擊事件,在wrapper添加事件,點(diǎn)擊inner都會(huì)觸發(fā)click事件。但這種情況需要我們每次都去點(diǎn)擊回調(diào)函數(shù)才會(huì)執(zhí)行,有沒(méi)有函數(shù)不需要我們手動(dòng)觸發(fā),自動(dòng)觸發(fā)呢?

wrapper
inner

這里用到了一個(gè)需要用到一個(gè)API:
createEvent,具體代碼如下:

  let event = document.createEvent("Event")
  event.initEvent("click", true, true)
  $inner.dispatchEvent(event)

這里我們通過(guò)createEvent創(chuàng)建了一個(gè)事件,并且其后必須馬上進(jìn)行初始化,然后通過(guò)dispatchEvent進(jìn)行事件分發(fā),這樣就用js代碼進(jìn)行事件的觸發(fā),而不需要我們進(jìn)行點(diǎn)擊才能觸發(fā)。

事件模擬

在event模塊中有這么一段代碼

focus = {focus: "focusin", blur: "focusout"},
hover = {mouseenter: "mouseover", mouseleave: "mouseout"}

focus和blur我們都知道,但是為什么要重新隱射focusin和blur事件呢,在mdn中我們可以看到focus和focusin的區(qū)別在于focus不支持事件冒泡,如果不支持事件冒泡,那么帶來(lái)的效果就是不能夠進(jìn)行事件委托。
同樣的mouseenter和mouseleave也不支持事件冒泡,但是mouseenter會(huì)帶來(lái)巨大的性能消耗,所以我們常用mouseover進(jìn)行mouseenter進(jìn)行事件的模擬。在鼠標(biāo)事件中,有一個(gè)relatedTarget事件,在前面提到因?yàn)閙ouseover支持冒泡,那該如何來(lái)模擬mouseenter事件呢。relatedTarget事件屬性返回的是和事件的目標(biāo)節(jié)點(diǎn)相關(guān)的節(jié)點(diǎn)。對(duì)于mouseover事件來(lái)說(shuō),該屬性是鼠標(biāo)指針移到目標(biāo)節(jié)點(diǎn)上所離開(kāi)的那個(gè)節(jié)點(diǎn)。對(duì)于mouseout事件來(lái)說(shuō),該屬性是離開(kāi)目標(biāo)時(shí),鼠標(biāo)進(jìn)入的節(jié)點(diǎn)。根據(jù)上面的描述,我們可以對(duì)relatedTarget的值進(jìn)行判斷:如果值不是目標(biāo)元素,也不是目標(biāo)元素的子元素,就說(shuō)明鼠標(biāo)已經(jīng)移入目標(biāo)元素而不是在元素內(nèi)部移動(dòng)

核心代碼 zid
var _zid = 1
function zid(element) {
  return element._zid || (element._zid = zid++)
}

zid主要是用來(lái)標(biāo)記已經(jīng)綁定時(shí)間的元素,這個(gè)函數(shù)返回元素的_zid,如果沒(méi)有,那就全局的zid加一,并且賦值給元素的_zid屬性

parse
  function parse(event) {
    var parts=("" + event).split(".")
    return {
      e: parts[0], ns: parts.slice(1).sort().join(" ")
    }
  }

parse方法用來(lái)分解事件名和命名空間,{e: 事件名, ns: 命名空間},先把event變成字符串進(jìn)行分割,得到事件名,和命名空間,命名空間可以為s1.s2.s3這種

compatible

這是用來(lái)修正event對(duì)象中瀏覽器的差異

eventMethods = {
  preventDefault: "isDefaultPrevented",
  stopImmediatePropagation: "isImmediatePropagationStopped",
  stopPropagation: "isPropagationStopped"
}
function compatible(event, source) {
  if (source || !event.isDefaultPrevented) {
    source || (source = event)

    $.each(eventMethods, function(name, predicate) {
      var sourceMethod = source[name]
      event[name] = function(){
        this[predicate] = returnTrue
        return sourceMethod && sourceMethod.apply(source, arguments)
      }
      event[predicate] = returnFalse
    })

    event.timeStamp || (event.timeStamp = Date.now())

    if (source.defaultPrevented !== undefined ? source.defaultPrevented :
        "returnValue" in source ? source.returnValue === false :
        source.getPreventDefault && source.getPreventDefault())
      event.isDefaultPrevented = returnTrue
  }
  return event
}

具體來(lái)看看他的代碼

if (source || !event.isDefaultPrevented) {
    source || (source = event)

如果原事件存在,或者事件event的isDefaultPrevented為false或者不存在成立
如果原事件source不存在,就把event賦值給source

  $.each(eventMethod, function(name, predicate) {
    var sourceMethod = source[name]
    event[name] = function(){
      this[predicate] = returnTrue
      return sourceMethod && sourceMethod.apply(source, arguments)
    }
  }) 

這里是遍歷eventMethod,獲取原事件對(duì)應(yīng)的方法名sourceMethod。對(duì)event事件進(jìn)行重新賦值,先把方法賦值為returnTrue函數(shù),返回執(zhí)行原方法的返回值。

event[predicate] = returnFalse

新添加的屬性初始化為returnFalse。

  event.timeStamp || (event.timeStamp = Date.now())

看事件是否支持timeStamp,如果不支持,將Date.now()賦值給timeStamp,最后返回做了兼容性處理的event。

createProxy
function createProxy(event) {
  var key, proxy = { originalEvent: event }
  for (key in event)
    if (!ignoreProperties.test(key) && event[key] !== undefined) proxy[key] = event[key]

  return compatible(proxy, event)
}

這個(gè)函數(shù)的作用在于生成代理的event,首先在proxy的originalEvent掛載本身,然后遍歷event,將event的屬性復(fù)制到proxy,最后返回對(duì)proxy和event做兼容性處理。

add
// element 事件綁定的元素,events綁定的事件列表,fn事件執(zhí)行時(shí)的句柄,data傳遞給事件對(duì)象的數(shù)據(jù)
// 綁定元素的選擇器,delegator事件委托函數(shù),capture哪個(gè)階段執(zhí)行事件句柄
function add(element, events, fn, data, selector, delegator, capture){
    var id = zid(element), set = (handlers[id] || (handlers[id] = []))
    events.split(/s/).forEach(function(event){
      if (event == "ready") return $(document).ready(fn)
      var handler   = parse(event)
      handler.fn    = fn
      handler.sel   = selector
      // emulate mouseenter, mouseleave
      if (handler.e in hover) fn = function(e){
        var related = e.relatedTarget
        if (!related || (related !== this && !$.contains(this, related)))
          return handler.fn.apply(this, arguments)
      }
      handler.del   = delegator
      var callback  = delegator || fn
      handler.proxy = function(e){
        e = compatible(e)
        if (e.isImmediatePropagationStopped()) return
        e.data = data
        var result = callback.apply(element, e._args == undefined ? [e] : [e].concat(e._args))
        if (result === false) e.preventDefault(), e.stopPropagation()
        return result
      }
      handler.i = set.length
      set.push(handler)
      if ("addEventListener" in element)
        element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))
    })
  }

add方法主要是給元素添加事件和事件響應(yīng)。

id = zid(element), set = (handlers[id] || (handlers[id] = []))

獲取element的id,然后通過(guò)id來(lái)獲取他的句柄容器

events.split(/s/).forEach(function (event) {
  if (event == "ready") return $(document).ready(fn)
})

對(duì)events進(jìn)行分解,如果event是ready就直接執(zhí)行fn

var handler   = parse(event)
    handler.fn    = fn
    handler.sel   = selector

對(duì)event進(jìn)行事件名和命名空間進(jìn)行分離,然后將信息掛載到handler上,handler的最終結(jié)構(gòu)是這樣的:

{
  fn: "", // 函數(shù)
  e: "", // 事件名
  ns: "", // 命名空間
  sel: "", // 選擇器
  i: "",  // 函數(shù)索引
  del: "", // 委托函數(shù)
  proxy: "" // 代理函數(shù)
}

繼續(xù)看下面的

 if (handler.e in hover) {
   fn = function (e) {
     var related = e.relatedTarget;
     if (!related || (related !== this && !$.contains(this, related))) {
       return handler.fn.apply(this, arguments)
     }
   }
 }

這就是我們最先提到的mouseover和mouseenter事件,這里就是對(duì)Hover事件進(jìn)行判斷,如果related不存在,或者related不等于目標(biāo)元素,并且不是目標(biāo)元素的子元素,就能夠完成mouseenter的模擬,然后返回函數(shù)處理后的結(jié)果。

handler.proxy = function (e) {
  e = compatible(e);
  if (e.isImmediatePropagationStopped()) {
    return
  }
  e.data = data;
  var result = callback.apply(element, e._args == undefined ? [e] : [e].concat(e._args))
  if (result === false) {
    e.preventDefault();
    e.stopPropagation();
  }
  return result;
}

首先對(duì)e進(jìn)行兼容處理,然后判斷是否阻止默認(rèn)行為,如果是則啥都不做,把data掛載到event對(duì)象上,e是事件執(zhí)行時(shí)的event對(duì)象,并且使用compatible對(duì)e進(jìn)行修正。調(diào)用句柄,并且返回處理結(jié)果。

set.push(handler)
if ("addEventListener" in element) 
  element.addEventListener(realEvent(hander.e), handler.proxy, eventCapture(handler, capture))

向句柄容器添加句柄,并且給元素添加事件。

on
$.fn.on = function (event, selector, data, callback, one) {
  var autoRemove, delegator, $this = this
  if (event && !isString(event)) {
    $.each(event, function (type, fn) {
      $this.on(type, selector, data, fn, one)
    })
    return $this
  }

  if (!isString(selector) && !isFunction(callback) && callback !== false)
    callback = data, data = selector, selector = undefined
  if (callback === undefined || data === false)
    callback = data, data = undefined

  if (callback === false) callback = returnFalse

  return $this.each(function (_, element) {
    if (one) autoRemove = function (e) {
      remove(element, e.type, callback)
      return callback.apply(this, arguments)
    }

    if (selector) delegator = function (e) {
      var evt, match = $(e.target).closest(selector, element).get(0)
      if (match && match !== element) {
        evt = $.extend(createProxy(e), {
          currentTarget: match,
          liveFired: element
        })
        return (autoRemove || callback).apply(match, [evt].concat(slice.call(arguments, 1)))
      }
    }
    }
    }

    add(element, event, callback, data, selector, delegator || autoRemove)
  })
}

on方法是給元素綁定事件,最后調(diào)用的add方法。

 var autoRemove, delegator, $this = this
 if (event && !isString(event)) {
   $.each(event, function (type, fn) {
     $this.on(type, selector, data, fn, one)
   })
   return $this
 }

如果event不是字符串,可能就是對(duì)象或者數(shù)組,然后對(duì)其遍歷,每個(gè)都調(diào)用on函數(shù)。

  if (!isString(selector) && !isFunction(callback) && callback !== false)
      callback = data, data = selector, selector = undefined
    if (callback === undefined || data === false)
      callback = data, data = undefined

這是針對(duì)參數(shù)不同的情況進(jìn)行的操作

return $this.each(function (_, element) {
  if (one) 
  autoRemove = function (e) {
    remove(element, e.type, callback)
    return callback.apply(this, arguments)
  }
})

如果one為true,autoRemove進(jìn)行的操作是把元素上對(duì)應(yīng)的事件進(jìn)行解綁,并且調(diào)用回調(diào)。

if (selector) 
  delegator = function (e) {
    var evt, match = $(e.target).closet(selector, element).get(0)
    if (match && match !== element) {
      evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element})
      return (autoRemove || callback).apply(match, [evt].concat(slice.call(arguments, 1)))
    }
  }
  add(element, event, callback, data, selector, delegator || autoRemove)

如果selector,就需要做事件代理,調(diào)用closet找到最近selector的元素。如果match存在,并且不是當(dāng)前元素,就調(diào)用createProxy(),給當(dāng)前事件對(duì)象創(chuàng)建代理對(duì)象,在調(diào)用extend方法,為其擴(kuò)展currentTarget和liveFired屬性,將代理元素和觸發(fā)元素保存到事件對(duì)象中。
最后執(zhí)行句柄函數(shù),match作為上下文,用代理后的event對(duì)象evt替換掉原句柄函數(shù)的第一個(gè)函數(shù)。

triggerHandler
$.fn.triggerHandler = function (event, args) {
  var e, result;
  this.each(function(i, element) {
    e = createProxy(isString(event) ? $.Event(event) : event)
    e._args = args
    e.target = element
    $.each(findHandlers(element, event.type || event), function (i, handler) {
      result = handler.proxy(e);
      if (e.isImmediatePropagationStopped()) return false;
    })
  })
  return result;
}

triggerHandler用于直接執(zhí)行函數(shù)。

this.each(function(i, element) {
    e = createProxy(isString(event) ? $.Event(event) : event)
    e._args = args
    e.target = element

遍歷元素,然后判斷event如果是字符串則使用$.Event創(chuàng)建事件,然后使用createProxy創(chuàng)建代理。

$.each(findHandlers(element, event.type || event), function (i, handler) {
      result = handler.proxy(e);
      if (e.isImmediatePropagationStopped()) return false;
    })

尋找元素上所有的句柄,handler.proxy我們?cè)谥疤岬竭^(guò)這是真的回調(diào)函數(shù),如果有isImmediatePropagationStopped,則終止遍歷。

Event
$.Event = function (type, props) {
  if (!isString(type)) props = type, type = props.type;
  var event = document.createEvent(specialEvents[type] || "Events"),
    bubbles = true
  if (props)
    for (var name in props)(name == "bubbles") ? (bubbles = !!props[name]) : (event[name] = props[name])
  event.initEvent(type, bubbles, true)
  return compatible(event)
}

簡(jiǎn)單來(lái)說(shuō)這個(gè)部分就是創(chuàng)建事件,初始化事件,然后返回兼容后的event。

參考文章:
mouseenter與mouseover為何這般糾纏不清?
讀Zepto源碼之Event模塊
Zepto文檔

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/96580.html

相關(guān)文章

  • zepto.js學(xué)習(xí)如何手動(dòng)(trigger)觸發(fā)DOM事件

    摘要:好啦我們已經(jīng)解決了是啥的疑問(wèn)了,現(xiàn)在回去開(kāi)始一步步解讀如何實(shí)現(xiàn)手動(dòng)觸發(fā)事件。我們主要看看這里面幾乎含有如何手動(dòng)觸發(fā)一個(gè)事件的大部分步驟和內(nèi)容。 前言 前端在最近幾年實(shí)在火爆異常,vue、react、angular各路框架層出不窮,咱們要是不知道個(gè)雙向數(shù)據(jù)綁定,不曉得啥是虛擬DOM,也許就被鄙視了?;馃岬谋澈笸彩菬o(wú)盡的浮躁,學(xué)習(xí)這些先進(jìn)流行的類庫(kù)或者框架可以讓我們走的更快,但是靜下心...

    spacewander 評(píng)論0 收藏0
  • zepto.js學(xué)習(xí)如何手動(dòng)(trigger)觸發(fā)DOM事件

    摘要:好啦我們已經(jīng)解決了是啥的疑問(wèn)了,現(xiàn)在回去開(kāi)始一步步解讀如何實(shí)現(xiàn)手動(dòng)觸發(fā)事件。我們主要看看這里面幾乎含有如何手動(dòng)觸發(fā)一個(gè)事件的大部分步驟和內(nèi)容。 前言 前端在最近幾年實(shí)在火爆異常,vue、react、angular各路框架層出不窮,咱們要是不知道個(gè)雙向數(shù)據(jù)綁定,不曉得啥是虛擬DOM,也許就被鄙視了?;馃岬谋澈笸彩菬o(wú)盡的浮躁,學(xué)習(xí)這些先進(jìn)流行的類庫(kù)或者框架可以讓我們走的更快,但是靜下心...

    fuyi501 評(píng)論0 收藏0
  • zepto.js學(xué)習(xí)如何手動(dòng)(trigger)觸發(fā)DOM事件

    摘要:好啦我們已經(jīng)解決了是啥的疑問(wèn)了,現(xiàn)在回去開(kāi)始一步步解讀如何實(shí)現(xiàn)手動(dòng)觸發(fā)事件。我們主要看看這里面幾乎含有如何手動(dòng)觸發(fā)一個(gè)事件的大部分步驟和內(nèi)容。 前言 前端在最近幾年實(shí)在火爆異常,vue、react、angular各路框架層出不窮,咱們要是不知道個(gè)雙向數(shù)據(jù)綁定,不曉得啥是虛擬DOM,也許就被鄙視了?;馃岬谋澈笸彩菬o(wú)盡的浮躁,學(xué)習(xí)這些先進(jìn)流行的類庫(kù)或者框架可以讓我們走的更快,但是靜下心...

    Julylovin 評(píng)論0 收藏0
  • Zepto源碼之Touch模塊

    摘要:在觸發(fā)事件前,先將保存定時(shí)器的變量釋放,如果對(duì)象中存在,則觸發(fā)事件,保存的是最后觸摸的時(shí)間。如果有觸發(fā)的定時(shí)器,清除定時(shí)器即可阻止事件的觸發(fā)。其實(shí)就是清除所有相關(guān)的定時(shí)器,最后將對(duì)象設(shè)置為。進(jìn)入時(shí),立刻清除定時(shí)器的執(zhí)行。 大家都知道,因?yàn)闅v史原因,移動(dòng)端上的點(diǎn)擊事件會(huì)有 300ms 左右的延遲,Zepto 的 touch 模塊解決的就是移動(dòng)端點(diǎn)擊延遲的問(wèn)題,同時(shí)也提供了滑動(dòng)的 swip...

    Prasanta 評(píng)論0 收藏0
  • zepto源碼ajax模塊學(xué)習(xí)

    摘要:對(duì)象待會(huì)講,我認(rèn)為是設(shè)計(jì)最巧妙的地方。在跨域的時(shí)候會(huì)使用到,這是為了禁止使用。的目的在于創(chuàng)建一個(gè)事件,然后在觸發(fā)他,如果默認(rèn)行為被取消了,則返回。這是的初始化,默認(rèn)是請(qǐng)求,是新建的對(duì)象,表示瀏覽器是否應(yīng)該被允許緩存響應(yīng)。 在學(xué)習(xí)zepto的源碼的時(shí)候,不得不稱贊這些人的厲害,我雖然能看明白,但是要我寫,估計(jì)吭哧吭哧寫不出來(lái)。雖然現(xiàn)在很少人使用zepto了,但是學(xué)習(xí)這些源碼我相信每次看都...

    hizengzeng 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<