這篇依然是跟 dom 相關(guān)的方法,側(cè)重點是操作樣式的方法。

讀Zepto源碼系列文章已經(jīng)放到了github上,歡迎star: reading-zepto

源碼版本

本文閱讀的源碼為 zepto1.2.0

內(nèi)部方法

classRE

classCache = {}function classRE(name) {
  return name in classCache ?
    classCache[name] : (classCache[name] = new RegExp('(^|\\s)' + name + '(\\s|$)'))}

這個函數(shù)是用來返回一個正則表達式,這個正則表達式是用來匹配元素的 class 名的,匹配的是如 className1 className2 className3 這樣的字符串。

calssCache 初始化時是一個空對象,用 name 用為 key ,如果正則已經(jīng)生成過,則直接從 classCache中取出對應(yīng)的正則表達式。

否則,生成一個正則表達式,存儲到 classCache 中,并返回。

來看一下這個生成的正則,'(^|\\s)' 匹配的是開頭或者空白(包括空格、換行、tab縮進等),然后連接指定的 name ,再緊跟著空白或者結(jié)束。

maybeAddPx

cssNumber = { 'column-count': 1, 'columns': 1, 'font-weight': 1, 'line-height': 1, 'opacity': 1, 'z-index': 1, 'zoom': 1 }function maybeAddPx(name, value) {
  return (typeof value == "number" && !cssNumber[dasherize(name)]) ? value + "px" : value}

在給屬性設(shè)置值時,猜測所設(shè)置的屬性可能需要帶 px 單位時,自動給值拼接上單位。

cssNumber 是不需要設(shè)置 px 的屬性值,所以這個函數(shù)里首先判斷設(shè)置的值是否為 number 類型,如果是,并且需要設(shè)置的屬性不在 cssNumber 中時,給值拼接上 px 單位。

defaultDisplay

elementDisplay = {}function defaultDisplay(nodeName) {
  var element, display  if (!elementDisplay[nodeName]) {
    element = document.createElement(nodeName)    document.body.appendChild(element)
    display = getComputedStyle(element, '').getPropertyValue("display")    element.parentNode.removeChild(element)
    display == "none" && (display = "block")
    elementDisplay[nodeName] = display  }
  return elementDisplay[nodeName]}

先透露一下,這個方法是給 .show() 用的,show 方法需要將元素顯示出來,但是要顯示的時候能不能直接將 display 設(shè)置成 block 呢?顯然是不行的,來看一下 display 的可能會有那些值:

display: none

display: inline
display: block
display: contents
display: list-item
display: inline-block
display: inline-table
display: table
display: table-cell
display: table-column
display: table-column-group
display: table-footer-group
display: table-header-group
display: table-row
display: table-row-group
display: flex
display: inline-flex
display: grid
display: inline-grid
display: ruby
display: ruby-base
display: ruby-text
display: ruby-base-container
display: ruby-text-container 
display: run-indisplay: inherit
display: initial
display: unset

如果元素原來的 display 值為 table ,調(diào)用 show 后變成 block 了,那頁面的結(jié)構(gòu)可能就亂了。

這個方法就是將元素顯示時默認的 display 值緩存到 elementDisplay,并返回。

函數(shù)用節(jié)點名 nodeName 為 key ,如果該節(jié)點顯示時的 display 值已經(jīng)存在,則直接返回。

element = document.createElement(nodeName)document.body.appendChild(element)

否則,使用節(jié)點名創(chuàng)建一個空元素,并且將元素插入到頁面中

display = getComputedStyle(element, '').getPropertyValue("display")element.parentNode.removeChild(element)

調(diào)用 getComputedStyle 方法,獲取到元素顯示時的 display 值。獲取到值后將所創(chuàng)建的元素刪除。

display == "none" && (display = "block")
elementDisplay[nodeName] = display

如果獲取到的 display 值為 none ,則將顯示時元素的 display 值默認為 block。然后將結(jié)果緩存起來。display 的默認值為 none? Are you kiding me ? 真的有這種元素嗎?還真的有,像 style、 head 和 title 等元素的默認值都是 none 。將 style 和 head 的 display 設(shè)置為 block ,并且將 style 的 contenteditable 屬性設(shè)置為 true ,style 就顯示出來了,直接在頁面上一邊敲樣式,一邊看效果,爽?。?!

關(guān)于元素的 display 默認值,可以看看這篇文章 Default CSS Display Values for Different HTML Elements

funcArg

function funcArg(context, arg, idx, payload) {
  return isFunction(arg) ? arg.call(context, idx, payload) : arg}

這個函數(shù)要注意,本篇和下一篇介紹的絕大多數(shù)方法都會用到這個函數(shù)。

