DomBom回顾
Dom
window
window:是一个全局对象, 代表浏览器中一个打开的窗口, 每个窗口都是一个window对象
document
document是window的一个属性, 这个属性是一个对象
document: 代表当前窗口中的整个网页,
document对象保存了网页上所有的内容, 通过document对象就可以操作网页上的内容
DOM
DOM 定义了访问和操作 HTML文档(网页)的标准方法
DOM全称: Document Object Model, 即文档模型对象
所以学习DOM就是学习如何通过document对象操作网页上的内容
获取DOM元素
通过id获取指定元素
由于id不可以重复, 所以找到了就会将找到的标签包装成一个对象返回给我们, 找不到就返回Null
注意点: DOM操作返回的是一个对象, 这个对象是宿主类型对象(浏览器提供的对象)
let oDiv = document.getElementById("box")
通过class名称获取
由于class可以重复, 所以找到了就返回一个存储了标签对象的数组, 找不到就返回一个空数组
let oDivs = document.getElementsByClassName("father");
通过name名称获取
由于name可以重复, 所以找到了就返回一个存储了标签对象的数组, 找不到就返回一个空数组
let oDivs = document.getElementsByName("test");
通过标签名称获取
由于标签名称可以重复, 所以找到了就返回一个存储了标签对象的数组, 找不到就返回一个空数组
let oDivs = document.getElementsByTagName("div");
通过选择器获取
querySelector只会返回根据指定选择器找到的第一个元素
let oDiv = document.querySelector("#box");
通过选择器获取
querySelectorAll会返回指定选择器找到的所有元素
let oDivs = document.querySelectorAll(".father");
获取指定元素所有的子元素
children属性获取到的是指定元素中所有的子元素
console.log(oDiv.children);
childNodes属性获取到的是指定元素中所有的节点
console.log(oDiv.childNodes);
获取相邻上一个节点
console.log(item.previousSibling);
获取相邻上一个元素
console.log(item.previousElementSibling);
获取相邻下一个节点
console.log(item.nextSibling)
获取相邻下一个元素
console.log(item.nextElementSibling);
元素增删改查
创建节点
let oSpan = document.createElement("span");
添加节点
// 注意点: appendChild方法会将指定的元素添加到最后
// let oA = document.createElement("a");
// oDiv.appendChild(oA);
插入节点
oDiv.insertBefore(oSpan, oP)
删除节点
注意点: 在js中如果想要删除某一个元素, 只能通过对应的父元素来删除
oSpan.parentNode.removeChild(oSpan);
克隆节点
注意点: cloneNode方法默认不会克隆子元素, 如果想克隆子元素需要传递一个true
let newDiv = oDiv.cloneNode(true);
元素属性操作
无论是通过document创建还是查询出来的标签,系统都会将元素包装成一个对象返回给我们,
系统在包装这个对象的时候会自动将元素的属性都包装到这个对象中,
所以只要拿到这个对象就可以拿到标签属性,操作标签属性
如何获取元素属性
console.log(oImg.alt);
console.log(oImg.getAttribute("alt"));
注意点: 通过对象.属性名称的方式无法获取到自定义属性的取值
// 通过getAttribute方法可以获取到自定义属性的取值
如何修改元素属性
oImg.title = "新的title";
注意点和获取元素属性一样
oImg.setAttribute("title", "新的title222");
如何新增元素属性
oImg.setAttribute("it666", "itzb");
// 注意点: setAttribute方法如果属性不存在就是新增, 如果属性存在就是修改
.如何删除元素属性
// oImg.alt = "";
// oImg.removeAttribute("alt");
// 注意点和获取元素属性一样 无法删除自定义属性的值
元素内容操作
获取元素内容
1.innerHTML获取的内容包含标签, innerText/textContent获取的内容不包含标签
2.innerHTML/textContent获取的内容不会去除两端的空格, innerText获取的内容会去除两端的空格
设置元素内容
特点:
无论通过innerHTML/innerText/textContent设置内容, 新的内容都会覆盖原有的内容
区别:
如果通过innerHTML设置数据, 数据中包含标签, 会转换成标签之后再添加
如果通过innerText/textContent设置数据, 数据中包含标签, 不会转换成标签, 会当做一个字符串直接设置
操作元素样式
设置元素样式
第一种方式
注意点: 由于class在JS中是一个关键字, 所以叫做className
oDiv.className = “box”;
第二种方式
注意点: 过去CSS中通过-连接的样式, 在JS中都是驼峰命名
注意点: 通过JS添加的样式都是行内样式, 会覆盖掉同名的CSS样式
获取元素样式
oDiv.style.width = "300px";
注意点: 通过style属性只能获取到行内样式的属性值, 获取不到CSS设置的属性值
注意点: 如果想获取到CSS设置的属性值, 必须通过getComputedStyle方法来获取
getComputedStyle方法接收一个参数, 这个参数就是要获取的元素对象
getComputedStyle方法返回一个对象, 这个对象中就保存了CSS设置的样式和属性值
let style = getComputedStyle(oDiv);
console.log(style.width);
DOM事件
事件
用户和浏览器之间的交互行为我们就称之为事件, 比如:点击,移入/移出
元素绑定事件
在JavaScript中所有的HTML标签都可以添加事件
元素.事件名称 = function(){};
当对应事件被触发时候就会自动执行function中的代码
// 注意点: 如果给元素添加了和系统同名的事件, 我们添加的事件不会覆盖系统添加的事件
添加事件三种方式
通过onxxx的方式来添加
注意点: 由于是给属性赋值, 所以后赋值的会覆盖先赋值
oBtn.onclick = function () {
alert("777");
}
通过addEventListener方法添加
oBtn.addEventListener("click", function () {
alert("666");
});
注意点:
1.事件名称不需要添加on
2.后添加的不会覆盖先添加的
3.只支持最新的浏览器IE9
通过attachEvent方法添加
oBtn.attachEvent("onclick", function () {
alert("666");
});
注意点:
1.事件名称必须加上on
2.后添加的不会覆盖先添加的
3.只支持低版本的浏览器
兼容性写法
function addEvent(ele, name, fn) {
if(ele.attachEvent){
ele.attachEvent("on"+name, fn);
}else{
ele.addEventListener(name, fn);
}
}
事件对象
事件对象就是一个系统自动创建的一个对象
当注册的事件被触发的时候, 系统就会自动创建事件对象
事件对象的注意点:
在高级版本的浏览器中, 会自动将事件对象传递给回到函数
在低级版本的浏览器中, 不会自动将事件对象传递给回调函数
在低级版本的浏览器中, 需要通过window.event来获取事件对象
let oA = document.querySelector("a");
oA.onclick = function (event) {
// 兼容性的写法
event = event || window.event;
alert("666");
// 阻止默认行为
return false; // 企业开发推荐
事件的三个阶段
事件的三个阶段
1.1.捕获阶段(从外向内的传递事件)
1.2.当前目标阶段
1.3.冒泡的阶段(从内向外的传递事件)
注意点:
三个阶段只有两个会被同时执行
要么捕获和当前, 要么当前和冒泡
为什么要么只能是捕获和当前, 要么只能是当前和冒泡?
这是JS处理事件的历史问题
早期各大浏览器厂商为占领市场, 以及对事件的理解不同
后续W3C为了兼容, 将两种方式都纳入标准
如何设置事件到底是捕获还是冒泡?
通过addEventListener方法, 这个方法接收三个参数
第一个参数: 事件的名称
第二个参数: 回调函数
第三个参数: false冒泡 / true 捕获
注意点:
onXxx的属性, 不接收任何参数, 所以默认就是冒泡
attachEvent方法, 只能接收两个参数, 所以默认就是冒泡
阻止事件冒泡
oSDiv.onclick = function (event) {
event = event || window.event;
// 注意点: stopPropagation方法只支持高级浏览器
// event.stopPropagation();
// event.cancelBubble = true; // 低级浏览器
if(event.cancelBubble){
event.cancelBubble = true;
}else{
event.stopPropagation();
}
console.log("son");
}
事件委托
给父元素注册事件,利用事件冒泡,当子元素的事件触发,会冒泡到父元素,然后去控制相应的子元素。
事件委托的作用
-
我们只操作了一次 DOM ,提高了程序的性能。
-
动态新创建的子元素,也拥有事件。
-
// 事件委托的核心原理:给父节点添加侦听器, 利用事件冒泡影响每一个子节点 var ul = document.querySelector('ul'); ul.addEventListener('click', function(e) { // e.target 这个可以得到我们点击的对象 e.target.style.backgroundColor = 'pink'; })
定时器
setInterval
setTimeout
闭包(closure)
闭包是一种特殊的函数。
如何生成一个闭包?
当一个内部函数引用了外部函数的数据(变量/函数)时, 那么内部的函数就是闭包
所以只要满足"是函数嵌套"、“内部函数引用外部函数数据”
闭包特点:
只要闭包还在使用外部函数的数据, 那么外部的数据就一直不会被释放
也就是说可以延长外部函数数据的生命周期
闭包注意点:
当后续不需要使用闭包时候, 一定要手动将闭包设置为null, 否则会出现内存泄漏
排它思想
let oItems = document.querySelectorAll("li");
let previousIndex = 0;
//es6
for(let i = 0; i < oItems.length; i++){
let item = oItems[i];
item.onclick = function () {
// 排它
// for(let j = 0; j < oItems.length; j++){
// let li = oItems[j];
// li.className = "";
// }
let preItem = oItems[previousIndex];
preItem.className = "";
this.className = "current";
previousIndex = i;
}
}
//es5
for(var i = 0; i < oItems.length; i++) {
var item = oItems[i];
(function (index) {
item.onclick = function () {
// 排它
var preItem = oItems[previousIndex];
preItem.className = "";
this.className = "current";
previousIndex = index;
}
})(i);
}
Date对象
获取当前时间
// let date = new Date();
// console.log(date);
获取当前时间距离1970年1月1日(世界标准时间)起的毫秒
// console.log(Date.now());
// let date = new Date();
// console.log(date.valueOf());
创建指定时间
let date1 = new Date("2019-11-11 09:08:07");
console.log(date1);
// 注意点: 在创建指定时间的时候, 如果月份是单独传入的, 那么会多一个月
let date2 = new Date(2019, 10, 11, 9, 8, 7);
console.log(date2);
获取指定时间年月日时分秒
let date = new Date();
console.log(date); //Wed Oct 14 2020 20:21:11 GMT+0800 (中国标准时间)
console.log(date.getFullYear());//2020
// 注意点; 通过getMonth方法获取到的月份会少一个月
console.log(date.getMonth() + 1);//10
console.log(date.getDate());//14
console.log(date.getHours());//20
console.log(date.getMinutes());//21
console.log(date.getSeconds());//11
时间格式化函数
function formartDate(date) {
return `${date.getFullYear()}-
${date.getMonth() + 1}-
${date.getDate()}
${date.getHours()}:
${date.getMinutes()}:
${date.getSeconds()}`;
}
匀速动画
function linearAnimation(ele, target) {
clearInterval(timerId);
timerId = setInterval(function () {
// 1.拿到元素当前的位置
let begin = parseInt(ele.style.marginLeft) || 0;
// 2.定义变量记录步长
// 0 - 500 = -500 13
// 500 - 200 = 300 -13
let step = (begin - target) > 0 ? -13 : 13;
// 3.计算新的位置
begin += step;
console.log(Math.abs(target - begin), Math.abs(step));
if(Math.abs(target - begin) <= Math.abs(step)){
clearInterval(timerId);
begin = target;
}
// 4.重新设置元素的位置
ele.style.marginLeft = begin + "px";
}, 100);
}
缓动动画
function easeAnimation(ele, target) {
clearInterval(timerId);
timerId = setInterval(function () {
// 1.拿到元素当前的位置
let begin = parseInt(ele.style.marginLeft) || 0;
// 2.定义变量记录步长
// 公式: (结束位置 - 开始位置) * 缓动系数(0 ~1)
let step = (target - begin) * 0.3;
console.log(step);
// 3.计算新的位置
begin += step;
if(Math.abs(Math.floor(step)) <= 1){
clearInterval(timerId);
begin = target;
}
// 4.重新设置元素的位置
ele.style.marginLeft = begin + "px";
}, 100);
}
匀速动画增强
function linearAnimation(ele, obj, fn) {
clearInterval(timerId);
timerId = setInterval(function () {
// flag变量用于标记是否所有的属性都执行完了动画
let flag = true;
for(let key in obj){
let attr = key;
let target = obj[key];
// 1.拿到元素当前的位置
// let begin = parseInt(ele.style.width) || 0;
let style = getComputedStyle(ele);
// let begin = parseInt(style.width) || 0;
let begin = parseInt(style[attr]) || 0;
// 2.定义变量记录步长
let step = (begin - target) > 0 ? -13 : 13;
// 3.计算新的位置
begin += step;
if(Math.abs(target - begin) > Math.abs(step)){
flag = false;
}else{
begin = target;
}
// 4.重新设置元素的位置
// ele.style.width = begin + "px";
ele.style[attr] = begin + "px";
}
if(flag){
clearInterval(timerId);
// if(fn){
// fn();
// }
fn && fn();
}
}, 100);
}
移入移出事件区别
onmouseover和onmouseenter的区别
onmouseover移入到子元素,父元素的移入事件也会被触发
onmouseenter移入到子元素,父元素的移入事件不会被触发
onmouseout和onmouseleave的区别
onmouseout移出到子元素,父元素的移入事件也会被触发
onmouseleave移出到子元素,父元素的移入事件不会被触发
鼠標位置获取
offsetX/offsetY: 事件触发相对于当前元素自身的位置
clientX/clientY: 事件触发相对于浏览器可视区域的位置
注意点: 可视区域是不包括滚动出去的范围的
pageX/pageY: 事件触发相对于整个网页的位置
主要点: 整个网页包括滚动出去的范围的
screenX/screenY: 事件触发相对于屏幕的位置
注意点: clientX/clientY无论高级浏览器还是低级浏览器都支持
pageX/pageY只有高级浏览器支持, 低级浏览器是不支持的(IE9以下)
Bom
BOM就是一套操作浏览器的API(接口/方法/属性)
BOM中常见的对象
window: 代表整个浏览器窗口
注意: window是BOM中的一个对象, 并且是一个*的对象(全局)
Navigator: 代表当前浏览器的信息, 通过Navigator我们就能判断用户当前是什么浏览器
Location: 代表浏览器地址栏的信息, 通过Location我们就能设置或者获取当前地址信息
History: 代表浏览器的历史信息, 通过History来实现刷新/上一步/下一步
注意点: 出于隐私考虑, 我们并不能拿到用户所有的历Z史记录, 只能拿到当前的历史记录
Screen: 代表用户的屏幕信息
Navigator
代表当前浏览器的信息, 通过Navigator我们就能判断用户当前是什么浏览器
简单判断浏览器
if(/chrome/i.test(agent)){
alert("当前是谷歌浏览器");
}else if(/firefox/i.test(agent)){
alert("当前是火狐浏览器");
}else if(/msie/i.test(agent)){
alert("当前是低级IE浏览器");
}else if("ActiveXObject" in window){
alert("当前是高级IE浏览器");
}
Location
代表浏览器地址栏的信息, 通过Location我们就能设置或者获取当前地址信息
获取当前地址栏的地址
window.location.valueOf()
window.location.href
设置当前地址栏的地址
window.location.href = "http://www.it666.com";
重新加载界面
window.location.reload()
强制刷新
window.location.reload(true)
History
代表浏览器的历史信息, 通过History来实现刷新/前进/后退
注意点: 出于隐私考虑, 我们并不能拿到用户所有的历史记录, 只能拿到当前的历史记录
前进
注意点:
只有当前访问过其它的界面, 才能通过forward/go方法前进
如果给go方法传递1, 就代表前进1个界面, 传递2就代表进行2个界面
window.history.forward();
window.history.go(1);
刷新
如果给go方法传递0, 就代表刷新
window.history.go(0);
后退
注意点:
只有当前访问过其它的界面, back/go方法后退
如果给go方法传递-1, 就代表后退1个界面, 传递-2就代表后退2个界面
window.history.go(-1);
window.history.back();
获取元素宽高其它方式
通过getComputedStyle获取宽高
1.1获取的宽高不包括 边框和内边距
1.2即可以获取行内设置的宽高也可以获取CSS设置的宽高
1.3只支持获取, 不支持设置
1.4只支持IE9及以上浏览器
2.通过currentStyle属性获取宽高
2.1获取的宽高不包括 边框和内边距
2.2即可以获取行内设置的宽高也可以获取CSS设置的宽高
2.3只支持获取, 不支持设置
2.4只支持IE9以下浏览器
3.通过style属性获取宽高
3.1获取的宽高不包括 边框和内边距
3.2只能获取行内设置的宽高, 不能获取CSS设置的宽高
3.3可以获取也可以设置
3.4高级低级浏览器都支持
4.offsetWidth和offsetHeight
4.1获取的宽高包含 边框 + 内边距 + 元素宽高
4.2即可以获取行内设置的宽高也可以获取CSS设置的宽高
4.3只支持获取, 不支持设置
4.4高级低级浏览器都支持
总结
1.getComputedStyle/currentStyle/style
获取的宽高不包括 边框和内边距
2.offsetWidth/offsetHeight
获取的宽高包括 边框和内边距
3.getComputedStyle/currentStyle/offsetXXX
只支持获取, 不支持设置
4.style
可以获取, 也可以设置
5.getComputedStyle/currentStyle/offsetXXX
即可以获取行内,也可以获取外链和内联样式
6.style
只能获取行内样式
获取元素位置
.offsetLeft和offsetTop作用
获取元素到第一个定位祖先元素之间的偏移位
如果没有祖先元素是定位的, 那么就是获取到body的偏移位
offsetParent作用
获取元素的第一个定位祖先元素
如果没有祖先元素是定位的, 那么就是获取到的就是body
client属性
clientWidth,clientHeight:内容区域的宽高,padding+content的宽高,不包括边框宽度值。
offsetWidth = 宽度 + 内边距 + 边框
offsetHeight = 高度 + 内边距 + 边框
clientLeft,clientTop:表示内容区域的左上角相对于整个元素左上角的位置(包括边框),实测,
offsetLeft/offsetTop: 距离第一个定位祖先元素偏移位 || body
clientLeft=左侧边框的宽度,clientTop=顶部边框的宽度
scroll属性
内容没有超出元素范围时
scrollWidth: = 元素宽度 + 内边距宽度 == clientWidth
scrollHeight: = 元素高度 + 内边距的高度 == clientHeight
.内容超出元素范围时
scrollWidth: = 元素宽度 + 内边距宽度 + 超出的宽度
scrollHeight: = 元素高度 + 内边距的高度 + 超出的高度
scrollTop / scrollLeft
scrollTop: 超出元素内边距顶部的距离
scrollLeft: 超出元素内边距左边的距离
获取网页宽高
innerWidth/innerHeight
只能在IE9以及IE9以上的浏览器中才能获取
console.log(window.innerWidth);
documentElement.clientWidth/documentElement.clientHeight
可以用于在IE9以下的浏览器的标准模式中获取
浏览器在渲染网页的时候有两种模式"标准模式"/“混杂模式/怪异模式”
默认情况下都是以标准模式来进行渲染的(CSS1Compat)
如果网页没有书写文档声明, 那么就会按照"混杂模式/怪异模式"来进行渲染的(BackCompat)
注意点: 在混杂模式中利用如下的方式获取可视区域的宽高
console.log(document.body.clientWidth);
function getScreen() {
let width, height;
if(window.innerWidth){
width = window.innerWidth;
height = window.innerHeight;
}else if(document.compatMode === "BackCompat"){//混杂模式
width = document.body.clientWidth;
height = document.body.clientHeight;
}else{//标准模式
width = document.documentElement.clientWidth;
height = document.documentElement.clientHeight;
}
return {
返回
width: width,
height: height
}
网页滚动距离
function getPageScroll() {
let x, y;
if(window.pageXOffset){//IE9以及IE9以上的浏览器
x = window.pageXOffset;
y = window.pageYOffset;
}else if(document.compatMode === "BackCompat"){//混杂(怪异)模式下浏览器
x = document.body.scrollLeft;
y = document.body.scrollTop;
}else{//标准模式下浏览器
x = document.documentElement.scrollLeft;
y = document.documentElement.scrollTop;
}
return {
x: x,
y: y
}
}
函数防抖
函数防抖是优化高频率执行js代码的一种手段
可以让被调用的函数在一次连续的高频操作过程中只被调用一次
函数防抖应用场景
oninput / onmousemove / onscroll / onresize等事件
function debounce(fn, delay) { // fn = test
let timerId = null;
return function () {
let self = this;
let args = arguments;
timerId && clearTimeout(timerId);
timerId = setTimeout(function () {
fn.apply(self, args);
}, delay || 1000);
}
}
函数节流[throttle]
函数节流也是优化高频率执行js代码的一种手段
可以减少高频调用函数的执行次数
函数节流应用场景
oninput / onmousemove / onscroll / onresize等事件
函数节流和函数防抖区别
函数节流是减少连续的高频操作函数执行次数 (例如连续调用10次, 可能只执行3-4次)
函数防抖是让连续的高频操作时函数只执行一次(例如连续调用10次, 但是只会执行1次)
function throttle(fn, delay) { // fn = test
let timerId = null;
let flag = true;
return function () {
if(!flag) return;
flag = false;
let self = this;
let args = arguments;
timerId && clearTimeout(timerId);
timerId = setTimeout(function () {
flag = true;
fn.apply(self, args);
}, delay || 1000);
}
}
上一篇: C# 子线程如何操作主线程窗体上的控件
下一篇: 十道前端面试题(6)