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

資訊專欄INFORMATION COLUMN

原生js系列之DOM工廠模式

junbaor / 3254人閱讀

摘要:于是在不斷的摸索和思考中,想出了工廠模式這個概念,咱們可以這么理解工廠就是取快遞的地方,不管是從哪里發來的貨品,統一送到這里,然后再由特定的人群來取。

寫在前面

如今,在項目中使用React、Vue等框架作為技術棧已成為一種常態,在享受帶來便利性的同時,也許我們漸漸地遺忘原生js的寫法。

現在,是時候回歸本源,響應原始的召喚了。本文將一步一步帶領大家封裝一套屬于自己的DOM操作庫,我將其命名為qnode。

功能特性

qnode吸收了jquery優雅的鏈式寫法,并且融入了我個人的一些經驗和思考:

自定義DOM工廠模式(緩存節點、跨文件操作)

快速優雅地創建新的DOM節點

CSS3樣式前綴自動識別和添加

大家可能會比較疑惑,什么是DOM工廠模式?

實際上是這樣,有一些需要共享的節點、數據和方法,它在某個文件中定義,在另一個文件中調用。我最初的方式是在文件中暴露(export)出這些東西,但是后來發現文件多了,這種方式很難維護,而且需要引入很多的文件,非常麻煩。

于是在不斷的摸索和思考中,想出了DOM工廠模式這個概念,咱們可以這么理解:DOM工廠就是取快遞的地方,不管是從哪里發來的貨品,統一送到這里,然后再由特定的人群來取。

當然,未來還有很長的路要走,我會不斷地探索和改進,融入更多的想法和改進。

目錄結構
qnode
├── QNode.js
├── README.md
├── api.js
├── core
│?? ├── attr.js
│?? ├── find.js
│?? ├── index.js
│?? ├── klass.js
│?? ├── listener.js
│?? ├── node.js
│?? └── style.js
├── index.js
├── q.js
└── tools.js

q.js是DOM操作的集合,融合了所有core的方法

QNode.js是工廠模式,提供節點、數據以及方法的緩存和獲取

core目錄下的文件是DOM操作的具體方法

編寫代碼

core/attr.js:內容屬性操作

import { isDef } from "../tools"

export function text (value) {
  if (!isDef(value)) {
    return this.node.textContent
  }

  this.node.textContent = value
  return this
}

export function html (value) {
  if (!isDef(value)) {
    return this.node.innerHTML
  }

  this.node.innerHTML = value
  return this
}

export function value (val) {
  if (!isDef(val)) {
    return this.node.value
  }

  this.node.value = val
  return this
}

export function attr (name, value) {
  if (!isDef(value)) {
    return this.node.getAttribute(name)
  }

  if (value === true) {
    this.node.setAttribute(name, "")
  } else if (value === false || value === null) {
    this.node.removeAttribute(name)
  } else {
    this.node.setAttribute(name, value)
  }
  return this
}

core/find.js:節點信息獲取

export function tagName () {
  return this.node.tagName
}

export function current () {
  return this.node
}

export function parent () {
  return this.node.parentNode
}

export function next () {
  return this.node.nextSibling
}

export function prev () {
  return this.node.previousSibling
}

export function first () {
  return this.node.firstChild
}

export function last () {
  return this.node.lastChild
}

export function find (selector) {
  return this.node.querySelector(selector)
}

core/klass.js:class樣式操作

import { isArray } from "../tools"

export function addClass (cls) {
  let classList = this.node.classList
  let classes = isArray(cls) ? cls : [].slice.call(arguments, 0)

  classList.add.apply(classList, classes.filter(c => c).join(" ").split(" "))
  return this
}

export function removeClass (cls) {
  let classList = this.node.classList
  let classes = isArray(cls) ? cls : [].slice.call(arguments, 0)

  classList.remove.apply(classList, classes.filter(c => c).join(" ").split(" "))
  return this
}

export function hasClass (cls) {
  let classList = this.node.classList

  // 若有空格,則必須滿足所有class才返回true
  if (cls.indexOf(" ") !== -1) {
    return cls.split(" ").every(c => classList.contains(c))
  }

  return classList.contains(cls)
}

core/listener.js:事件監聽處理

