国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

mouseenter與mouseover為何這般糾纏不清?

_Dreams / 3472人閱讀

摘要:而當(dāng)鼠標(biāo)本身在元素邊界內(nèi)時(shí),要觸發(fā)該事件,必須先將鼠標(biāo)移出元素邊界外,再次移入才能觸發(fā)。造成以上現(xiàn)象本質(zhì)上是事件不支持冒泡所致。事件屬性返回與事件的目標(biāo)節(jié)點(diǎn)相關(guān)的節(jié)點(diǎn)。我們通過排查和,最后只留下,也就是與事件一起觸發(fā)的時(shí)機(jī)。

前言

原文地址

項(xiàng)目地址

不知道大家在面試或者工作過程中有沒有被mouseovermouseenter(對(duì)應(yīng)的是mouseoutmouseleave)事件所困擾。自己之前在面試的時(shí)候就有被問到諸如mouseover和mouseenter事件的異同之類的問題?當(dāng)時(shí)沒有答出來,一直也對(duì)這兩個(gè)事件有點(diǎn)模糊不清,趁著最近正在讀zepto源碼,準(zhǔn)備寫一篇這方面的文章,如果有錯(cuò)誤,請(qǐng)大家指正。

mouseenter與mouseover的異同?

要說清楚mouseenter與mouseover有什么不同,也許可以從兩方面去講。

是否支持冒泡

事件的觸發(fā)時(shí)機(jī)

先來看一張圖,對(duì)這兩個(gè)事件有一個(gè)簡單直觀的感受。

再看看官網(wǎng)對(duì)mouseenter的解釋

mouseenter | onmouseenter event.aspx)

The event fires only if the mouse pointer is outside the boundaries of the object and the user moves the mouse pointer inside the boundaries of the object. If the mouse pointer is currently inside the boundaries of the object, for the event to fire, the user must move the mouse pointer outside the boundaries of the object and then back inside the boundaries of the object.

大概意思是說:當(dāng)鼠標(biāo)從元素的邊界之外移入元素的邊界之內(nèi)時(shí),事件被觸發(fā)。而當(dāng)鼠標(biāo)本身在元素邊界內(nèi)時(shí),要觸發(fā)該事件,必須先將鼠標(biāo)移出元素邊界外,再次移入才能觸發(fā)。(英語比較渣?,湊合看哈)

Unlike the onmouseover event, the onmouseenter event does not bubble.

大概意思是:和mouseover不同的是,mouseenter不支持事件冒泡 (英語比較渣?,湊合看哈)

由于mouseenter不支持事件冒泡,導(dǎo)致在一個(gè)元素的子元素上進(jìn)入或離開的時(shí)候會(huì)觸發(fā)其mouseover和mouseout事件,但是卻不會(huì)觸發(fā)mouseenter和mouseleave事件

我們用一張動(dòng)圖來看看他們的區(qū)別(或者點(diǎn)擊該鏈接體驗(yàn))。

我們給左右兩邊的ul分別添加了mouseovermouseenter事件,當(dāng)鼠標(biāo)進(jìn)入左右兩邊的ul時(shí),mouseovermouseenter事件都觸發(fā)了,但是當(dāng)移入各自的子元素li的時(shí)候,觸發(fā)了左邊ul上的mouseover事件,然而右邊ul的mouseenter事件沒有被觸發(fā)。

造成以上現(xiàn)象本質(zhì)上是mouseenter事件不支持冒泡所致。

如何模擬mouseenter事件。

可見mouseover事件因其具有冒泡的性質(zhì),在子元素內(nèi)移動(dòng)的時(shí)候,頻繁被觸發(fā),如果我們不希望如此,可以使用mouseenter事件代替之,但是早期只有ie瀏覽器支持該事件,雖然現(xiàn)在大多數(shù)高級(jí)瀏覽器都支持了mouseenter事件,但是難免會(huì)有些兼容問題,所以如果可以自己手動(dòng)模擬,那就太好了。

關(guān)鍵因素: relatedTarget 要想手動(dòng)模擬mouseenter事件,需要對(duì)mouseover事件觸發(fā)時(shí)的事件對(duì)象event屬性relatedTarget了解。

relatedTarget事件屬性返回與事件的目標(biāo)節(jié)點(diǎn)相關(guān)的節(jié)點(diǎn)。

對(duì)于mouseover事件來說,該屬性是鼠標(biāo)指針移到目標(biāo)節(jié)點(diǎn)上時(shí)所離開的那個(gè)節(jié)點(diǎn)。

對(duì)于mouseout事件來說,該屬性是離開目標(biāo)時(shí),鼠標(biāo)指針進(jìn)入的節(jié)點(diǎn)。

對(duì)于其他類型的事件來說,這個(gè)屬性沒有用。

重新回顧一下文章最初的那張圖,根據(jù)上面的解釋,對(duì)于ul上添加的mouseover事件來說,relatedTarget只可能是

ul的父元素wrap(移入ul時(shí),此時(shí)也是觸發(fā)mouseenter事件的時(shí)候, 其實(shí)不一定,后面會(huì)說明),

