日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

原生js系列之DOM工厂模式

發布時間:2023/12/19 编程问答 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 原生js系列之DOM工厂模式 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

寫在前面

如今,在項目中使用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 = valuereturn this }export function html (value) {if (!isDef(value)) {return this.node.innerHTML}this.node.innerHTML = valuereturn this }export function value (val) {if (!isDef(val)) {return this.node.value}this.node.value = valreturn 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.classListlet 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.classListlet 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才返回trueif (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 = nullif (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 = nulllet realReference = getRealNode(reference) || this.node.firstChildif (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 = nullif (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.lengthfunction 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] + upperNameif (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 nodeif (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 || emptyNodethis.__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_TYPEthis.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] = valuereturn 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] = thisFnreturn 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系列之無限循環輪播圖。

附:本文源碼

總結

以上是生活随笔為你收集整理的原生js系列之DOM工厂模式的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。