例如本篇將要說到的 addClass 和 removeClass 等方法的參數(shù)可以為固定值或者函數(shù),這些方法的參數(shù)即為形參 arg。

當參數(shù) arg 為函數(shù)時,調(diào)用 arg 的 call 方法,將上下文 context ,當前元素的索引 idx 和原始值 payload 作為參數(shù)傳遞進去,將調(diào)用結(jié)果返回。

如果為固定值,直接返回 arg

className

function className(node, value) {
  var klass = node.className || '',
      svg = klass && klass.baseVal !== undefined

  if (value === undefined) return svg ? klass.baseVal : klass
  svg ? (klass.baseVal = value) : (node.className = value)}

className 包含兩個參數(shù),為元素節(jié)點 node 和需要設(shè)置的樣式名 value。

如果 value 不為 undefined(可以為空,注意判斷條件為 value === undefined,用了全等判斷),則將元素的 className 設(shè)置為給定的值,否則將元素的 className 值返回。

這個函數(shù)對 svg 的元素做了兼容,如果元素的 className 屬性存在,并且 className 屬性存在 baseVal時,為 svg 元素,如果是 svg 元素,取值和賦值都是通過 baseVal 。對 svg 不是很熟,具體見文檔: SVGAnimatedString.baseVal

.css()

css: function(property, value) {
  if (arguments.length < 2) {
    var element = this[0]    if (typeof property == 'string') {
      if (!element) return
      return element.style[camelize(property)] || getComputedStyle(element, '').getPropertyValue(property)        } else if (isArray(property)) {
          if (!element) return
          var props = {}
          var computedStyle = getComputedStyle(element, '')          $.each(property, function(_, prop) {
            props[prop] = (element.style[camelize(prop)] || computedStyle.getPropertyValue(prop))          })          return props        }
  }

  var css = ''
  if (type(property) == 'string') {
    if (!value && value !== 0)      this.each(function() { this.style.removeProperty(dasherize(property)) })      else
        css = dasherize(property) + ":" + maybeAddPx(property, value)        } else {
          for (key in property)            if (!property[key] && property[key] !== 0)              this.each(function() { this.style.removeProperty(dasherize(key)) })              else
                css += dasherize(key) + ':' + maybeAddPx(key, property[key]) + ';'
                }

  return this.each(function() { this.style.cssText += ';' + css })}

css 方法有兩個參數(shù),property 是的 css 樣式名,value 是需要設(shè)置的值,如果不傳遞 value 值則為取值操作,否則為賦值操作。

來看看調(diào)用方式:

css(property)   ? value  // 獲取值css([property1, property2, ...])   ? object // 獲取值css(property, value)   ? self // 設(shè)置值css({ property: value, property2: value2, ... })   ? self // 設(shè)置值

下面這段便是處理獲取值情況的代碼:

if (arguments.length < 2) {
  var element = this[0]  if (typeof property == 'string') {
    if (!element) return
    return element.style[camelize(property)] || getComputedStyle(element, '').getPropertyValue(property)      } else if (isArray(property)) {
        if (!element) return
        var props = {}
        var computedStyle = getComputedStyle(element, '')        $.each(property, function(_, prop) {
          props[prop] = (element.style[camelize(prop)] || computedStyle.getPropertyValue(prop))        })        return props      }}

當為獲取值時,css 方法必定只傳遞了一個參數(shù),所以用 arguments.length < 2 來判斷,用 css 方法來獲取值,獲取的是集合中第一個元素對應(yīng)的樣式值。

if (!element) returnreturn element.style[camelize(property)] || getComputedStyle(element, '').getPropertyValue(property)

當 property 為 string 時,如果元素不存在,直接 return 掉。

如果 style 中存在對應(yīng)的樣式值,則優(yōu)先獲取 style 中的樣式值,否則用 getComputedStyle 獲取計算后的樣式值。

為什么不直接獲取計算后的樣式值呢?因為用 style 獲取的樣式值是原始的字符串,而 getComputedStyle 顧名思義獲取到的是計算后的樣式值,如 style = "transform: translate(10px, 10px)" 用 style.transform獲取到的值為 translate(10px, 10px),而用 getComputedStyle 獲取到的是 matrix(1, 0, 0, 1, 10, 10)。這里用到的 camelize 方法是將屬性 property 轉(zhuǎn)換成駝峰式的寫法,該方法在《讀Zepto源碼之內(nèi)部方法》有過分析。

else if (isArray(property)) {
  if (!element) return
  var props = {}
  var computedStyle = getComputedStyle(element, '')  $.each(property, function(_, prop) {
    props[prop] = (element.style[camelize(prop)] || computedStyle.getPropertyValue(prop))  })  return props}

