JS-Web-API-DOM
DOM 节点操作
获取 DOM 节点
document.getElementById():返回对拥有指定 id 的第一个对象的引用。
document.getElementsByTagName():返回带有指定标签名的对象集合。
document.getElementsByClassName():返回一个包含了所有指定类名的子元素的类数组对象。
document.querySelectorAll():返回与指定的选择器组匹配的文档中的元素列表 (使用深度优先的先序遍历文档的节点)。返回的对象是 NodeList 。
示例:获取 DOM 节点的 Demo
<div id="div1" class="container"> <p>一段文字</p> <p>一段文字</p> <p>一段文字</p> </div> <div id="div2" class="container"> <img src="./code.png"> </div> const div1 = document.getElementById('div1') console.log('div1', div1) const divList = document.getElementsByTagName('div') console.log('divList.length', divList.length) console.log('divList[1]', divList[1]) const containerList = document.getElementsByClassName('container') console.log('containerList.length', containerList.length) console.log('containerList[1]', containerList[1]) const pList = document.querySelectorAll('p') console.log('pList', pList)
DOM 节点的 property 和 attribute
- property:修改对象属性,不会体现到 html 结构中
- attribute:修改 html 属性,会改变 html 结构
- 两者都有可能引起 DOM 的重新渲染
示例 1:property 修改对象属性(推荐使用)
.container { border: 1px solid #ccc; } .red { color: red; } <div id="div1" class="container"> <p>一段文字1</p> <p>一段文字2</p> <p>一段文字3</p> </div> // property 形式 const pList = document.querySelectorAll('p') const p1 = pList[0] p1.style.width = '100px' console.log(p1.style.width) p1.className = 'red' console.log(p1.className) console.log(p1.nodeName) console.log(p1.nodeType)
示例 2:attribute 修改 html 属性
.container { border: 1px solid #ccc; } .red { color: red; } <div id="div1" class="container"> <p>一段文字1</p> <p>一段文字2</p> <p>一段文字3</p> </div> // attribute const pList = document.querySelectorAll('p') const p1 = pList[0] p1.setAttribute('data-name', 'imooc') console.log(p1.getAttribute('data-name')) p1.setAttribute('style', 'font-size: 30px;') console.log(p1.getAttribute('style'))
DOM 结构操作
新增、插入、移动节点
- 新增节点:createElement
- 插入节点:appendChild
- 移动节点:已有的节点插入到别的容器就会发生移动
示例:
<div id="div1" class="container"> <p id="p1">一段文字1</p> <p>一段文字2</p> <p>一段文字3</p> </div> <div id="div2" class="container"></div> const div1 = document.getElementById('div1') const div2 = document.getElementById('div2') const newP = document.createElement('p') // 新建节点 newP.innerHTML = '新增的一段文字' // 插入节点 div1.appendChild(newP) // 移动节点 const p1 = document.getElementById('p1') div2.appendChild(p1)
获取子元素列表 & 获取父元素
- parentNode:父元素
- childNodes:子元素
- removeChild:删除子元素
示例 1:div1ChildNodes 不是数组,需要Array.from() 转为数组
<div id="div1" class="container"> <p id="p1">一段文字1</p> <p>一段文字2</p> <p>一段文字3</p> </div> const div1 = document.getElementById('div1') // 获取父元素 console.log(p1.parentNode) // 获取子元素列表 const div1ChildNodes = div1.childNodes console.log(div1.childNodes) const divChildNodesP = Array.from(div1ChildNodes).filter(child => { if(child.nodeType === 1) { return true } return false }) console.log('divChildNodesP', divChildNodesP)
删除子元素
示例:在上文的基础上进行删除子元素
div1.removeChild(divChildNodesP[0])
如何优化 DOM 操作的性能
DOM 查询做缓存
- 做缓存,先接收所有的 length,避免循环的时候重复操作 DOM
示例 1:
// 不缓存 DOM 查询结果 for(let i = 0; i < document.getElementsByTagName('p').length; i++) { // 每次循环,都会计算 length,频繁进行 DOM 查询 }
示例 2:
// 缓存 DOM 查询结果 const pList = document.getElementsByTagName('p') const length = pList.length for(let i = 0; i < length; i++) { // 缓存 length,只进行一次 DOM 查询 }
将频繁操作改为一次性操作
- 创建文档片段,把循环的内容先插入到文档片段中
示例 1:频繁的 DOM 操作
<ul id="list"></ul> const list = document.getElementById('list') for(let i = 0; i < 10; i++) { const li = document.createElement('li') li.innerHTML = `List item ${i}` list.appendChild(li) }
示例 2:创建文档片段
<ul id="list"></ul> const list = document.getElementById('list') // 创建一个文档片段,此时还没有插入到 DOM 树中 const frag = document.createDocumentFragment() // 执行插入 for(let i = 0; i <= 10; i++) { const li = document.createElement('li') li.innerHTML = `List item ${i}` // 先插入文档片段中 frag.appendChild(li) } // 都完成之后,再统一插入到 DOM 树中 list.appendChild(frag)
JS-Web-API-BOM
BOM 操作相关面试题
navigation 和 screen
- navigation.userAgent:浏览器用于 HTTP 请求的用户代理头的值
- screen.width:屏幕宽度
- screen.height:屏幕高度
拆解 url 各部分
location.href:返回完整的URL
location.protocol:返回一个URL协议
location.host:返回一个URL的主机名和端口
location.search:返回一个URL的查询部分
location.hash:返回一个URL的锚部分
location.pathname:返回的URL路径名。
JS-Web-API-事件
事件绑定
示例:通用事件绑定函数
<button id="btn1">一个按钮</button> function bindEvent(elem, type, fn) { elem.addEventListener(type, fn) } const btn1 = document.getElementById('btn1') bindEvent(btn1, 'click', event => { console.log(event.target) // alert('clicked') })
事件冒泡
- 基于 DOM 树形结构
- 事件会顺着触发元素往上冒泡
- 应用场景:代理
- stopPropagation:用于阻止事件冒泡
<div id="div1"> <p id="p1">激活</p> <p id="p2">取消</p> <p id="p3">取消</p> <p id="p4">取消</p> </div> <div id="div2"> <p id="p5">取消</p> <p id="p6">取消</p> </div> function bindEvent(elem, type, fn) { elem.addEventListener(type, fn) } const p1 = document.getElementById('p1') const body = document.body bindEvent(p1, 'click', event => { event.stopPropagation() // 阻止冒泡 console.log('激活') }) bindEvent(body, 'click', event => { console.log('取消') })
不阻止冒泡,点击激活会冒泡到取消:
阻止冒泡,点击激活不会冒泡:
事件代理
- 代码更简洁
- 减少浏览器内存占用
- 但是,不要滥用
- 对于复杂,不好去每个都绑定事件的时候使用
示例:元素 a 把事件处理委托给自己的父元素 div 去处理
<div id="div1"> <a href="#">a1</a> <a href="#">a2</a> <a href="#">a3</a> <a href="#">a4</a> <button>加载更多...</button> </div> function bindEvent(elem, type, fn) { elem.addEventListener(type, fn) } const div1 = document.getElementById('div1') bindEvent(div1, 'click', event => { event.preventDefault() // 阻止页面跳转 const target = event.target if(target.nodeName === 'A') { alert(target.innerHTML) } })
点击 a1-4 会弹出对话框,点击按钮则不会:
通用事件绑定函数(考虑代理)
- fn 不能是箭头函数,箭头函数无法被 call
<div id="div1"> <a href="#">a1</a> <a href="#">a2</a> <a href="#">a3</a> <a href="#">a4</a> <button>加载更多...</button> </div> <button id="btn1">点击</button> // 通用事件绑定函数 function bindEvent(elem, type, selector, fn) { // 如果传入三个参数,把 selector 赋值给 fn,selector 置空 if (fn == null) { fn = selector selector = null } elem.addEventListener(type, event => { const target = event.target if (selector) { // 代理绑定 if (target.matches(selector)) { fn.call(target, event) } } else { // 普通绑定 fn.call(target, event) } }) } // 普通绑定 const btn1 = document.getElementById('btn1') bindEvent(btn1, 'click', function(event) { event.preventDefault() alert(this.innerHTML) }) // 代理绑定 const div1 = document.getElementById('div1') bindEvent(div1, 'click', 'a', function (event) { event.preventDefault() alert(this.innerHTML) })
点击 a1-4 会弹出对话框,点击按钮则不会:
不积跬步无以至千里 不积小流无以成江海