节点
整个DOM树就是由各种节点构成。我们可以增加或减少节点,来改变DOM树结构。如下就是节点的简单操作。
<div id="box">div 1</div>
<div id="demo">
<p>paragraph 1</p>
<p>paragraph 2</p>
<p>paragraph 3</p>
<div class="inner">
<p>inner p</p>
</div>
</div>
<script>
// 生成一个新的元素对象
var newNode = document.createElement("div");
newNode.innerHTML = "新的 div";
// 给 body 添加一个新的节点子元素
document.body.appendChild(newNode);
// 移除一些元素
var box = document.getElementById("box");
var demo = document.getElementById("demo");
document.body.removeChild(box);
// 获取节点内部的子元素节点
var child1 = demo.children;
console.log(child1);
</script>
节点属性
- nodeType节点的类型,属性值为数字,表示不同的节点类型,共12种,只读:
1元素节点
2属性节点
3文本节点 - nodeName节点的名称(标签名称),只读
- nodeValue节点值,返回或设置当前节点的值。元素节点的nodevalue始终是null
<div id="box">div 1</div>
<script>
// 元素节点
var box = document.getElementById("box");
console.dir(box);
// 属性节点获取
var idNode = box.getAttributeNode("id");
console.dir(idNode);
idNode.nodeValue = "demo";
// 文本节点
var childNodes = box.childNodes;
console.log(childNodes);
childNodes[0].nodeValue = "box 1";
</script>
父子节点常用属性
- childNodes 只读属性,获取一个节点所有子节点的实时的集合,集合是动态变化的。
- children 只读属性,返回一个节点所有的子元素节点集合,是一个动态更新的HTML元素
- firstChild 只读属性,返回该节点的第一个子节点,如果该节点没有子节点则返回null。
- lastChild 只读属性,返回该节点的最后一个子节点,如果该节点没有子节点则返回null。
- parentNode 返回一个当前节点的父节点,如果没有这样的节点,比如说像这个节点是树结构的顶端或者没有插入一棵树中,这个属性返回null。
- parentElement 返回当前节点的父元素节点,如果该元素没有父节点,或者父节点不是一个DOM元素,则返回null。
<div id="box">
<p>段落</p>
<span>span 小盒子</span>
</div>
<script>
var box = document.getElementById("box");
// 获取子节点
console.log(box.childNodes); //获取所有类型的子节点
console.log(box.children); //获取所有元素类型的子节点, 常用
console.log(box.firstChild); //获取所有类型的子节点的第一个
console.log(box.lastChild); //获取所有类型的子节点的最后一个
console.log(box.lastElementChild); //获取所有元素类型的子节点的最后一个
// 获取父级
console.log(box.parentNode);// 常用
console.log(box.parentElement);
</script>
兄弟节点常用属性
- nextSibling 只读属性,返回与该节点同级的下一个节点,如果没有返回null。
- previousSibling 只读属性,返回与该节点同级的上一个节点,如果没有返回null。
- nextElementSibling只读属性,返回与该节点同级的下一个元素节点,如果没有返回null。
- previousElementSibing只读属性,返回与该节点同级的上一个元素节点,如果没有返回null。 注意:nextElementSibling和previousElementSibling有兼容性问题,IE9以后才支持。
<div id="box">
<p>这是段落1</p>
<p>这是段落2</p>
<p id="p3">这是段落3</p>
<p>这是段落4</p>
<p>这是段落5</p>
</div>
<script src="common.js"></script>
<script>
// 获取元素
var p3 = my$("p3");
// 上一个兄弟节点
console.log(p3.previousSibling);
// 下一个兄弟节点
console.log(p3.nextSibling);
// 上一个兄弟元素的节点
console.log(p3.previousElementSibling);
// 下一个兄弟元素的节点
console.log(p3.nextElementSibling);
</script>
创建新节点的方法
- document.createElement("div") 创建元素节点
- document.createAttribute("id") 创建属性节点
- document.createTextNode("hello") 创建文本节点 一般将创建的新节点存在变量中,方便使用。
// 创建新的节点
var div = document.createElement("div");
console.dir(div);
var cls = document.createAttribute("class");
console.dir(cls);
var txt = document.createTextNode("hello");
console.dir(txt);
// 创建的新的节点,是存储在内存中的,但是并没添加到 DOM 树上
节点的常用操作方法
- parentNode.appendChild(child):将一个节点添加到指定父节点的子节点列表末尾。
- parentNode.replaceChild(newChild,oldChild):用指定的节点替换当前节点的一个子节点,并返回被替换掉的节点。
- parentNode.insertBefore(newNode,referenceNode):在参考节点之前插入一个拥有指定父节点的子节点,referenceNode必须设置,如果referenceElement为null则newNode将被插入到子节点的末尾。
- parentNode.removeChild(child):移除当前节点的一个子节点。这个子节点必须存在于当前节点中。
- Node.cloneNode(false):克隆一个节点,并且可以选择是否克隆这个节点下的所有内容。参数为Boolean布尔值,表示是否采用深度克隆,如果为true,则该节点的所有后代节点也都会被克隆,如果为false,则只克隆该节点本身,默认值为true,节点下的内容会被克隆。
注意:克隆时,标签上的属性和属性值也会被复制,写在标签行内的绑定事件可以被复制,但是通过JavaScript动态绑定的事件不会被复制。 - Node.hasChildNodes()没有参数,返回一个Boolean布尔值,来表示该元素是否包含有子节点。
- Node.contains(child)返回一个Boolean布尔值,来表示传入的节点是否为该节点的后代节点。
// 创建新的节点
var div = document.createElement("div");
var cls = document.createAttribute("class");
var txt = document.createTextNode("hello");
// 创建的新的节点,是存储在内存中的,但是并没添加到 DOM 树上
// 获取元素
var box = my$("box");
var p2 = my$("p2");
box.appendChild(div);
// 文本节点也可以添加到元素内部
div.appendChild(txt);
// 注意:自己创建的元素节点本身也是一个对象,也可以去添加一些新的属性和方法,
// 这些操作将来在元素加载到 DOM 树中时,依旧保留
// DOM 中原有的节点也可以传给 appendChild 的参数
// 将节点从原始位置剪切,添加到新的指定位置
// 原因:内存中原有节点只有一个,渲染时只能有一个位置
box.appendChild(p2);
// 替换节点
box.replaceChild(div,p2);
// 在某个指定子节点之前添加一个新的子节点
box.insertBefore(div,p2);
box.insertBefore(div,null);
// 移除节点
box.removeChild(p2);
// 克隆元素 box
// 浅度克隆
var newBox = box.cloneNode(false);
// 深度克隆
var newBox = box.cloneNode(true);
newBox.id = "newBox";
document.body.appendChild(newBox);
// 判断内部有子节点的方法有三种
// 1.判断有没有子节点,不区分节点类型
console.log(box.hasChildNodes());
console.log(demo.hasChildNodes());
// 2.判断节点内部第一个子节点是否不为空
console.log(box.firstChild !== null);
console.log(demo.firstChild !== null);
// 3.判断子节点的数组对象的长度是否为0
console.log(box.childNodes.length > 0);
console.log(demo.childNodes.length > 0);
// 补充子元素节点是否存在
console.log(box.children.length > 0);
console.log(demo.children.length > 0);
// 判断节点内部是否有某个后代节点
console.log(box.contains(p2));
动态创建列表案例
<h1>动态创建列表</h1>
<div id="box">
<!-- <ul>
<li>刘备</li>
</ul> -->
</div>
<script src="common.js"></script>
<script>
// 获取元素
var box = my$("box");
// 创建一组数据
var names = ["刘备","曹操","孙权","关羽"];
// 生成 ul 元素
var ul = document.createElement("ul");
// 添加 元素 到 box内部
box.appendChild(ul);
// 根据数组的项数往 ul 中添加数据
for (var i = 0; i < names.length ; i++) {
// 每次都要生成一个新的 li 标签元素
var li = document.createElement("li");
// 添加到 ul 对象内部
ul.appendChild(li);
// 给每个生成的 li 元素添加内容
li.innerText = names[i];
}
</script>
动态创建表格案例
选择水果案例
DOM事件操作方法
addEventListener绑定事件方法
element.addEventListener()方法。
参数:
第一个参数:事件类型的字符串(直接书写"click",不需要加on)
第二个参数:事件函数
同一个元素可以多次绑定事件监听,同一个事件类型可以注册多个事件函数
兼容性问题:不支持IE9以下的浏览器
var btn = document.getElementById("btn");
// DOM0 级事件
// 绑定事件的方式
// btn.onclick = function () {
// alert(1);
// };
// 绑定多次相同的事件,只能绑定一次
// btn.onclick = function () {
// alert(2);
// };
// DOM 2 级事件绑定方式
btn.addEventListener("click",function () {
alert(1);
});
// 多次绑定相同的事件类型,事件会根据书写的顺序进行一个事件排队
btn.addEventListener("click",clickEvent);
function clickEvent() {
alert(2);
}
// 方法不支持 IE 9 以下的浏览器
</script>
addEventListener绑定事件方法
element.attachEvent()方法。
参数:
第一个参数:事件类型的字符串(需要加on)
第二个参数:事件函数
同一个元素可以多次绑定事件监听,同一个事件类型可以注册多个事件函数
兼容性问题:只支持IE10及以下的浏览器
var btn = document.getElementById("btn");
// DOM 2 级事件绑定方式
// 兼容:IE 10 及以下浏览器
// IE8 及以下的浏览器处理事件队列时,会出现顺序错乱
btn.attachEvent("onclick",function () {
alert(3);
});
btn.attachEvent("onclick",clickEvent);
function clickEvent() {
alert(4);
}
绑定/注册事件的兼容写法
自定义一个注册事件函数
参数:事件源,事件类型(不加on),事件函数
IE9及以上的浏览器,使用addEventListener方法
IE9以下的浏览器,使用attachEvent方法
判断浏览器时,不需要判断它的版本,可以检测浏览器能力
浏览器能力检测:将某个方法的调用作为if语句的判断条件,如果浏览器认识该方法返回true,否则返回false
var btn = document.getElementById("btn");
// 调用函数
addEvent(btn,"click",function () {
alert(1);
});
// DOM 2 级事件绑定方式
// 自己制作一个兼容所有浏览器的绑定事件的函数
// 参数:事件源,事件类型,事件函数
function addEvent(ele,type,fn) {
// IE 9 及以上的浏览器和其他浏览器,使用 addEventListener 方法
// IE 9 以下的浏览器,使用 attachEvent 方法
// 浏览器能力检测
if (ele.addEventListener) {
ele.addEventListener(type,fn);
} else if (ele.attachEvent) {
ele.attachEvent("on" + type,fn);
}
}
解除事件绑定的方法
- 对DOM0级绑定,直接设置element.onclick = null即可接触绑定;
- 对DOM2级绑定,使用element.removeEventListener()方法解除绑定。
参数:
第一个参数:事件类型的字符串(直接书写”click”,不需要加on)
第二个参数:事件函数引用名
注意:没有办法移除一个匿名函数,所以在注册事件时需要单独声明一个有函数名的事件函数。
兼容性问题:不支持IE9以下的浏览器 - 对DOM2级绑定,element.detachEvent()方法。
参数:
第一个参数:事件类型的字符串(需要加on)
第二个参数:事件函数
注意:没有办法移除一个匿名函数,所以在注册事件时需要单独声明一个有函数名的事件函数。
兼容性问题:只支持IEI0及以下的浏览器
var btn = document.getElementById("btn");
// 绑定事件
btn.onclick = function () {
alert(1);
};
// 解除绑定方法
btn.onclick = null;
// 绑定事件
btn.addEventListener("click",fun);
btn.addEventListener("click",fun2);
// 解除绑定
btn.removeEventListener("click",fun);
// 绑定事件
btn.attachEvent("onclick",fun);
// 解除绑定
btn.detachEvent("onclick",fun);
function fun() {
alert(2);
}
function fun2() {
alert(3);
}
解除事件绑定的兼容方法
自定义一个移除事件函数
参数:事件源,事件类型(不加on),事件函数
IE9及以上的浏览器,使用removeEventListener方法
IE9以下的浏览器,使用detachEvent方法
建议:将自己封装的一些常用函数和方法,放到一个单独的.js文件中。
var btn = document.getElementById("btn");
// 调用函数
addEvent(btn,"click",fun);
// 移除事件
removeEvent(btn,"click",fun);
function fun() {
alert(1);
}
// DOM 2 级事件绑定方式
// 自己制作一个兼容所有浏览器的绑定事件的函数
// 参数:事件源,事件类型,事件函数
function addEvent(ele,type,fn) {
// IE 9 及以上的浏览器和其他浏览器,使用 addEventListener
// IE 9 以下的浏览器,使用 attachEvent 方法
// 浏览器能力检测
if (ele.addEventListener) {
ele.addEventListener(type,fn);
} else if (ele.attachEvent) {
ele.attachEvent("on" + type,fn);
}
}
// 兼容所有浏览器的 解除绑定事件的函数
// 参数:事件源,事件类型,事件函数
function removeEvent(ele,type,fn) {
// 浏览器能力检测
if (ele.removeEventListener) {
ele.removeEventListener(type,fn);
} else if (ele.detachEvent) {
ele.detachEvent("on" + type,fn);
}
}
DOM事件流
冒泡过程:子级→父级→祖先级
捕获过程:祖先级→父级→子级
事件流的三种阶段
第一种阶段:事件捕获
第二种阶段:事件执行过程
第三种阶段:事件冒泡
addEventListener()第三个参数为false时,事件冒泡
addEventListener()第三个参数为true时,事件捕获
onclick类型:只能进行事件冒泡过程,没有捕获阶段
attachEvent()方法:只能进行事件冒泡过程,没有捕获阶段
// 获取元素
var box1 = document.getElementById("box1");
var box2 = document.getElementById("box2");
var box3 = document.getElementById("box3");
// 添加点击事件
// addEventListener 有第三个参数,用来决定事件流的方向
// 参数值是 布尔类型的值,false 表示事件冒泡过程,true 表示事件捕获过程
// 参数默认值是 false
box1.addEventListener("click",function () {
console.log(1);
},false);
box2.addEventListener("click",function () {
console.log(2);
},false);
box3.addEventListener("click",function () {
console.log(3);
},false);
box1.addEventListener("click",function () {
console.log(this.id);
},true);
box2.addEventListener("click",function () {
console.log(this.id);
},true);
box3.addEventListener("click",function () {
console.log(this.id);
},true);
// onclick 属性添加事件方法只有冒泡过程,没有捕获过程
// attachEvent() 方法添加事件方法只有冒泡过程,没有捕获过程
box1.onclick = function () {
console.log(1);
};
box2.onclick = function () {
console.log(2);
};
box3.onclick = function () {
console.log(3);
};
事件冒泡应用-事件委托
- 利用事件冒泡,将子级的事件委托给父级加载
- 同时,需要利用事件函数的一个e参数(e.target),内部存储的是事件对象
<ul id="list">
<li>刘亦菲</li>
<li>杨幂</li>
<li>唐嫣</li>
<li>赵丽颖</li>
<li>刘诗诗</li>
</ul>
<script>
// 让每个 li 被点击后,自己添加特殊的背景色,而其他兄弟不添加
// 以前的思路:获取所有的 li 标签元素,批量添加事件
// 事件委托:可以将一些子级的公共类型的事件委托给他们的父级添加
// 在父级内部想办法找到真正触发事件的最底层的事件源
// 获取元素
var list = document.getElementById("list");
var lis = list.children;
// 给 ul 添加点击事件
list.onclick = function (e) {
// 在内部要想办法找到真正触发事件的 li
// 借用事件函数内部的一个参数 e,e 是事件对象
// 只要触发事件,函数内部都可以得到一个事件对象,
// 对象中存储了关于事件的一系列数据
// e.target 属性记录的就是真正触发事件的事件源
// 排除其他
for (var i = 0 ; i < lis.length ; i++) {
lis[i].style.backgroundColor = "";
}
e.target.style.backgroundColor = "pink";
};
</script>
事件对象
只要触发事件,就会有一个对象,内部存储了与事件相关的数据。e在低版本浏览器中有兼容问题,低版本浏览器使用的是window.event
事件对象常用的属性:
e.eventPhase查看事件触发时所处的阶段
e.target用于获取触发事件的元素
e.srcElement用于获取触发事件的元素,低版本浏览器使用
e.currentTarget用于获取绑定事件的事件源元素
e.type获取事件类型
e.clientX/e.clientY所有浏览器都支持,鼠标距离浏览器窗口左上角的距离
e.pageX/e.pageY IE8以前不支持,鼠标距离整个HTML页面左上顶点的距离
<div id="box1">
<div id="box2">
<div id="box3"></div>
</div>
</div>
<script>
// 获取元素
var box1 = document.getElementById("box1");
var box2 = document.getElementById("box2");
var box3 = document.getElementById("box3");
// 添加事件
box1.onclick = function (e) {
// e指的就是存储事件对象的参数,只要事件被触发,e就会自动接收数据
// 兼容写法
e = e || window.event;
// e.eventPhase 判断出事件执行时处于哪个阶段
// 1:捕获阶段
// 2:目标阶段
// 3:冒泡阶段
// console.log(e.eventPhase);
// 获取真正触发事件的元素,兼容写法
var target = e.target || e.srcElement;
// console.log(target);
// 获取绑定事件的事件源元素
console.log(e.currentTarget);
// this 指向
console.log(this);
};
</script>
<div id="box1"></div>
<script>
// 获取元素
var box1 = document.getElementById("box1");
// e.type 属性获取事件类型
// box1.onclick = function (e) {
// // 事件对象兼容
// e = e || window.event;
// // 触发的事件类型
// console.log(e.type);
// };
// 更多时候可能给同一个元素对象添加不同的事件类型,对应执行的事件函数内部的代码不同
// box1.onmouseover = function () {
// box1.style.backgroundColor = "skyblue";
// };
// box1.onmouseout = function () {
// box1.style.backgroundColor = "yellowgreen";
// };
// 可以将 所有给一个元素绑定的事件的事件函数写在一个 函数内,通过函数内部的 e.type判断走不同的分支
box1.onmouseover = fn;
box1.onmouseout = fn;
// 避免添加多个函数,占用更多的内存
function fn(e) {
e = e || window.event;
// 根据事件类型,执行不同的代码
switch (e.type) {
case "mouseover":
box1.style.backgroundColor = "pink";
break;
case "mouseout":
box1.style.backgroundColor = "yellowgreen";
break;
}
// client系列: 客户端尺寸,点击的点参考浏览器窗口左上角的距离
console.log(e.clientX);
console.log(e.clientY);
// page 系列:html 页面尺寸,点击的点参考html文档左上角的距离
console.log(e.pageX);
console.log(e.pageY);
}
</script>
案例-图片跟随鼠标移动效果
取消默认行为和阻止事件传播的方式
e.preventDefault()取消默认行为
e.returnValue=false取消默认行为,低版本浏览器使用
e.stopPropagation(),阻止冒泡,标准方式
e.cancelBubble=true,阻止冒泡,IE低版本,标准中已废弃
<a id="link" href="52_图片跟随鼠标移动效果.html">点击</a>
<script>
var link = document.getElementById('link');
link.onclick = function (e) {
e = e || window.event;
alert('hello');
// 实际使用以下方式其中一种即可实现
// 1.普通的方式阻止默认行为
// return false;
// 2.DOM 的方法
// e.preventDefault();
// 3.低版本浏览器需要使用一个对象的属性,在高版本中也兼容
e.returnValue = false;
}
</script>
// 事件冒泡
var box1 = document.getElementById('box1');
var box2 = document.getElementById('box2');
var box3 = document.getElementById('box3');
var array = [box1, box2, box3];
for (var i = 0; i < array.length; i++) {
var box = array[i];
box.onclick = function (e) {
e = e || window.event;
console.log(this.id);
// 实际使用以下方式其中一种即可实现
// 1.阻止事件冒泡
// e.stopPropagation();
// 2.低版本浏览器使用属性
e.cancelBubble = true;
}
}
其他事件类型
MDNweb事件参考:developer.mozilla.org/zh-CN/docs/…
DOM特效
偏移量属性
偏移量属性是表示元素自身位置和大小属性
offsetParent偏移参考父级,距离自己最近的有定位的父级,如果都没有定位参考body(或html)
offsetLeft/offsetTop偏移位置
offsetWidth/offsetHeight偏移大小
<div id="box">
<div id="child">
</div>
</div>
<script>
// 获取元素
var child = document.getElementById("child");
// 元素天生就认识自己的偏移参考父级
// console.log(child.offsetParent);
// 偏移位置
// console.log(child.offsetLeft);
// console.log(child.offsetTop);
// 偏移大小
console.log(child.offsetWidth);
console.log(child.offsetHeight);
</script>
客户端大小
client系列没有参考父级元素。
clientLeft/clientTop边框区域尺寸,不常用
clientWidth/clientHeight边框内部大小
滚动偏移属性
scrollLeft/scrollTop盒子内部滚动出去的尺寸
scrollWidth/scrollHeight盒子内容的宽度和高度,
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
*{
padding: 0;
margin: 0;
}
body {
margin: 0;
}
#box {
width: 200px;
height: 200px;
margin: 50px;
border: 30px solid red;
padding: 50px;
background-color: green;
overflow: auto;
}
#box p{
width: 300px;
height: 300px;
background-color: pink;
}
</style>
</head>
<body>
<div id="box">
<p></p>
</div>
<script>
var box = document.getElementById("box");
// 滚动偏移位置和大小
// 滚动条滚动事件
box.onscroll = function () {
console.log(box.scrollLeft);
console.log(box.scrollTop);
};
// console.log(box.scrollLeft);
// console.log(box.scrollTop);
// console.log(box.scrollWidth);
// console.log(box.scrollHeight);
// console.log(box.clientWidth);
// console.log(box.offsetWidth);
</script>
</body>
</html>