DOM 编程
Document Object Model 文档对象模型
JS就是用 document 操作网页
获取任意元素、获取任意标签
有很多API
window.idxxx 或者直接 idxxx
document.getElementById(‘idxxx’)
document.getElementsByTagNmae(‘div’)[0]
document.getElementsByClassName(‘red’)[0]
document.querySelector(‘#idxxx’)
document.querySelectorAll(‘.red’)[0]
最后两个是比较常用的
中间三个是兼容IE 的
获取特点元素
获取 html 元素
document.documentElement
获取 head 元素
document.head
获取 body 元素
document.body
获取窗口 (窗口不是元素)
window
获取所有元素
document.all (是第6个 falsy 值)
获取到的元素都是对象
节点 Node 包括以下几种
MDN 有完整描述, x.nodeType 得到一个数字
1 表示元素 Element, 也叫标签 Tag
3 表示文本 Text
8 表示注释 Comment
9 表示文档Document
11 表示文档片段 DocumentFragment
记住 1 和 3 即可
节点的增删改查
增
创建一个标签节点
1 2 3 4
| let div1 = document.createElement('div') document.createElement('style') document.createElement('script') document.createElement('li')
|
创建一个文本节点
1
| text1 = document.createTextNode('你好')
|
标签里面插入文本
不能用 div1.appendChild(’你好’)
1 2 3
| div1.appendChild(text1) div1.innerText('你好') div1.textContent('你好')
|
插入页面中
创建的标签默认处于 JS 线程中
必须把他插到 head 或者 body 里面,它才会生效
1 2
| document.body.appendChild(div) 已在页面中的元素.appendChild(div)
|
appendChid
1 2 3
| let div = document.createElement('div') test1.appendChild(div) test2.appendChidl(div)
|
最终会出现在test2 中
一个元素不能出现在两个地方,除非复制一份
删
两种方法
1 2
| div1.parentNode.removeChild(div1) div1.remove()
|
如果一个node 被移出页面(DOM树)
还可以再加到页面中
删除后再 div1 = null 即可删除内存中的属性
改
写标准属性
1 2 3 4 5 6
| 改 class: div.className = 'red pink' (全覆盖) 改 class: div.classList.add('red') 改 style: div.style = 'width:100px;color:blue;' 改 style 的一部分 : div.style.width = '150px' JS中大小写代替中线: div.style.backgroundColor = 'withe' 改 data-* 属性: div.dataset.x = 'lucas'
|
读标准属性
都可以用,值可能稍微有些不同,下面的更保险一点
1 2
| div.classList / a.href div.getAttribute('class') / a.getAttribute('href')
|
改事件处理函数
div.onclick 默认为 null
默认点击 div 不会有任何事情发生
但是如果把 div.onclick 改为一个函数 fn
那么点击 div 的时候,浏览器就会调用这个函数
并且是这样调用的 fn.call(div,event)
div 会被当做 this
event 则包含了点击事件的所有内容,如坐标
div.addEventListener
是div.onclick 的升级版
改内容
改文本内容
1 2
| div.innerText = 'xxx' div.textContent = 'xxx'
|
两者几乎没区别
改HTML 内容
1
| div.innerHTML = '<strong>加粗内容</strong>'
|
改标签
1 2
| div.innerHTML = '' div.appendChild(div1)
|
改父级
从原先的地方消失
1
| newParent.appendChild(div)
|
查
查父级
1 2 3
| div.parentNode div.parentElement div.parentNode.parentNode
|
查子级
1 2
| div.childNodes div.children
|
当子级变化时,两者也会实时变化
查同级
1 2
| div.parentNode.childNodes div.parentNode.children
|
查看老大
查看小儿
查看上一个同级
1 2
| div.previousSibling div.previousElementSibling
|
查看下一个同级
1 2
| div.nextSibling div.nextElementSibling
|
遍历一个div的所有元素
1 2 3 4 5 6 7 8 9
| travel = (node,fn)=>{ fn(node) if(node.children){ for(let i = 0;i<node.children.length;i++){ travel(node.children[i],fn) } } } travel(div1,(node)=>console.log(node))
|
跨线程操作
各线程各司其职
JS 引擎不能直接操作页面,只能操作JS
渲染引擎也不能操作JS,只能操作页面
document.body.appendChild(div1)
跨线程通信
当浏览器发现JS 在body 里面加了一个 div1 对象
浏览器就会通知渲染引擎在页面里也新增一个 div 元素
新增的 div 元素所有的属性都照抄 div1 对象
插入新标签的完整过程
在 div1 放入页面之前
对div1 的所有操作都属于 JS线程内的操作
把 div1 放入页面时
浏览器会发现 JS 的想法
就会通知渲染线程在页面中渲染 div1 对应的元素
把 div1 放入页面之后
对 div1 的操作都有可能会触发重新渲染
div1.id = ‘newId’ 可能会重新渲染,也可能不会
div1.title = ‘new’ 也可能会重新渲染,也可能不会
如果连续对 div1 多次操作,浏览器可能会合并成一次操作,也可能不会,除非中间有能强制浏览器进行渲染的操作
属性同步
标准属性
对 div1 的标准属性的修改,会被浏览器同步到页面中
比如 id 、 className、title 等
data-* 属性
同上
非标准属性
对非标准属性的修改,只会停留在 JS 线程中
不会同步到页面里
重点:如果有自定义属性,又想被同步到页面中,需使用 data-作为前嘴
Property & Attribute
property 属性
JS 县城中 div1 的所有属性,叫 property
attribute 属性
渲染引擎中 div1 对应标签的属性,叫 attribute
区别
大部分时候,同名的 property 和 attribute 值相等
如果不是标准属性,那么他们只会在一开始时相等
注意 attribute 只支持字符串
property 支持字符串、布尔等类型
封装DOM
术语
库
提供给他人使用的工具代码叫做库
比如 jQuery、Underscore
API
库暴露出来的函数或属性叫做 API (应用编程接口)
框架
当库变得很大,并且需要学习才能看懂时
这个库就叫做框架,比如 Vue / React
ps.编程的术语没有固定的解释
对象风格
命名空间风格
window.dom 是创建的全局对象
增
1 2 3 4 5
| dom.create('<div>1</div>') dom.after(div, div1) dom.before(div, div1) dom.append(div.div, div1) dom.wrap(div, div1)
|
删
1 2
| dom.remove(node) dom.empty(parent)
|
改
1 2 3 4 5 6 7 8
| dom.attr(node,'title,?') dom.text(node,?) dom.html(node,?) dom.style(node,{key:value}) dom.class.add(node,'className') dom.class.remove(node,'className') dom.on(node,'click',fn) dom.off(node,'click',fn)
|
查
1 2 3 4 5 6 7 8
| dom.find('选择器') dom.parent(node) dom.children(node) dom.siblings(node) dom.next(node) dom.previous(node) dom.each(nodes,fn) dom.index(node)
|
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
| window.dom = { create(string) { const container = document.createElement('template'); console.log(container); container.innerHTML = string.trim(); return container.content.firstChild; }, after(node, node2) { node.parentNode.insertBefore(node2, node.nextSibling); }, before(node, node2) { node.parentNode.insertBefore(node2, node); }, append(parent, node) { parent.appendChild(node); }, wrap(node, parent) { this.after(parent, node); this.append(parent, node); }, remove(node) { node.parentNode.removeChild(node) return node }, empty(node) { const array = [] let x = node.firstChild while (x) { array.push(dom.remove(node.firstChild)) x = node.firstChild } return array }, attr(node, name, value) { if (arguments.length === 3) { node.setAttribute(name, value) } else if (arguments.length === 2) { return node.getAttribute(name) } }, text(node, string) { if (arguments.length === 2) { if ('innerText' in node) { node.innerText = string } else { node.textContent = string } } else if (arguments.length === 1) { if ('innerText' in node) { return node.innerText } else { return node.textContent } } }, html(node, string) { if (arguments.length === 2) { node.innerHTML = string } else if (arguments.length === 1) { return node.innerHTML } }, style(node, name, value) { if (arguments.length === 3) { node.style[name] = value } else if (arguments.length === 2) { if (name instanceof Object) { const object = name for (let key in object) { node.style[key] = object[key] } } else if (typeof name === string) { return node.style[name] } } }, calss: { add(node, className) { node.classList.add(className) }, remove(node, className) { node.classList.remove(className) }, has(node, className) { return node.classList.contains(className) } }, on(node, eventName, fn) { node.addEventListener(eventName, fn) }, off(node, eventName, fn) { node.removeEventListener(eventName, fn) }, find(selector, scope) { return (scope || document).querySelectorAll(selector) }, parent(node) { return node.parentNode }, children(node) { return node.children }, siblings(node) { return Array.from(node.parentNode.children).filter(n => n !== node) }, next(node) { let x = node.nextSibling while (x && x.nodeType === 3) { x = x.nextSibling } return x }, previous(node) { let x = node.previousSibling while (x && x.nodeType === 3) { x = x.previousSibling } return x }, each(nodeList, fn) { for (let i = 0; i < nodeList.length; i++) { fn.call(null, nodeList[i]) } }, index(node) { const list = dom.children(node.parentNode) let i for (i = 0; i < list.length; i++) { if (list[i] === node) { break } } return i } };
|