export function on (type, fn, useCapture = false) {
  this.node.addEventListener(type, fn, useCapture)
  return this
}

export function off (type, fn) {
  this.node.removeEventListener(type, fn)
  return this
}

core/node.js:DOM操作

import { createFragment } from "../api"
import { getRealNode, isArray } from "../tools"

export function append (child) {
  let realNode = null

  if (isArray(child)) {
    let fragment = createFragment()
    child.forEach(c => {
      realNode = getRealNode(c)
      if (realNode) {
        fragment.appendChild(realNode)
      }
    })
    this.node.appendChild(fragment)
  } else {
    realNode = getRealNode(child)
    if (realNode) {
      this.node.appendChild(realNode)
    }
  }
  return this
}

export function appendTo (parent) {
  parent = getRealNode(parent)

  if (parent) {
    parent.appendChild(this.node)
  }
  return this
}

export function prepend (child, reference) {
  let realNode = null
  let realReference = getRealNode(reference) || this.node.firstChild

  if (isArray(child)) {
    let fragment = createFragment()
    child.forEach(c => {
      realNode = getRealNode(c)
      if (realNode) {
        fragment.appendChild(realNode)
      }
    })
    this.node.insertBefore(fragment, realReference)
  } else {
    realNode = getRealNode(child)
    if (realNode) {
      this.node.insertBefore(realNode, realReference)
    }
  }
  return this
}

export function prependTo (parent, reference) {
  parent = getRealNode(parent)

  if (parent) {
    parent.insertBefore(this.node, getRealNode(reference) || parent.firstChild)
  }
  return this
}

export function remove (child) {
  // 沒有要移除的子節點則移除本身
  if (!child) {
    if (this.node.parentNode) {
      this.node.parentNode.removeChild(this.node)
    }
  } else {
    let realNode = null

    if (isArray(child)) {
      child.forEach(c => {
        realNode = getRealNode(c)
        if (realNode) {
          this.node.removeChild(realNode)
        }
      })
    } else {
      realNode = getRealNode(child)
      if (realNode) {
        this.node.removeChild(realNode)
      }
    }
  }
  return this
}

core/style.js:內聯樣式操作

import { isDef, isObject } from "../tools"

const htmlStyle = document.documentElement.style
const prefixes = ["webkit", "moz", "ms", "o"]
const prefixLen = prefixes.length

function getRealStyleName (name) {
  if (name in htmlStyle) {
    return name
  }

  // 首字母大寫
  let upperName = name[0].toUpperCase() + name.substr(1)

  // 前綴判斷
  for (let i = 0; i < prefixLen; i++) {
    let realName = prefixes[i] + upperName

    if (realName in htmlStyle) {
      return realName
    }
  }

  // 都不支持則返回原值
  return name
}

export function css (name) {
  if (!this.computedStyle) {
    this.computedStyle = window.getComputedStyle(this.node)
  }

  if (!isDef(name)) {
    return this.computedStyle
  }

  return this.computedStyle[name]
}

export function style (a, b) {
  if (isObject(a)) {
    Object.keys(a).forEach(name => {
      name = getRealStyleName(name)
      this.node.style[name] = a[name]
    })
  } else {
    a = getRealStyleName(a)

    if (!isDef(b)) {
      return this.node.style[a]
    }

    this.node.style[a] = b
  }

  return this
}

export function show () {
  if (this.node.style.display === "none") {
    this.node.style.display = ""
  }
  return this
}

export function hide () {
  this.node.style.display = "none"
  return this
}

export function width () {
  return this.node.clientWidth
}

export function height () {
  return this.node.clientHeight
}

core/index.js:所有操作集合

import * as attr from "./attr"
import * as find from "./find"
import * as klass from "./klass"
import * as listener from "./listener"
import * as node from "./node"
import * as style from "./style"

export default Object.assign({},
  attr,
  find,
  klass,
  listener,
  node,
  style
)

api.js:DOM API

// 創建dom節點
export function createElement (tagName) {
  return document.createElement(tagName)
}

// 創建dom節點片段
export function createFragment () {
  return document.createDocumentFragment()
}

tools.js:工具方法

import { createElement } from "./api"

