Skip to content

DOM操作

参考

获取DOM节点

js
// 根据id返回单个元素节点
document.getElementById("id");

// 根据标签名或类名返回全部的元素节点列表(是一个HTMLCollection类数组对象,犀牛书上说的是返回Nodelist)
// 即使只有一个元素节点也必须通过下标访问
document.getElementsByTagName("ul");
document.getElementsByClassName("head");

// 根据标签name属性选择元素
document.getElementsByName("tel");

// 在CSS3标准化同时新增了一组“选择器API”,用于增强筛选节点的能力
document.querySelector(selector);
document.querySelectorAll(selector);

需要注意的是

  • HTMLCollection返回的是动态的节点引用,这意味着如果节点发生了改变,不需要通过getElementsByTagName等方式从新获取
  • querySelectorAll返回的是静态的节点列表,返回的只是调用该方法时页面节点的一个静态副本,不会随着页面节点的改变而变化
html
 <ul id="myUl">
    <li>3</li>
</ul>
<script>
    var t1 = document.getElementsByTagName("li")
    var t2 = document.querySelectorAll("li")
    console.log(t1)
    console.log(t2)

    myUl.innerHTML = `<li>1</li><li>2</li>`

    console.log(t1)
    console.log(t2)
</script>

操作节点

属性节点

js
var oTest = document.querySelector("#test");
oTest.getAttribute("title"); //获取title属性的值,若无返回null
//为titile属性设置hello的值
oTest.setAttribute("title","hello") // titile属性设置hello的值
oTest.removeAttribute("title")
// HTML5新增了自定义属性,使用data-*进行作为属性名,用于保存我们自定义的属性,同时提供了一个dataset属性用于访问自定义属性
oTest.dataset.demo

文本节点

js
// 获取文本节点
var htm = oTest.innerHTML; // Haha <a href="#">link</a>
// 设置文本节点
oTest.innerHTML = "Hello World";
oTest.innerText; // Haha link

遍历节点

js
parentNode // 返回当前结点的父节点
offsetParent // 返回的是离当前结点最近的有定位的父节点
childNodes // 返回当前结点的全部子节点
firstElementChild // 元素的第一个子元素节点
lastElementChild // 表示元素的最后一个子元素节点

// 访问相邻的兄弟节点
nextSibling
previousSibling

nextElementSibling
previousSibling

操作CSS样式

查询元素的style属性返回的是一个叫做CSSStyleDeclaration对象而不是简单的字符串,该对象包含了元素的样式 需要注意的是:通过这种方式只能获得元素的行内样式

// 由于-是JavaScript的元素符,因此,如果所需的CSS属性中包含-连字符,比如font-size等,需要转换成fontSize驼峰形式的才行
test.style.color; // green
test.style.color = "red";

如果是需要获取元素的渲染样式

js
function getStyle(obj, attr) {
    return obj.currentStyle ? obj.currentStyle[attr] : getComputedStyle(obj)[attr];
}

在开发中更常用的做法是切换样式类来修改元素的样式

js
test.className = "text-green";

HTML5为元素节点提供了一个classList的属性,该属性是一个只读的类数组对象(无法被覆盖),包含了该元素节点的单独类名,并提供了相关的接口用于操作,addremovetogglecontains

构造元素节点

我们可以直接向文档中添加标记,让浏览器重新解析文档并生成节点。可以使用的是document.write()方法和node.innerHTML属性。

此外DOM也提供了相关的接口

js
// 创建对应标签的元素节点并返回一个ElementNode对象
document.createElement(tagname);

// 创建内容为text的文本节点
document.createTextNode(text);

// 创建评论节点
document.createComment(text);

// 克隆节点,传入true则会递归复制该节点的所有子节点
test.cloneNode()

插入节点

js
// 将动态创建的节点插入到父节点中
parent.appendChild(node);
// 将动态创建的节点插入到兄弟节点前,注意这个方法也是在父节点上进行的
parentUl.insertBefore(oLi, siblingLi);

// 并没有insertAfter方法,因此可以自己实现一个
function insertAfter(parent, sibling, node){
    var children = parent.children;

    if (children[children.length - 1] == sibling){
        parent.appendChild(node);
    }else {
        var next = sibling.nextElementSibling;
        console.log(next);
        parent.insertBefore(node, next);
    }
}

HTML5引进了一个十分强大的插入节点的API,叫做innerAdjacentHTML,用于插入文档节点,这里是文档传送门

删除和替换子节点

js
// 移除子节点
parentUl.removeChild(siblingLi)
// 使用oLi新节点替换子节点
 parentUl.replaceChild(oLi, siblingLi)

此外还可以通过innerHTML进行覆盖,移除相关的节点。

