/*
|
DOM 操作 API
|
*/
|
|
// 根据 html 代码片段创建 dom 对象
|
function createElemByHTML(html) {
|
let div
|
div = document.createElement('div')
|
div.innerHTML = html
|
return div.children
|
}
|
|
// 是否是 DOM List
|
function isDOMList(selector) {
|
if (!selector) {
|
return false
|
}
|
if (selector instanceof HTMLCollection || selector instanceof NodeList) {
|
return true
|
}
|
return false
|
}
|
|
// 封装 document.querySelectorAll
|
function querySelectorAll(selector) {
|
const result = document.querySelectorAll(selector)
|
if (isDOMList(result)) {
|
return result
|
} else {
|
return [result]
|
}
|
}
|
|
// 记录所有的事件绑定
|
const eventList = []
|
|
// 创建构造函数
|
function DomElement(selector) {
|
if (!selector) {
|
return
|
}
|
|
// selector 本来就是 DomElement 对象,直接返回
|
if (selector instanceof DomElement) {
|
return selector
|
}
|
|
this.selector = selector
|
const nodeType = selector.nodeType
|
|
// 根据 selector 得出的结果(如 DOM,DOM List)
|
let selectorResult = []
|
if (nodeType === 9) {
|
// document 节点
|
selectorResult = [selector]
|
} else if (nodeType === 1) {
|
// 单个 DOM 节点
|
selectorResult = [selector]
|
} else if (isDOMList(selector) || selector instanceof Array) {
|
// DOM List 或者数组
|
selectorResult = selector
|
} else if (typeof selector === 'string') {
|
// 字符串
|
selector = selector.replace('/\n/mg', '').trim()
|
if (selector.indexOf('<') === 0) {
|
// 如 <div>
|
selectorResult = createElemByHTML(selector)
|
} else {
|
// 如 #id .class
|
selectorResult = querySelectorAll(selector)
|
}
|
}
|
|
const length = selectorResult.length
|
if (!length) {
|
// 空数组
|
return this
|
}
|
|
// 加入 DOM 节点
|
let i
|
for (i = 0; i < length; i++) {
|
this[i] = selectorResult[i]
|
}
|
this.length = length
|
}
|
|
// 修改原型
|
DomElement.prototype = {
|
constructor: DomElement,
|
|
// 类数组,forEach
|
forEach: function (fn) {
|
let i
|
for (i = 0; i < this.length; i++) {
|
const elem = this[i]
|
const result = fn.call(elem, elem, i)
|
if (result === false) {
|
break
|
}
|
}
|
return this
|
},
|
|
// clone
|
clone: function (deep) {
|
const cloneList = []
|
this.forEach(elem => {
|
cloneList.push(elem.cloneNode(!!deep))
|
})
|
return $(cloneList)
|
},
|
|
// 获取第几个元素
|
get: function (index) {
|
const length = this.length
|
if (index >= length) {
|
index = index % length
|
}
|
return $(this[index])
|
},
|
|
// 第一个
|
first: function () {
|
return this.get(0)
|
},
|
|
// 最后一个
|
last: function () {
|
const length = this.length
|
return this.get(length - 1)
|
},
|
|
// 绑定事件
|
on: function (type, selector, fn) {
|
// selector 不为空,证明绑定事件要加代理
|
if (!fn) {
|
fn = selector
|
selector = null
|
}
|
|
// type 是否有多个
|
let types = []
|
types = type.split(/\s+/)
|
|
return this.forEach(elem => {
|
types.forEach(type => {
|
if (!type) {
|
return
|
}
|
|
// 记录下,方便后面解绑
|
eventList.push({
|
elem: elem,
|
type: type,
|
fn: fn
|
})
|
|
if (!selector) {
|
// 无代理
|
elem.addEventListener(type, fn)
|
return
|
}
|
|
// 有代理
|
elem.addEventListener(type, e => {
|
const target = e.target
|
if (target.matches(selector)) {
|
fn.call(target, e)
|
}
|
})
|
})
|
})
|
},
|
|
// 取消事件绑定
|
off: function (type, fn) {
|
return this.forEach(elem => {
|
elem.removeEventListener(type, fn)
|
})
|
},
|
|
// 获取/设置 属性
|
attr: function (key, val) {
|
if (val == null) {
|
// 获取值
|
return this[0].getAttribute(key)
|
} else {
|
// 设置值
|
return this.forEach(elem => {
|
elem.setAttribute(key, val)
|
})
|
}
|
},
|
|
// 添加 class
|
addClass: function(className) {
|
if (!className) {
|
return this
|
}
|
return this.forEach(elem => {
|
let arr
|
if (elem.className) {
|
// 解析当前 className 转换为数组
|
arr = elem.className.split(/\s/)
|
arr = arr.filter(item => {
|
return !!item.trim()
|
})
|
// 添加 class
|
if (arr.indexOf(className) < 0) {
|
arr.push(className)
|
}
|
// 修改 elem.class
|
elem.className = arr.join(' ')
|
} else {
|
elem.className = className
|
}
|
})
|
},
|
|
// 删除 class
|
removeClass: function (className) {
|
if (!className) {
|
return this
|
}
|
return this.forEach(elem => {
|
let arr
|
if (elem.className) {
|
// 解析当前 className 转换为数组
|
arr = elem.className.split(/\s/)
|
arr = arr.filter(item => {
|
item = item.trim()
|
// 删除 class
|
if (!item || item === className) {
|
return false
|
}
|
return true
|
})
|
// 修改 elem.class
|
elem.className = arr.join(' ')
|
}
|
})
|
},
|
|
// 修改 css
|
css: function (key, val) {
|
const currentStyle = `${key}:${val};`
|
return this.forEach(elem => {
|
const style = (elem.getAttribute('style') || '').trim()
|
let styleArr, resultArr = []
|
if (style) {
|
// 将 style 按照 ; 拆分为数组
|
styleArr = style.split(';')
|
styleArr.forEach(item => {
|
// 对每项样式,按照 : 拆分为 key 和 value
|
let arr = item.split(':').map(i => {
|
return i.trim()
|
})
|
if (arr.length === 2) {
|
resultArr.push(arr[0] + ':' + arr[1])
|
}
|
})
|
// 替换或者新增
|
resultArr = resultArr.map(item => {
|
if (item.indexOf(key) === 0) {
|
return currentStyle
|
} else {
|
return item
|
}
|
})
|
if (resultArr.indexOf(currentStyle) < 0) {
|
resultArr.push(currentStyle)
|
}
|
// 结果
|
elem.setAttribute('style', resultArr.join('; '))
|
} else {
|
// style 无值
|
elem.setAttribute('style', currentStyle)
|
}
|
})
|
},
|
|
// 显示
|
show: function () {
|
return this.css('display', 'block')
|
},
|
|
// 隐藏
|
hide: function () {
|
return this.css('display', 'none')
|
},
|
|
// 获取子节点
|
children: function () {
|
const elem = this[0]
|
if (!elem) {
|
return null
|
}
|
|
return $(elem.children)
|
},
|
|
// 获取子节点(包括文本节点)
|
childNodes: function () {
|
const elem = this[0]
|
if (!elem) {
|
return null
|
}
|
|
return $(elem.childNodes)
|
},
|
|
// 增加子节点
|
append: function($children) {
|
return this.forEach(elem => {
|
$children.forEach(child => {
|
elem.appendChild(child)
|
})
|
})
|
},
|
|
// 移除当前节点
|
remove: function () {
|
return this.forEach(elem => {
|
if (elem.remove) {
|
elem.remove()
|
} else {
|
const parent = elem.parentElement
|
parent && parent.removeChild(elem)
|
}
|
})
|
},
|
|
// 是否包含某个子节点
|
isContain: function ($child) {
|
const elem = this[0]
|
const child = $child[0]
|
return elem.contains(child)
|
},
|
|
// 尺寸数据
|
getSizeData: function () {
|
const elem = this[0]
|
return elem.getBoundingClientRect() // 可得到 bottom height left right top width 的数据
|
},
|
|
// 封装 nodeName
|
getNodeName: function () {
|
const elem = this[0]
|
return elem.nodeName
|
},
|
|
// 从当前元素查找
|
find: function (selector) {
|
const elem = this[0]
|
return $(elem.querySelectorAll(selector))
|
},
|
|
// 获取当前元素的 text
|
text: function (val) {
|
if (!val) {
|
// 获取 text
|
const elem = this[0]
|
return elem.innerHTML.replace(/<.*?>/g, () => '')
|
} else {
|
// 设置 text
|
return this.forEach(elem => {
|
elem.innerHTML = val
|
})
|
}
|
},
|
|
// 获取 html
|
html: function (value) {
|
const elem = this[0]
|
if (value == null) {
|
return elem.innerHTML
|
} else {
|
elem.innerHTML = value
|
return this
|
}
|
},
|
|
// 获取 value
|
val: function () {
|
const elem = this[0]
|
return elem.value.trim()
|
},
|
|
// focus
|
focus: function () {
|
return this.forEach(elem => {
|
elem.focus()
|
})
|
},
|
|
// parent
|
parent: function () {
|
const elem = this[0]
|
return $(elem.parentElement)
|
},
|
|
// parentUntil 找到符合 selector 的父节点
|
parentUntil: function (selector, _currentElem) {
|
const results = document.querySelectorAll(selector)
|
const length = results.length
|
if (!length) {
|
// 传入的 selector 无效
|
return null
|
}
|
|
const elem = _currentElem || this[0]
|
if (elem.nodeName === 'BODY') {
|
return null
|
}
|
|
const parent = elem.parentElement
|
let i
|
for (i = 0; i < length; i++) {
|
if (parent === results[i]) {
|
// 找到,并返回
|
return $(parent)
|
}
|
}
|
|
// 继续查找
|
return this.parentUntil(selector, parent)
|
},
|
|
// 判断两个 elem 是否相等
|
equal: function ($elem) {
|
if ($elem.nodeType === 1) {
|
return this[0] === $elem
|
} else {
|
return this[0] === $elem[0]
|
}
|
},
|
|
// 将该元素插入到某个元素前面
|
insertBefore: function (selector) {
|
const $referenceNode = $(selector)
|
const referenceNode = $referenceNode[0]
|
if (!referenceNode) {
|
return this
|
}
|
return this.forEach(elem => {
|
const parent = referenceNode.parentNode
|
parent.insertBefore(elem, referenceNode)
|
})
|
},
|
|
// 将该元素插入到某个元素后面
|
insertAfter: function (selector) {
|
const $referenceNode = $(selector)
|
const referenceNode = $referenceNode[0]
|
if (!referenceNode) {
|
return this
|
}
|
return this.forEach(elem => {
|
const parent = referenceNode.parentNode
|
if (parent.lastChild === referenceNode) {
|
// 最后一个元素
|
parent.appendChild(elem)
|
} else {
|
// 不是最后一个元素
|
parent.insertBefore(elem, referenceNode.nextSibling)
|
}
|
})
|
}
|
}
|
|
// new 一个对象
|
function $(selector) {
|
return new DomElement(selector)
|
}
|
|
// 解绑所有事件,用于销毁编辑器
|
$.offAll = function () {
|
eventList.forEach(item => {
|
const elem = item.elem
|
const type = item.type
|
const fn = item.fn
|
// 解绑
|
elem.removeEventListener(type, fn)
|
})
|
}
|
|
export default $
|