或者ul元素本身(在其子元素上移出時(shí)),

又或者是子元素本身(直接從子元素A移動(dòng)到子元素B)。

根據(jù)上面的描述,我們可以對(duì)relatedTarget的值進(jìn)行判斷:如果值不是目標(biāo)元素,也不是目標(biāo)元素的子元素,就說明鼠標(biāo)已移入目標(biāo)元素而不是在元素內(nèi)部移動(dòng)。

條件1: 不是目標(biāo)元素很好判斷e.relatedTarget !== target(目標(biāo)元素)

條件2:不是目標(biāo)元素的子元素,這個(gè)應(yīng)該怎么判斷呢?

ele.contains

這里需要介紹一個(gè)新的api [node.contains(otherNode)

](https://developer.mozilla.org... 表示傳入的節(jié)點(diǎn)是否為該節(jié)點(diǎn)的后代節(jié)點(diǎn), 如果 otherNode 是 node 的后代節(jié)點(diǎn)或是 node 節(jié)點(diǎn)本身.則返回true , 否則返回 false

用法案例

  • 1
  • 2
let $list = document.querySelector(".list")
let $item = document.querySelector(".item")
let $test = document.querySelector(".test")

$list.contains($item) // true
$list.contains($test) // false
$list.contains($list) // true

那么利用contains這個(gè)api我們便可以很方便的驗(yàn)證條件2,接下來我們封裝一個(gè)contains(parent, node)函數(shù),專門用來判斷node是不是parent的子節(jié)點(diǎn)

let contains = function (parent, node) {
  return parent !== node && parent.contains(node)
}

用我們封裝過后的contains函數(shù)再去試試上面的例子

contains($list, $item) // true
contains($list, $test) // false
contains($list, $list) // false (主要區(qū)別在這里)

這個(gè)方法很方便地幫助我們解決了模擬mouseenter事件中的條件2,但是悲催的ode.contains(otherNode),具有瀏覽器兼容性,在一些低級(jí)瀏覽器中是不支持的,為了做到兼容我們?cè)賮砀膶懸幌耤ontains方法

let contains = docEle.contains ? function (parent, node) {
  return parent !== node && parent.contains(node)
} : function (parent, node) {
  let result = parent !== node

  if (!result) { // 排除parent與node傳入相同的節(jié)點(diǎn)
    return result
  }

  if (result) {
    while (node && (node = node.parentNode)) {
      if (parent === node) {
        return true
      }
    }
  }

  return false
}

說了這么多,我們來看看用mouseover事件模擬mouseenter的最終代碼

// callback表示如果執(zhí)行mouseenter事件時(shí)傳入的回調(diào)函數(shù)

let emulateEnterOrLeave = function (callback) {
  return function (e) {
    let relatedTarget = e.relatedTarget
    if (relatedTarget !== this && !contains(this, relatedTarget)) {
      callback.apply(this, arguments)
    }
  }
}

模擬mouseenter與原生mouseenter事件效果對(duì)比

html

wrap, mouseenter
    count:
  • 1
  • 2
  • 3
wrap, emulate mouseenter,用mouseover模擬實(shí)現(xiàn)mouseenter
    count:
  • 1
  • 2
  • 3

css

.wrap{
  width: 50%;
  box-sizing: border-box;
  float: left;
}

.wrap, .list{
  border: solid 1px green;
  padding: 30px;
  margin: 30px 0;
}

.list{
  border: solid 1px red;
}

.list li{
  border: solid 1px blue;
  padding: 10px;
  margin: 10px;
}

.count{
  color: red;
}

javascript

let $mouseenter = document.querySelector(".mouseenter")
let $emulateMouseenter = document.querySelector(".emulate-mouseenter")
let $enterCount = document.querySelector(".mouseenter .count")
let $emulateMouseenterCounter = document.querySelector(".emulate-mouseenter .count")

let addCount = function (ele, start) {
  return function () {
    ele.innerHTML = ++start
  }
}

let docEle = document.documentElement
  let contains = docEle.contains ? function (parent, node) {
    return parent !== node && parent.contains(node)
  } : function (parent, node) {
  let result = parent !== node

  if (!result) {
    return result
  }

  if (result) {
    while (node && (node = node.parentNode)) {
      if (parent === node) {
        return true
      }
    }
  }

  return false
}

let emulateMouseenterCallback = addCount($emulateMouseenterCounter, 0)
  
let emulateEnterOrLeave = function (callback) {
  return function (e) {
    let relatedTarget = e.relatedTarget
    if (relatedTarget !== this && !contains(this, relatedTarget)) {
      callback.apply(this, arguments)
    }
  }
}

$mouseenter.addEventListener("mouseenter", addCount($enterCount, 0), false)
$emulateMouseenter.addEventListener("mouseover", emulateEnterOrLeave(emulateMouseenterCallback), false)  

效果預(yù)覽

詳細(xì)代碼點(diǎn)擊

