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

資訊專欄INFORMATION COLUMN

向Zepto學(xué)習(xí)關(guān)于"偏移"的那些事

hzx / 1176人閱讀

摘要:獲得當(dāng)前元素相對(duì)于的位置。返回一個(gè)對(duì)象含有和當(dāng)給定一個(gè)含有和屬性對(duì)象時(shí),使用這些值來對(duì)集合中每一個(gè)元素進(jìn)行相對(duì)于的定位。獲取對(duì)象集合中第一個(gè)元素相對(duì)于其的位置。結(jié)尾以上就是中與偏移相關(guān)的幾個(gè)的解析,歡迎指出其中的問題和有錯(cuò)誤的地方。

前言
這篇文章主要想說一下Zepto中與"偏移"相關(guān)的一些事,很久很久以前,我們經(jīng)常會(huì)使用offsetpositionscrollTopscrollLeft等方式去改變?cè)氐奈恢茫麄冎g有什么區(qū)別,是怎么實(shí)現(xiàn)的呢?接下來我們一點(diǎn)點(diǎn)去扒開他們的面紗。

原文鏈接

源碼倉(cāng)庫(kù)

offsetParent

offsetposition兩個(gè)api內(nèi)部的實(shí)現(xiàn)都依賴offsetParent方法,我們先看一下它是怎么一回事。

找到第一個(gè)定位過的祖先元素,意味著它的css中的position 屬性值為“relative”, “absolute” or “fixed” #offsetParent

我們都知道css屬性position用于指定一個(gè)元素在文檔中的定位方式,其初始值是static, css3中甚至還增加了sticky等屬性,不過目前貌似瀏覽器幾乎還未支持。

看一下這個(gè)例子

html

css


javascript

console.log($(".child3").offsetParent()) // child1
console.log(document.querySelector(".child3").offsetParent) // child1

既然原生已經(jīng)有了一個(gè)offsetParentmdn offsetParent屬性供我們使用,為什么Zepto還要自己實(shí)現(xiàn)一個(gè)呢?其實(shí)他們之間還是有些不同的,比如同樣是上面的例子,如果child3的display屬性設(shè)置為了none,原生的offsetParent返回的是null,但是Zepto返回的是包含body元素的Zepto對(duì)象。

源碼分析

offsetParent: function () {
  return this.map(function () {
    var parent = this.offsetParent || document.body
    while (parent && !rootNodeRE.test(parent.nodeName) && $(parent).css("position") == "static")
      parent = parent.offsetParent
    return parent
  })
}

實(shí)現(xiàn)邏輯還是比較簡(jiǎn)單,通過map方法遍歷當(dāng)前選中的元素集合,結(jié)果是一個(gè)數(shù)組,每個(gè)項(xiàng)即是元素的最近的定位祖先元素。

首先通過offsetParent原生DOM屬性去獲取定位元素,如果沒有默認(rèn)是body節(jié)點(diǎn),這里其實(shí)就能解釋前面的child3設(shè)置為display:none,原生返回null,但是Zepto得到的是body了

var parent = this.offsetParent || document.body

再通過一個(gè)while循環(huán)如果

parent元素存在

parent元素不是html或者body元素

parent元素的display屬性是static,則再次獲取parent屬性的offsetParent再次循環(huán)。

offset
獲得當(dāng)前元素相對(duì)于document的位置。返回一個(gè)對(duì)象含有: top, left, width和height

當(dāng)給定一個(gè)含有l(wèi)eft和top屬性對(duì)象時(shí),使用這些值來對(duì)集合中每一個(gè)元素進(jìn)行相對(duì)于document的定位。

offset() ? object

offset(coordinates) ? self v1.0+

offset(function(index, oldOffset){ ... }) ?

#offset

源碼

offset: function (coordinates) {
  if (coordinates) return this.each(function (index) {
    var $this = $(this),
      coords = funcArg(this, coordinates, index, $this.offset()),
      parentOffset = $this.offsetParent().offset(),
      props = {
        top: coords.top - parentOffset.top,
        left: coords.left - parentOffset.left
      }
    if ($this.css("position") == "static") props["position"] = "relative"
    $this.css(props)
  })

  if (!this.length) return null
  if (document.documentElement !== this[0] && !$.contains(document.documentElement, this[0]))
    return { top: 0, left: 0 }
  var obj = this[0].getBoundingClientRect()
  return {
    left: obj.left + window.pageXOffset,
    top: obj.top + window.pageYOffset,
    width: Math.round(obj.width),
    height: Math.round(obj.height)
  }
}

和Zepto中的其他api類似遵循get one, set all原則,我們先來看看獲取操作是如何實(shí)現(xiàn)的。

if (!this.length) return null
if (document.documentElement !== this[0] && !$.contains(document.documentElement, this[0]))
  return { top: 0, left: 0 }
var obj = this[0].getBoundingClientRect()
return {
  left: obj.left + window.pageXOffset,
  top: obj.top + window.pageYOffset,
  width: Math.round(obj.width),
  height: Math.round(obj.height)
}