export const Q_TYPE = (typeof Symbol === "function" && Symbol("q")) || 0x89bc
export const QNODE_TYPE = (typeof Symbol === "function" && Symbol("QNode")) || 0x7b96

// 占位node節點
export const emptyNode = createElement("div")

export function isQ (ele) {
  return ele && ele.node && ele.__type__ === Q_TYPE
}

/**
 * 判斷值是否定義
 * @param {any} t
 * @returns {boolean}
 */
export function isDef (t) {
  return typeof t !== "undefined"
}

/**
 * 判斷是否為字符串
 * @param {any} t
 * @returns {boolean}
 */
export function isString (t) {
  return typeof t === "string"
}

/**
 * 是否為對象
 * @param {any} t
 * @param {boolean} [includeArray=false] 是否包含數組
 * @returns {boolean}
 */
export function isObject (t) {
  return t && typeof t === "object"
}

/**
 * 判斷是否為數組
 * @param {any} t
 * @returns {boolean}
 */
export function isArray (t) {
  if (Array.isArray) {
    return Array.isArray(t)
  }
  return Object.prototype.toString.call(t) === "[object Array]"
}

// 判斷是否為dom元素
export function isElement (node) {
  if (isObject(HTMLElement)) {
    return node instanceof HTMLElement
  }

  return node && node.nodeType === 1 && isString(node.nodeName)
}

export function getRealNode (ele) {
  if (isElement(ele)) {
    return ele
  } else if (isQ(ele)) {
    return ele.node
  }
  return null
}

q.js

import { createElement } from "./api"
import { Q_TYPE, isElement, isString, emptyNode } from "./tools"
import core from "./core"

class Q {
  constructor (selector) {
    let node

    if (isElement(selector)) {
      node = selector
    } else if (isString(selector)) {
      if (selector[0] === "$") {
        node = createElement(selector.substring(1))
      } else {
        node = document.querySelector(selector)
      }
    }

    // node不存在,則創建一個占位node,避免操作dom報錯
    this.node = node || emptyNode
    this.__type__ = Q_TYPE
  }
}

// 集合
Object.assign(Q.prototype, core)

export default function q (selector) {
  return new Q(selector)
}

QNode.js

import { QNODE_TYPE, isQ, isArray } from "./tools"
import q from "./q"

export default class QNode {
  constructor () {
    this.__type__ = QNODE_TYPE
    this.qNodes = {}
    this.store = {}
    this.methods = {}
  }

  q (selector) {
    return q(selector)
  }

  getNode (name) {
    return this.qNodes[name]
  }

  setNode (name, node) {
    if (isArray(node)) {
      this.qNodes[name] = node.map(n => isQ(n) ? n : q(n))
    } else {
      this.qNodes[name] = isQ(node) ? node : q(node)
    }
    return this.qNodes[name]
  }

  getStore (name) {
    return this.store[name]
  }

  setStore (name, value) {
    this.store[name] = value
    return value
  }

  getMethod (name) {
    return this.methods[name]
  }

  execMethod (name) {
    let fn = this.methods[name]
    return fn && fn.apply(this, [].slice.call(arguments, 1))
  }

  setMethod (name, fn) {
    let thisFn = fn.bind(this)
    this.methods[name] = thisFn
    return thisFn
  }
}

index.js

import q from "./q"
import QNode from "./QNode"

export {
  q,
  QNode
}

export default {
  q,
  QNode
}

到這里為止,所有代碼已經編寫完成了。

API Reference q(獲取|創建節點)

參數:

#id 根據id獲取節點

.class 根據class獲取節點

tagName 根據標簽獲取節點

$tagName 創建新的節點

備注:如果有多個節點,則只獲取第一個

方法:

attr

text: str 【設置文本內容,若無參數則獲取文本內容】

html: str 【設置html,若無參數則獲取html】

value: val 【設置表單值,若無參數則獲取表單值】

attr: name, value 【設置name屬性的值,若value無參數則獲取name的值】

find

tagName 【獲取節點名稱】

current 【獲取節點本身】

parent 【獲取父節點】

next 【獲取后一個節點】

prev 【獲取上一個節點】

first 【獲取第一個子節點】

last 【獲取最后一個子節點】

find: #id | .class | tagName 【找子節點】

class

addClass: str | arr | a, b, ... 【添加樣式class】

