- 源码">源码
- 一、“增”的 API
- 二、“删”的 API
- 三、“改”的 API
- 1.
dom.attr(node,'title',?)(用于读写属性) - 2.
dom.text(node,?)(用于读写文本内容) - 3.
dom.html(node,?)(用于读写 HTML 内容) - 4.
dom.style(node,{color:'red'})(用于修改 style) - 5.
dom.class.add(node,'blue')(用于添加 class) - 6.
dom.class.remove(node,'blue')(用于删除 class) - 7.
dom.on(node,'click',fn)(用于添加事件监听) - 8.
dom.off(node,'click',fn)(用于删除事件监听)
- 1.
- 四、“查”的 API
源码
一、“增”的 API
1. dom.create(hi) (用于创建节点)
最简单的一步:

或者直接写到上面的 window.dom 里面
删掉 : 和 function 意思是一样的window.dom = {//create:function(){}create(){}}
1.1.先试着只是添加标签名
dom.js 里已经写好了,那么来尝试着在 main.js 里用一下<br />  <br />原因是:没有在index.html里面引用dom.js<br />注意引入顺序,要先引入 dom.js 否则还是会说 dom 未定义<br />这样显示标签名就添加成功了<br />但是引出了一个问题  用下面的方法来实现
1.2.实现可以直接创建 <div><span>1</span></div>
<br />这种写法有一个bug,里面不能是 td ,会显示undefined ,因为这里的容器是div,里面不允许有td元素
- template — 专门用来容纳任意元素
并且 table 元素不能用children[] 来获取
window.dom = {create(string){const container = document.createElement('template') //容器container.innerHTML = string.trim() //trim() 用来去掉前后空格return container.content.firstChild}}

string 后面不加 trim() 的结果:会选择前面的空格作为字符串显示出来
最后对于最终结果的总结:
td元素不能用children[0] 获取
2. dom.after(node,node2) (用于新增弟弟)
插入到该节点的后面
after(node, node2) {node.parentNode.insertBefore(node2, node.nextSibling)}
- 需要先找到该节点node的下一个节点,然后在node的下一个节点前面添加新节点 node2
- 关于 insertBfore 在 DOM 编程笔记里并没有涉及
3. dom.before(node,node2) (用于新增哥哥)
这个是支持的
before(node, node2) {node.parentNode.insertBefore(node2, node)}
4. dom.append(parent,child) (用于新增儿子)
append(parent,node){parent.appendChild(node)}
5. dom.wrap(node,parent) (用于新增爸爸)
wrap(node,parent){dom.before(node,parent)//先添加新节点到原先节点前面dom.append(parent,node)//再把原先的节点添加为新节点的儿子}
- 写法是dom.before 和 dom.append 的结合
二、“删”的 API
1. dom.remove(node) (用于删除节点)
remove(node){// node.remove() //该方法比较新,有可能会用不了node.parentNode.removeChild(node) //先找到父元素,再从父元素里删除该节点return node}
2. dom.empty(parent) (用于删除后代)
[x] 方法一:直接让元素后代为空,无法获取到删除的节点
empty(node){node.innerHTML = '' //直接设置元素后代为空//上面的方法也可行,但是如果想获取到里面的节点的引用,可用下面的方法(返回移除的节点)}
[x] 方法二:
empty(node){// node.innerHTML = '' //直接设置元素后代为空//上面的方法也可行,但是如果想获取到里面的节点的引用,可用下面的方法(返回移除的节点)const array = []let x = node.firstChildwhile(x){array.push(dom.remove(node.firstChild))x = node.firstChild}return array},
得到如下结果:

[ ] 为什么会有七个节点,这个上节课讲过,用 childNodes.length 会把空格也算一个文本节点
-

—————————————————————可略———————————————————-
方法不可行:删除后代,并获取后代(实践证明这个方法是错误的)
示例:empty(node){//const childNodes = node.childNodesconst {childNodes} = node //获取到所有的子节点,等价于上面那句const array = []for(let i=0;i<childNodes.length;i++){dom.remove(childNodes[i])array.push(childNodes[i])}return array}

说明该方法有问题:打出每次的 childNodes.length
才明白每删除一个i, childNodes.length 就发生了变化,所以这里不能使用for循环
—————————————————————可略———————————————————-
三、“改”的 API
1. dom.attr(node,'title',?) (用于读写属性)
attr - attribute 的缩写
node - 节点
‘title’ - 属性名
? - 是要改成的结果(属性值)
DOM 封装里的内容
attr(node, name, value) { // 重载if (arguments.length === 3) {//如果长度为3,设置节点(实现写)node.setAttribute(name, value)} else if (arguments.length === 2) {//如果长度为2,获取节点(实现读)return node.getAttribute(name)}}
重载:根据参数个数,写不同的代码就是重(chong)载
实际应用:
dom.attr(test,'title','Hi,I am Frank') //实现写属性const title = dom.attr(test,'title') //实现读取该属性console.log(`title:${title}`)
2.
dom.text(node,?)(用于读写文本内容)DOM 封装里的内容
text(node,string){ // 适配if('innerText' in node){node.innerText = string // IE}else{node.textContent = string // firefox | Chrome}}
上面只能写,那么怎么让他能只读又能改写内容呢,同上一种,加重载
text(node, string) {// 适配if (arguments.length === 2) {if ('innerText' in node) {node.innerText = string // IE} else {node.textContent = string // firefox | Chrome}} else if (arguments.length === 1) {if ('innerText' in node) {return node.innerText // IE} else {return node.textContent // firefox | Chrome}}}
实际应用:
dom.text(test,'你好,这是新的内容')

[x] 这里有一个问题,如果test里还有p标签等别的内容,会全部被覆盖掉(这就看具体要怎么使用了)
3.
dom.html(node,?)(用于读写 HTML 内容)DOM 封装里的内容
html(node, string) {if (arguments.length === 2) {node.innerHTML = string} else if (arguments.length === 1) {return node.innerHTML}}
4.
dom.style(node,{color:'red'})(用于修改 style)只写的话下面的代码就可以解决
DOM 封装里的内容
style(node,object){for(let key in object){// key: border / color// node.style.border = ...// node.style.color = ...node.style[key] = object[key]}}
[x] 变量做 key ,就必须把 key 放到 [] 里,不能直接 .key
-

实际应用:
dom.style(test, { border: '1px solid red', color: 'blue' })
那么分读写怎么解决呢
DOM 封装里的内容
style(node, name, value) {if (arguments.length === 3) {// dom.style(div, 'color', 'red')node.style[name] = value} else if (arguments.length === 2) {if (typeof name === 'string') {// dom.style(div,'color')return node.style[name]} else if (name instanceof Object) {// dom.style(div, {color: 'red'})const object = namefor (let key in object) {node.style[key] = object[key]}}}}
[x] typeof — 返回类型
- instanceof — 检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上
分别对应下面三种调用方式
dom.style(test, { border: '1px solid red', color: 'blue' })dom.style(test, 'border')dom.style(test, 'border', '1px solid red')
5.
dom.class.add(node,'blue')(用于添加 class)6.
dom.class.remove(node,'blue')(用于删除 class)DOM 封装里的内容
//添加、删除 class,以及检查该元素是否存在class: {add(node, className) {node.classList.add(className)},remove(node, className) {node.classList.remove(className)},contains(node, className) { // 检查是否有该元素return node.classList.contains(className)},}
[x] 不加 return 返回值就是 undefined
-
classList检查该元素是否存在用contains而不是 has(直接搜 classList mdn) 实际应用:
//添加 classdom.class.add(test, 'red')dom.class.add(test, 'blue')//删除 classdom.class.remove(test, 'blue')//检查该元素是否存在console.log(dom.class.contains(test, 'blue')) // false
7.
dom.on(node,'click',fn)(用于添加事件监听)8.
dom.off(node,'click',fn)(用于删除事件监听)DOM 封装里的内容
//添加、删除事件监听on(node, eventName, fn) {node.addEventListener(eventName, fn)},off(node, eventName, fn) {node.removeEventListener(eventName, fn)}
[x] 要给函数取个名字,不然没法删除掉
实际应用:
//添加、删除事件监听const fn = ()=>{console.log('点击了')} //要给函数取个名字,不然没法删除掉dom.on(test,'click',fn)dom.off(test, 'click',fn)
四、“查”的 API
1.
dom.find('选择器')(用于获取标签或标签们)DOM 封装里的内容
find(selector){return document.querySelectorAll(selector)}
实际应用: ```javascript //获取标签或标签们 const testDiv = dom.find(‘#test’)[0] console.log(testDiv)
// 获取对应的元素(不用下面两个参数的方法也可以获取到对应的元素) const div = dom.find(‘#test>.red’)[0]
- [x] 注意加上后面的 [0] 否则不是获取到数组里的第一个元素,而是NodeList<a name="pI0V2"></a>#### 当给 find 确定个范围应该怎么写呢(用两个参数)- DOM 封装里的内容```javascriptfind(selector,scope){return (scope || document).querySelectorAll(selector)//如果有scope,就在scope里找selector;否则就在document里找}
实际应用:
//获取标签或标签们const testDiv = dom.find('#test')[0]console.log(testDiv)const test2 = dom.find('#test2')[0]console.log(dom.find('.red',test2)[0]) //获取test2里的含有class = 'red' 的元素
2.
dom.parent(node)(用于获取父元素)DOM 封装里的内容
//获取父元素parent(node){return node.parentNode}
实际应用:
//获取父元素console.log(dom.parent(test))
3.
dom.children(node)(用于获取子元素)DOM 封装里的内容
children(node) {return node.children}
4.
dom.siblings(node)(用于获取兄弟姐妹元素)DOM 封装里的内容
children(node) {return Array.from(node.parentNode.children).filter(n=>n!==node)}
[x] 先找到该元素的爸爸,再找他爸爸的子元素
- .filter 是获取数组的通过测试的元素组成的数组,不改变原数组
- 因为获取到的是个NodeList(一个类似数组的对象,也就是伪数组),所以需要先用
Array.from()转成数组 实际应用:
//获取兄弟姐妹元素console.log(dom.siblings(dom.find('#s2')[0]))
[x] 注意后面的 [0] 不能省略
5. dom.next(node) (用于获取弟弟)

结果返回的弟弟是个回车
DOM 封装里的内容
next(node){let x = node.nextSiblingwhile(x && x.nodeType === 3){x = x.nextSibling}//x存在且直到第一个x的节点类型不为3(文本节点),返回xreturn x}
实际应用:
//获取弟弟console.log(dom.next(dom.find('#s2')[0]))
6.
dom.previous(node)(用于获取哥哥)上面的 next => previous 其他完全一样
DOM 封装里的内容
next(node){let x = node.previousSiblingwhile(x && x.nodeType === 3){x = x.previousSibling}//x存在且直到第一个x的节点类型不为3(文本节点),返回xreturn x}
实际应用:
//获取弟弟console.log(dom.previous(dom.find('#s2')[0]))
7.
dom.each(nodes,fn)(用于遍历所有节点)DOM 封装里的内容
each(nodeList,fn){for(let i=0; i<nodeList.length; i++){fn.call(null,nodeList[i])}}
实际应用:
//遍历所有节点const t = dom.find('#travel')[0] //先找到要遍历的节点dom.each(dom.children(t),(n)=>dom.style(n,'color','red'))//找到子节点,并且在每个子节点上加一个style
8.
dom.index(node)(用于获取排行老几)DOM 封装里的内容
index(node) {//node.parentNode.childrenconst list = dom.children(node.parentNode)let ifor (i = 0; i < list.length; i++) {if (list[i] === node) {break}}return i}
[x] 先获取到它的兄弟姐妹,第一种写法容易有问题,所以这里使用了其他的写法
- 遍历 list ,直到 list 等于 node 停止,返回这个i
- 实际应用:
//获取排行老几console.log(dom.index(s2))
length 为1, 排行第二
newDiv 就到前面去了


这个 div 就得到了一个名为title的属性