!this.length如果當(dāng)前沒有選中元素,自然就沒有往下走的必要了,直接return掉

當(dāng)前選中的集合中不是html元素,并且也不是html節(jié)點(diǎn)子元素。直接返回{ top: 0, left: 0 }

接下來的邏輯才是重點(diǎn)。首先通過getBoundingClientRect獲取元素的大小及其相對(duì)于視口的位置,再通過pageXOffset、pageYOffset獲取文檔在水平和垂直方向已滾動(dòng)的像素值,相加既得到我們最后想要的值。

再看設(shè)置操作如何實(shí)現(xiàn)之前,先看下面這張圖,或許會(huì)有助于理解

if (coordinates) return this.each(function(index) {
  var $this = $(this),
      coords = funcArg(this, coordinates, index, $this.offset()),
      parentOffset = $this.offsetParent().offset(),
      props = {
        top: coords.top - parentOffset.top,
        left: coords.left - parentOffset.left
      }

  if ($this.css("position") == "static") props["position"] = "relative"
  $this.css(props)
})

還是那個(gè)熟悉的模式,熟悉的套路,循環(huán)遍歷當(dāng)前元素集合,方便挨個(gè)設(shè)置,通過funcArg函數(shù)包裝一下,使得入?yún)⒓瓤梢允呛瘮?shù),也可以是其他形式。

通過上面那張圖,我們應(yīng)該可以很清晰的看出,如果要將子元素設(shè)置到傳入的coords.left的位置,那其實(shí)

父元素(假設(shè)父元素是定位元素)相對(duì)文檔的左邊距(parentOffset.left)

子元素相對(duì)父元素的左邊距(left)

相加得到的就是入?yún)?b>coords.left

那再做個(gè)減法,就得到我們最終通過css方法需要設(shè)置的left和top值啦。

需要注意的是如果元素的定位屬性是static,則會(huì)將其改為relative定位,相對(duì)于其正常文檔流來計(jì)算。

position
獲取對(duì)象集合中第一個(gè)元素相對(duì)于其offsetParent的位置。
position: function() {
  if (!this.length) return

  var elem = this[0],
    offsetParent = this.offsetParent(),
    offset = this.offset(),
    parentOffset = rootNodeRE.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset()
  offset.top -= parseFloat($(elem).css("margin-top")) || 0
  offset.left -= parseFloat($(elem).css("margin-left")) || 0
  parentOffset.top += parseFloat($(offsetParent[0]).css("border-top-width")) || 0
  parentOffset.left += parseFloat($(offsetParent[0]).css("border-left-width")) || 0
  return {
    top: offset.top - parentOffset.top,
    left: offset.left - parentOffset.left
  }
}

先看一個(gè)例子

html

css

.parent{
  width: 400px;
  height: 400px;
  border: solid 1px red;
  padding: 10px;
  margin: 10px;
  position: relative;
}

.child{
  width: 200px;
  height: 200px;
  border: solid 1px green;
  padding: 20px;
  margin: 20px;
}

console.log($(".child").position()) // {top: 10, left: 10}

下面分別是父子元素的盒模型以及標(biāo)注了需要獲取的top的值

接下來我們來看它怎么實(shí)現(xiàn)的吧,come on!!!

第一步

var offsetParent = this.offsetParent(),
// Get correct offsets
// 獲取當(dāng)前元素相對(duì)于document的位置
offset = this.offset(),
// 獲取第一個(gè)定位祖先元素相對(duì)于document的位置,如果是根元素(html或者body)則為0, 0
parentOffset = rootNodeRE.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset()

第二步

// 相對(duì)于第一個(gè)定位祖先元素的位置關(guān)系不應(yīng)該包括margin的舉例,所以減去
offset.top -= parseFloat($(elem).css("margin-top")) || 0
offset.left -= parseFloat($(elem).css("margin-left")) || 0

第三步

// 祖先定位元素加上border的寬度
parentOffset.top += parseFloat($(offsetParent[0]).css("border-top-width")) || 0
parentOffset.left += parseFloat($(offsetParent[0]).css("border-left-width")) || 0

第四步

// 相減即結(jié)果
return {
  top: offset.top - parentOffset.top,
  left: offset.left - parentOffset.left
}

整體思路還是用當(dāng)前元素相對(duì)于文檔的位置減去第一個(gè)定位祖先元素相對(duì)于文檔的位置,但有兩點(diǎn)需要注意的是position這個(gè)api要計(jì)算出來的值,不應(yīng)該包括父元素的border長(zhǎng)度以及子元素的margin空間長(zhǎng)度。所以才會(huì)有第二和第三步。

scrollLeft
獲取或設(shè)置頁面上的滾動(dòng)元素或者整個(gè)窗口向右滾動(dòng)的滾動(dòng)距離。
scrollLeft: function (value) {
  if (!this.length) return
  var hasScrollLeft = "scrollLeft" in this[0]
  if (value === undefined) return hasScrollLeft ? this[0].scrollLeft : this[0].pageXOffset
  return this.each(hasScrollLeft ?
    function () { this.scrollLeft = value } :
    function () { this.scrollTo(value, this.scrollY) })
}

