/*
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) {
// 如
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 $