removeClass: str | arr | a, b, ... 【移除樣式class】

hasClass: str 【是否含有樣式class】

listener

on: type, fn, useCapture=false 【添加事件監聽】

off: type, fn 【移除事件監聽】

node

append: node | nodeList 【填充子節點到最后】

appendTo: parent 【填充到父節點中最后】

prepend: node | nodeList, reference 【填充子節點到最前或指定節點前】

prependTo: parent, reference【填充到父節點中最前或指定節點前】

remove: child 【移除子節點,若無參數則移除自身】

style

css: name 【獲取css文件中定義的樣式】

style: (name, value) | object 【1.設置或獲取內聯樣式;2.設置一組樣式】

show 【顯示節點】

hide 【隱藏節點】

width 【獲取節點寬度】

height 【獲取節點高度】

QNode(節點倉庫,包括數據和方法)

方法:

q: 同上述q

getNode: name 【獲取節點】

setNode: name, node 【設置節點,返回節點】

getStore: name 【獲取數據】

setStore: name, value 【設置數據,返回數據】

getMethod: name 【獲取方法】

setMethod: name, fn 【設置方法,返回方法,this綁定到qnode】

execMethod: name 【執行方法,name后面可以傳入方法需要的參數,this為qnode】

結語

本文到這里就要結束了,讀者對文中的代碼感興趣的話,建議自己動手試試,在編程這塊兒,實踐才能出真知。

寫完之后,是不是躍躍欲試呢?下一篇文章我將基于本文封裝的DOM庫來開發無限循環輪播圖,詳細請看下文:原生js系列之無限循環輪播圖。

附:本文源碼

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/112874.html

相關文章

  • 原生js系列DOM工廠模式

    摘要:于是在不斷的摸索和思考中,想出了工廠模式這個概念,咱們可以這么理解工廠就是取快遞的地方,不管是從哪里發來的貨品,統一送到這里,然后再由特定的人群來取。 寫在前面 如今,在項目中使用React、Vue等框架作為技術棧已成為一種常態,在享受帶來便利性的同時,也許我們漸漸地遺忘原生js的寫法。 現在,是時候回歸本源,響應原始的召喚了。本文將一步一步帶領大家封裝一套屬于自己的DOM操作庫,我將...

    sunny5541 評論0 收藏0
  • javascript知識點

    摘要:模塊化是隨著前端技術的發展,前端代碼爆炸式增長后,工程化所采取的必然措施。目前模塊化的思想分為和。特別指出,事件不等同于異步,回調也不等同于異步。將會討論安全的類型檢測惰性載入函數凍結對象定時器等話題。 Vue.js 前后端同構方案之準備篇——代碼優化 目前 Vue.js 的火爆不亞于當初的 React,本人對寫代碼有潔癖,代碼也是藝術。此篇是準備篇,工欲善其事,必先利其器。我們先在代...

    Karrdy 評論0 收藏0
  • JS或Jquery

    摘要:大潮來襲前端開發能做些什么去年谷歌和火狐針對提出了的標準,顧名思義,即的體驗方式,我們可以戴著頭顯享受沉浸式的網頁,新的標準讓我們可以使用語言來開發。 VR 大潮來襲 --- 前端開發能做些什么 去年谷歌和火狐針對 WebVR 提出了 WebVR API 的標準,顧名思義,WebVR 即 web + VR 的體驗方式,我們可以戴著頭顯享受沉浸式的網頁,新的 API 標準讓我們可以使用 ...

    CatalpaFlat 評論0 收藏0
  • 原生js系列無限循環輪播組件

    摘要:沒有看過上一篇文章的話,可以在這里找到原生系列之工廠模式。那么這篇文章,我們將基于上述的,從頭開始寫一個無限循環輪播圖的組件。附無限循環輪播圖示例本文源碼 前情回顧 在上一篇文章中,我們封裝了一個DOM庫(qnode),為了讓大家直觀地感受到其方便友好的自定義工廠模式,于是給大家帶來了這篇文章。 沒有看過上一篇文章的話,可以在這里找到:原生js系列之DOM工廠模式。 那么這篇文章,我們...

    ad6623 評論0 收藏0

發表評論

0條評論

junbaor

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<