首先判斷當(dāng)前選中的元素是否支持scrollLeft特性。

如果value沒有傳進(jìn)來,又支持hasScrollLeft特性,就返回第一個(gè)元素的hasScrollLeft值,不支持的話返回第一個(gè)元素的pageXOffset值。

pageXOffset是scrollX的別名,而其代表的含義是返回文檔/頁面水平方向滾動(dòng)的像素值

傳進(jìn)來了value就是設(shè)置操作了,支持scrollLeft屬性,就直接設(shè)置其值即可,反之需要用到scrollTo,當(dāng)然設(shè)置水平方向的時(shí)候,垂直方向還是要和之前的保持一致,所以傳入了scrollY作為

scrollTop
獲取或設(shè)置頁面上的滾動(dòng)元素或者整個(gè)窗口向下滾動(dòng)的距離。
scrollTop: function(value) {
  if (!this.length) return
  var hasScrollTop = "scrollTop" in this[0]
  if (value === undefined) return hasScrollTop ? this[0].scrollTop : this[0].pageYOffset
  return this.each(hasScrollTop ?
    function() { this.scrollTop = value } :
    function() { this.scrollTo(this.scrollX, value) })
},

可以看出基本原理和模式與scrollLeft一致,就不再一一解析。

結(jié)尾
以上就是Zepto中與"偏移"相關(guān)的幾個(gè)api的解析,歡迎指出其中的問題和有錯(cuò)誤的地方。
參考

讀Zepto源碼之屬性操作

scrollTo

scrollLeft

pageXOffset

...

文章記錄

ie模塊

Zepto源碼分析之ie模塊(2017-11-03)

data模塊

Zepto中數(shù)據(jù)緩存原理與實(shí)現(xiàn)(2017-10-03)

form模塊

zepto源碼分析之form模塊(2017-10-01)

zepto模塊

這些Zepto中實(shí)用的方法集(2017-08-26)

Zepto核心模塊之工具方法拾遺 (2017-08-30)

看zepto如何實(shí)現(xiàn)增刪改查DOM (2017-10-2)

Zepto這樣操作元素屬性(2017-11-13)

向Zepto學(xué)習(xí)關(guān)于"偏移"的那些事(2017-12-10)

event模塊

mouseenter與mouseover為何這般糾纏不清?(2017-06-05)

向zepto.js學(xué)習(xí)如何手動(dòng)觸發(fā)DOM事件(2017-06-07)

誰說你只是"會(huì)用"jQuery?(2017-06-08)

ajax模塊

原來你是這樣的jsonp(原理與具體實(shí)現(xiàn)細(xì)節(jié))(2017-06-11)

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

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

相關(guān)文章

  • 關(guān)于Javascript中"use strict"那些

    摘要:作用范圍這樣都會(huì)應(yīng)用上模式。如果你僅想在一個(gè)函數(shù)中使用的特性檢查對(duì)象中的重復(fù)鍵這段代碼會(huì)拋出一個(gè)錯(cuò)誤因?yàn)槌霈F(xiàn)了兩次。未聲明變量在模式下,給未聲明的變量賦值會(huì)拋出的警告。重復(fù)的參數(shù)注意出現(xiàn)了兩次,因此會(huì)拋出一個(gè)錯(cuò)誤。 use strict作用范圍 // file.js use strict function doStuff(){ // use strict is enabled ...

    icyfire 評(píng)論0 收藏0
  • 點(diǎn)透問題及解決

    摘要:?jiǎn)栴}封裝的事件由和實(shí)現(xiàn),事件在端無效使用中事件在中該版本的點(diǎn)透問題已經(jīng)解決可以放心使用,但是端仍然無效。一、問題描述實(shí)際學(xué)習(xí)與工作中可能會(huì)有這樣的需求:在移動(dòng)web中給有重疊的兩個(gè)元素都添加了點(diǎn)擊事件,當(dāng)觸發(fā)上方的元素的時(shí)候同時(shí)也會(huì)透過該元素觸發(fā)下面的元素。這就是點(diǎn)透,然而這并不是我想要的效果。二、例子下面通過多種方式來模擬感受點(diǎn)透:<divclass="tap"&...

    番茄西紅柿 評(píng)論0 收藏0
  • Python標(biāo)準(zhǔn)庫(kù)time使用方式詳解

      小編寫這篇文章的主要目的,是給大家進(jìn)行一個(gè)解答,解答關(guān)于標(biāo)準(zhǔn)庫(kù)times的使用方式一些,具體的操作,下面就給大家進(jìn)行一個(gè)解答。  1、time庫(kù)  時(shí)間戳(timestamp)的方式:通常來說,時(shí)間戳表示的是從1970年1月1日00:00:00開始按秒計(jì)算的偏移量  結(jié)構(gòu)化時(shí)間(struct_time)方式:struct_time元組共有9個(gè)元素  格式化的時(shí)間字符串(format_strin...

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

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

0條評(píng)論

hzx

|高級(jí)講師

TA的文章

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