如果參數(shù) property 為數(shù)組時,表示要獲取一組屬性的值。isArray 方法也在《讀Zepto源碼之內(nèi)部方法》有過分析。

獲取的方法也很簡單,遍歷 property ,獲取 style 上對應(yīng)的樣式值,如果 style 上的值不存在,則通過 getComputedStyle 來獲取,返回的是以樣式名為 key ,value 為對應(yīng)的樣式值的對象。

接下來是給所有元素設(shè)置值的情況:

var css = ''if (type(property) == 'string') {
  if (!value && value !== 0)    this.each(function() { this.style.removeProperty(dasherize(property)) })  else
    css = dasherize(property) + ":" + maybeAddPx(property, value) } else {
    for (key in property)        if (!property[key] && property[key] !== 0)            this.each(function() { this.style.removeProperty(dasherize(key)) })         else
            css += dasherize(key) + ':' + maybeAddPx(key, property[key]) + ';'
         }return this.each(function() { this.style.cssText += ';' + css })

這里定義了個變量 css 來接收需要新值的樣式字符串。

if (type(property) == 'string') {
  if (!value && value !== 0)    this.each(function() { this.style.removeProperty(dasherize(property)) })  else
    css = dasherize(property) + ":" + maybeAddPx(property, value) }

當參數(shù) property 為字符串時

如果 value 不存在并且值不為 0 時(注意,value 為 undefined 時,已經(jīng)在上面處理過了,也即是獲取樣式值),遍歷集合,將對應(yīng)的樣式值從 style 中刪除。

否則,拼接樣式字符串,拼接成如 width:100px 形式的字符串。這里調(diào)用了 maybeAddPx 的方法,自動給需要加 px 的屬性值拼接上了 px 單位。this.css('width', 100) 跟 this.css('width', '100px') 會得到一樣的結(jié)果。

for (key in property)  if (!property[key] && property[key] !== 0)    this.each(function() { this.style.removeProperty(dasherize(key)) })    else
      css += dasherize(key) + ':' + maybeAddPx(key, property[key]) + ';'

當 property 為 key 是樣式名,value 為樣式值的對象時,用 for...in 遍歷對象,接下來的處理邏輯跟 property 為 string 時差不多,在做 css 拼接時,在末尾加了 ;,避免遍歷時,將樣式名和值連接在了一起。

.hide()

hide: function() {
  return this.css("display", "none")},

將集合中所有元素的 display 樣式屬性設(shè)置為 node,就達到了隱藏元素的目的。注意,css 方法中已經(jīng)包含了 each 循環(huán)。

.show()

show: function() {
  return this.each(function() {
    this.style.display == "none" && (this.style.display = '')    if (getComputedStyle(this, '').getPropertyValue("display") == "none")      this.style.display = defaultDisplay(this.nodeName)      })},

hide 方法是直接將 display 設(shè)置為 none 即可,show 可不可以直接將需要顯示的元素的 display 設(shè)置為 block 呢?

這樣在大多數(shù)情況下是可以的,但是碰到像 table 、li 等顯示時 display 默認值不是 block 的元素,強硬將它們的 display 屬性設(shè)置為 block ,可能會更改他們的默認行為。

show 要讓元素真正顯示,要經(jīng)過兩步檢測:

this.style.display == "none" && (this.style.display = '')

如果 style 中的 display 屬性為 none ,先將 style 中的 display 置為 ``。

if (getComputedStyle(this, '').getPropertyValue("display") == "none")  this.style.display = defaultDisplay(this.nodeName)
 })

這樣還未完,內(nèi)聯(lián)樣式的 display 屬性是置為空了,但是如果嵌入樣式或者外部樣式表中設(shè)置了 display 為 none 的樣式,或者本身的 display 默認值就是 none 的元素依然顯示不了。所以還需要用獲取元素的計算樣式,如果為 none ,則將 display 的屬性設(shè)置為元素顯示時的默認值。如 table 元素的 style 中的 display 屬性值會被設(shè)置為 table。

.toggle()

toggle: function(setting) {
  return this.each(function() {
    var el = $(this);
    (setting === undefined ? el.css("display") == "none" : setting) ? el.show(): el.hide()  })},

切換元素的顯示和隱藏狀態(tài),如果元素隱藏,則顯示元素,如果元素顯示,則隱藏元素??梢杂脜?shù) setting 指定 toggle 的行為,如果指定為 true ,則顯示,如果為 false ( setting 不一定為 Boolean),則隱藏。

注意,判斷條件是 setting === undefined ,用了全等,

.toggleClasgs.com/hefty/p/6984688.html