获取DOM尺寸的各种API

  • window.innerWidth, window.innerHeight 获取浏览器整体窗口的宽高
  • client系列
    • clientTop: 获取元素border-top的宽度
    • **clientLeft:**获取元素border-left的宽度
    • clientWidth: 获取元素的宽度,不包含border
    • clientHeight:获取元素的高度,不包含border
    • getBoundingClientRect, 获取与元素尺寸信息有关的对象
      • top bottom: 获取元素上下边(不考虑margin)到浏览器窗口上边的距离数值
      • left right: 获取元素左右边(不考虑margin)到浏览器窗口左边的距离数值
      • width height: 元素的宽度和高度(包含border)
      • x y : 或元素左顶点到窗口左边和上边的距离(不考虑margin) 这个属性兼容性不好,不用
  • offset系列
    • **offsetParent:**获取元素的最近position不是static的祖先元素
    • offsetLeft: 获取元素(包含border)相对于最近position不是static的祖先元素的左边距离(不包含border,包含padding)
    • offsetTop: 获取元素(包含border)相对于最近position不是static的祖先元素的上边距离(不包含border,包含padding)
    • offsetWidth: 获取元素的宽度,包含border
    • offsetHeight: 获取元素的高度,包含border
  • scroll系列
    • scrollLeft: 获取左侧卷入不可见区域的宽度
    • scrollTop: 获取上侧卷入不可见的区域
    • scrollWidth: 获取的宽度为(内容实际宽度包括卷入的区域+padding部分)与(元素宽度+padding部分)
    • scrollHeight: 获取的宽度为(内容实际高度包括卷入的区域+padding部分)与(元素高度+padding部分)
    • window.scrollBy(x,y): 窗口相对滚动函数
    • window.scrollTo(x,y): 窗口绝对滚动函数

事件

JS动画

requestAnimationFrame

参考:

window.requestAnimationFrame(callback);
  • 在大多数浏览器里,当运行在后台标签页或者隐藏的iframe 里时,requestAnimationFrame() 会暂停调用以提升性能和电池寿命。
  • callback会被传入一个参数,DOMHighResTimeStamp,指示当前被 requestAnimationFrame() 排序的回调函数被触发的时间。即使每个回调函数的工作量的计算都花了时间,单个帧中的多个回调也都将被传入相同的时间戳

JS动画性能优化

JS实现动画性能优化,参考流畅JS动画的7个性能优化建议

  • 避免针对复杂css属性执行动画,提升web动画性能简单而有效的方式是改变那些只触发复合操作的css属性,如使用transform代替left等属性
  • 使用requestAnimationFrame代替setTimeout/setInterval,避免动画丢帧
  • 如果fps是60,则每一帧只有16.67毫秒,避免执行复杂的计算,可以适当做缓存处理,增加动画的流畅性
  • 某些DOM属性如offsetTop等会导致浏览器的重绘,可以使用变量将其缓存起来

BOM

BOM(浏览器对象模型)是浏览器本身的一些信息的设置和获取,一般常用的是

  • navigator,用于获取浏览器特征,比如判断平台等
  • screen,获取屏幕宽高
  • location,获取网址、协议、path、参数、hash
  • history,访问浏览器的历史记录栈

Web Storage API

HTML5新增了sessionStorage和localStorage用于本地存储,专门为了浏览器端缓存而设计的,优点有:

  • 存储量增大到 5MB
  • 不会带到 HTTP 请求中

注意

  • 与之前cookie保存数据的区别
  • localStoragesessionStorage的区别
  • 在使用localStoragesessionStorage时尽量加入到try-catch中,某些浏览器是禁用这个 API 的(如在无痕模式下访问)。

常见问题 cookies,sessionStorage 和 localStorage的区别?

相同:都是保存在浏览器,且同源的。 区别:

  • cookies 和 *Storage 的区别:
    • cookies会在服务器端和客户端间传递的;
    • sessionStorage 和 localStorage存放在客户端的,不会发送至服务器端,仅在本地保存。
    • cookies的兼容主流浏览器,包括IE6+;IE6,IE7不支持sessionStorage 和 localStorage
    • sessionStorage 和 localStorage中能存的数据比cookie大(cookie不能超过4k)
  • sessionStorage 和 localStorage的区别:
    • sessionStorage存的数据在每次关闭浏览器后被删除,localStorage不会。
    • 作用域不同,sessionStorage不能在浏览器的不同标签页中共享,即使是同一个页面(刷新页面可以继续存在);
    • localStorage 在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的

Web Worder

参考

JavaScript 语言采用的是单线程模型。Web Worker 的作用,就是为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行,这样做的好处是可以在独立线程中执行费时的处理任务,从而允许主线程(通常是UI线程)不会因此被阻塞/放慢。

在主线程中

js
// 创建worker
var worker = new Worker('work.js');
// 向worker发送消息
worker.postMessage('Hello World');
// 注意传输的数据是值的拷贝,worker无法修改主线程中的变量
worker.postMessage({method: 'echo', args: ['Work']});

// 主线程通过事件,接收子线程发回来的消息
worker.onmessage = function (event) {
  console.log('Received message ' + event.data);
  doSomething();
}
// 监听worker的错误
worker.onerror(function (event) {});


// 主线程关闭worker
worker.terminate();

在worker中,通过self代表子线程自身,即子线程的全局对象

js
// 监听message事件,接收主线程发送的消息
self.addEventListener('message', function (e) {
  // 通过postMessage向主线程发送消息
  self.postMessage('You said: ' + e.data);
}, false);

// 主动关闭
self.close();

Service Worker

参考:

可以用来作为web应用程序、浏览器和网络(如果可用)之间的代理服务,常见的如控制缓存等,可以用来实现PWA