代碼示例點(diǎn)擊

好了,我們已經(jīng)通過mouseove事件完整的模擬了mouseenter事件,但是反過頭來看看

對(duì)于ul上添加的mouseover事件來說,relatedTarget只可能是

ul的父元素wrap(移入ul時(shí),此時(shí)也是觸發(fā)mouseenter事件的時(shí)候, 其實(shí)不一定,后面會(huì)說明),

或者ul元素本身(在其子元素上移出時(shí)),

又或者是子元素本身(直接從子元素A移動(dòng)到子元素B)。

我們通過排查2和3,最后只留下1,也就是mouseenter與mouseover事件一起觸發(fā)的時(shí)機(jī)。既然這樣我們?yōu)槭裁床幌襁@樣判斷呢?

target.addEventListener("mouseover", function (e) {
  if (e.relatedTarget === this.parentNode) {
    // 執(zhí)行mouseenter的回調(diào)要做的事情  
  }
}, false)

這樣不是更加簡單嗎?,何必要折騰通過排查2和3來做?

原因是,target的父元素有一定的占位空間的時(shí)后,我們這樣寫是沒有太大問題的,但是反之,這個(gè)時(shí)候e.relatedTarget就可能是target元素的父元素,又祖先元素中的某一個(gè)。我們無法準(zhǔn)確判斷e.relatedTarget到底是哪個(gè)元素。所以通過排除2和3應(yīng)該是個(gè)更好的選擇。

用mouseout模擬mouseleave事件

當(dāng)mouseout被激活時(shí),relatedTarget表示鼠標(biāo)離開目標(biāo)元素時(shí),進(jìn)入了哪個(gè)元素,我們同樣可以對(duì)relatedTarget的值進(jìn)行判斷:如果值不是目標(biāo)元素,也不是目標(biāo)元素的子元素,就說明鼠標(biāo)已移出目標(biāo)元素

我們同樣可以用上面封裝的函數(shù)完成

// callback表示如果執(zhí)行mouseenter事件時(shí)傳入的回調(diào)函數(shù)

let emulateEnterOrLeave = function (callback) {
  return function (e) {
    let relatedTarget = e.relatedTarget
    if (relatedTarget !== this && !contains(this, relatedTarget)) {
      callback.apply(this, arguments)
    }
  }
}

詳細(xì)代碼點(diǎn)擊

代碼示例點(diǎn)擊

結(jié)尾

文中也許有些觀點(diǎn)不夠嚴(yán)謹(jǐn),歡迎大家拍磚。

原文地址

項(xiàng)目地址

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

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

相關(guān)文章

  • mouseentermouseover為何這般糾纏不清

    摘要:而當(dāng)鼠標(biāo)本身在元素邊界內(nèi)時(shí),要觸發(fā)該事件,必須先將鼠標(biāo)移出元素邊界外,再次移入才能觸發(fā)。造成以上現(xiàn)象本質(zhì)上是事件不支持冒泡所致。事件屬性返回與事件的目標(biāo)節(jié)點(diǎn)相關(guān)的節(jié)點(diǎn)。我們通過排查和,最后只留下,也就是與事件一起觸發(fā)的時(shí)機(jī)。 前言 原文地址 項(xiàng)目地址 不知道大家在面試或者工作過程中有沒有被mouseover和mouseenter(對(duì)應(yīng)的是mouseout和mouseleave)事件所困...

    王巖威 評(píng)論0 收藏0
  • Zepto源碼學(xué)習(xí)Event模塊

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

    Jeff 評(píng)論0 收藏0
  • 讀Zepto源碼之Event模塊

    摘要:不支持事件冒泡帶來的直接后果是不能進(jìn)行事件委托,所以需要對(duì)和事件進(jìn)行模擬。調(diào)用函數(shù),分隔出參數(shù)的事件名和命名空間。這里判斷是否為函數(shù),即第一種傳參方式,調(diào)用函數(shù)的方法,將上下文對(duì)象作為的第一個(gè)參數(shù),如果存在,則與的參數(shù)合并。 Event 模塊是 Zepto 必備的模塊之一,由于對(duì) Event Api 不太熟,Event 對(duì)象也比較復(fù)雜,所以乍一看 Event 模塊的源碼,有點(diǎn)懵,細(xì)看下...

    vpants 評(píng)論0 收藏0
  • zepto源碼分析之form模塊

    摘要:形如源代碼在的原型上添加了相關(guān)方法。類似源代碼每個(gè)表單的和都通過編碼最后通過符號(hào)分割有了的基礎(chǔ),就是將相應(yīng)的和都通過編碼,然后用符號(hào)進(jìn)行分割,也就達(dá)到了我們要的結(jié)果。 前言 JavaScript最初的一個(gè)應(yīng)用場(chǎng)景就是分擔(dān)服務(wù)器處理表單的責(zé)任,打破處處依賴服務(wù)器的局面,這篇文章主要介紹zepto中form模塊關(guān)于表單處理的幾個(gè)方法,serialize、serializeArray、sub...

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

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

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<