10-DOM
1.DOM简介
DOM,全称Document Object Model文档对象模型。
1).JS中通过DOM来对HTML文档进行操作。只要理解了DOM就可以随心所欲的操作WEB页面。
-文档——文档表示的就是整个的HTML网页文档
-对象——对象表示将网页中的每一个部分都转换为了一个对象。
-模型——使用模型来表示对象之间的关系,这样方便我们获取对象。
2).要使用DOM来操作网页,我们需要浏览器至少得先给我一个对象才能去完成各种操作
所以浏览器已经为我们提供了一个document对象,它是一个全局变量可以直接使用document代表的是整个的网页
3).浏览器已经为我们提供文档节点对象这个对象是window属性,可以在页面中直接使用,文档节点代表的是整个网页
<button id="btn">我是一个按钮 </button>
<script type="text/javascript">
//获取到button对象
var btn = document.getElementById("btn");
//修改按钮的文字
btn.innerHTML="I'm Button";
</script>
2.节点
1).节点:
-Node——构成HTML文档最基本的单元。
-常用节点分为四类:
-文档节点:整个HTML文档
-元素节点:HTML文档中的HTML标签
-属性节点︰元素的属性
-文本节点:HTML标签中的文本内容
2). document对象
- document对象表示的是整个网页
- document对象的原型链
HTMLDocument -> Document -> Node -> EventTarget -> 0bject.prototype -> null
-凡是在原型链上存在的对象的属性和方法都可以通过Document去调用I
-部分属性:
document.documentElement --> html根元素
document.head --> head元素
document.title --> title元素
document.body --> body元素
document.links -->获取页面中所有的超链接
...
console.log(document.links);
3).获取元素节点
元素节点对象(element):
-在网页中,每一个标签都是一个元素节点
-如何获取元素节点对象?
1.通过document对象来获取元素节点
2.通过document对象来创建元素节点
-通过document来获取已有的元素节点:
document.getElementById()
-documentElement保存的是html根标签
document.documentElement;
输出是html标签中的内容
-在document中有一个属性body,它保存的是body的引用
输出是body标签中的内容
–根据id获取一个元素节点对象
document.getElementsByClassName( )
-根据元素的class属性值获取一组元素节点对象
-返回的是一个类数组对象
-该方法返回的结果是一个实时更新的集合
当网页中新添加元素时,集合也会实时的刷新
-但是该方法不支持IE8及以下的浏览器
document.getElementsByTagName( )
-根据标签名获取一组元素节点对象-返回的结果是可以实时更新的集合
- document.getElementsByTagName("*")获取页面中所有的元素
document.getElementsByName( )
-根据name属性获取一组元素节点对象-返回一个实时更新的集合
-主要用于表单项
document.querySelectorAll()
-根据选择器去页面中查询元素
-会返回一个类数组(不会实时更新)
-会将符合条件的元素封装到一个数组中返回,即使符合条件的元素只有一个,它也会返回数组
document.querySelector()
-根据选择器去页面中查询第一个符合条件的元素
-需要一个选择器的字符串作为参数,可以根据一个CSS选择器来查询一个元素节点对象
-IE8中没有getElementsByClassName()但是可以使用querySelector()代替
-使用该方法总会返回唯一的一个元素,如果满足条件的元素有多个,那么它只会返回第一个,没有就返回null
-创建一个元素节点
document.createElement( )
-根据标签名创建一个元素节点对象
-它需要一个标签名作为参数,将会根据该标签名创建元素节点对象,并将创建好的对象作为返回值返回
<button id="btn">点我一下</button>
<!-- span.s1*5 -->
<span class="s1"></span>
<span class="s1"></span>
<span class="s1"></span>
<span class="s1"></span>
<span class="s1"></span>
<script>
const span=document.getElementsByClassName("s1")
console.log(span);
for (let i = 0; i < span.length; i++) {
console.log(span[i]);
span[i].innerText="哈哈哈"+i
}
const span2=document.querySelectorAll("span")
</script>
4).元素的属性和方法
div元素的原型链:
HTMLDivElement ->HTMLElement -> Element -> Node -> ...
通过元素节点对象获取其他节点的方法:
element.childNodes 获取当前元素的子节点,包括文本节点在的所有节点,DOM标签间空白也会当成文本节点
注意:在IE8及以下的浏览器中,不会将空白文本当成子节点
element.children 获取当前元素的子元素
element.firstChild 可以获取到当前元素的第一个子节点(包括空白文本节点)
element.firstElementChild 获取当前元素的第一个子元素
不支持IE8及以下的浏览器,如果需要兼容他们尽量不要使用
element.lastElementChild 获取当前元素的最后一个子元素
element.nextElementSibling 获取当前元素的下一个兄弟元素
element.nextSibling;
element.previousElementSibling 获取当前元素的前一个兄弟元素,IE8以下不支持
element.previousSibling 也可能获取到前面的空白文本
element.parentNode 获取当前元素的父节点
element.tagName 获取当前元素的标签名
<div id="box1">
我是div
<span class="s1">我是s1</span>
<span class="s1">我是s1</span>
</div>
<span class="s1">我是s1</span>
<script>
const div=document.getElementById("box1")
console.log(div);
</script>
5).文本节点
在DOM中,网页中所有的文本内容都是文本节点对象,可以通过元素来获取其中的文本节点对象,但是通常不会这么做
我们可以直接通过元素去修改其中的文本
修改文本的三个属性:
element.textContent获取或修改元素中的文本内容
-获取的是标签中的内容,不会考虑css样式
element.innerText获取或修改元素中的文本内容
-innerText获取内容时,会考虑css样式
–通过innerText去读取CSS样式,会触发网页的重排(计算CSS样式)
-当字符串中有标签时,会自动对标签进行转义
- <li> --> <li>
element.innerHTML获取或修改元素中的html代码
-可以直接向元素中添加html代码
-innerHTML插入内容时,有被xss注入的风险
-innerHTML用于获取元素内部的HTML代码的对于自结束标签,这个属性没有意义
<div id="box1">
<span style="display:none;">我是div</span>
<span style="text-transform: uppercase;">我是div</span>
</div>
<script>
const box1=document.getElementById("box1")
console.log(box1.textContent);
box1.textContent="新的内容"
</div>
</script>
6).属性节点(Attr)
a.在DOM也是一个对象,通常不需要获取对象而是直接通过元素即可完成对其的各种操作
b.如何操作属性节点:
①.方式一:
读取:元素.属性名(注意,class属性需要使用className来读取)
读取一个布尔值时,会返回true或false
修改:元素.属性名=属性值
②.方式二:
读取:元素.getAttribute(属性名)
修改:元素.setAttribute(属性名,属性值)
删除:元素.removeAttribute(属性名)
<inrput disabled="disabled" type="text" name="username" value="admin">
<script>
const input=document.querySelector("[name=username]")
console.log(input.type);
console.log(input.getAttribute("name "));
input.setAttribute("value","孙悟空")
input.removeAttribute("disabled","disabled")
</script>
3.事件
<!--
我们可以在事件对应的属性中设置一些js代码,
这样当事件被触发时,这些代码将会执行
这种写法我们称为结构和行为耦合,不方便维护,不推荐使用
-->
<button id="btn" onmousedown="alert('我被点了')">点我一下</button>
1).事件(event)
a.事件就是用户和页面之间发生的交互行为。比如:点击按钮、鼠标移动、双击按钮、敲击键盘、松开按键…
b.可以通过为事件绑定响应函数(回调函数)),来完成和用户之间的交互
c.绑定响应函数的方式:
①.可以直接在元素的属性中设置
②.可以通过为元素的指定属性设置回调函数的形式来绑定事件
-使用对象.事件=函数的形式绑定响应函数(一个事件只能绑定一个响应函数 后面定义的方法会覆盖前面的方法)
③.可以通过元素addEventListener()方法来绑定事件
-通过这个方法也可以为元素绑定响应函数
-参数:
Ⅰ.事件的字符串,不要on
Ⅱ.回调函数,当事件触发时该函数会被调用
Ⅲ.是否在捕获阶段触发事件,需要一个布尔值,一般都传false
-使用addEventListener()可以同时为一个元素的相同事件同时绑定多个响应函数,
-这样当事件被触发时,响应函数将会按照函数的绑定顺序执行
-这个方法不支持IE8以下的浏览器(兼容性问题)
<burtton id="btn">点我一下</button>
<script>
//获取按钮对象
const btn = document.getElementById("btn")
//可以为按钮的对应事件属性绑定处理函数的形式来响应事件
//绑定一个单击事件
//像这种为单击事件绑定的函数,我们称为单击响应函数
/* btn.onclick = function () {
alert('我又被点儿')
} */
/* btn.onclick = function () {
alert('我又被点了')
} */
btn.addEventListener('click', function () {
alert('嘻嘻嘻')
})
btn.addEventListener('click', function () {
alert('哈哈哈')
})
</script>
a.兼容性问题
attachEvent( )
a.在IE8中可以使用attachEvent()来绑定事件
b.参数:
①.事件的字符串,要on
②.回调函数
c.这个方法也可以同时为一个事件绑定多个处理函数,不同的是它是后绑定先执行,执行顺序和addEventListener()相反
btn.attachEvent("onclick",function(){
alert(1);
});
/* 兼容性问题 */
//定义一个函数,用来为制定元素绑定响应函数
/*
addEventListener()中的this是绑定事件的对象
attachEvent()中的this,是window
需要统一两个方法的this
*/
/*
参数:
obj 要绑定事件的对象
eventstr 事件的字符串(不要on)
callback 回调函数
*/
function bind(obj, eventStr, callback) {
if (obj.addEventListener) {
//大部分浏览器兼容的方法
obj.addEventListener(eventStr, callback, false);
} else {
/*
this是谁由谁调用决定
callback.call(obj);
*/
//IE8以下
obj.attachEvent("on" + eventStr, function () {
//在匿名函数中调用回调函数
callback.call(obj);
});
}
}
bind(btn, "click", function () {
alert(1);
});
a.滚轮事件
<style>
#box1{
width: 300px;
height: 300px;
background-color: yellowgreen;
}
</style>
<div id="box1"></div>
<script>
//获取id为box1的div
var box1=document.getElementById("box1");
//为box1绑定一个鼠标滚轮滚动的事件,会在滚轮滚动时触发
box1.onmousewheel=function(event){
event=event||window.event;
}
</script>
onmousewheel
-鼠标滚轮滚动的事件,会在滚轮滚动时触发,但是火狐不支持该属性
-在火狐中需要使用DOMMousescroll来绑定滚动事件,注意该事件需要通过addEventListener()函数来绑定
//为火狐绑定滚轮事件
bind(box1,"DOMMousescroll",box1.onmousewheel);
function bind(obj,eventStr,callback){
if(obj.addEventListener){
//大部分浏览器兼容的方法
obj.addEventListener(eventStr,callback,false);
}else{
/*
this是谁由谁调用决定
callback.call(obj);
*/
//IE8以下
obj.attachEvent("on"+ eventStr,function(){
//在匿名函数中调用回调函数
callback.call(obj);
});
}
}
bind(box1,"DOMMousescroll",function(){
alert("滚了");
});
判断鼠标滚轮的方向
event.wheelDelta
-可以获取鼠标滚轮滚动的方向
向下滚-120
向上滚120
-wheelDelta这个值不看大小,只看正负
alert(event.wheelDelta);
wheelDelta这个属性火狐中不支持
在火狐中使用event.detail来获取滚轮滚动的方向
向上滚 -3
向下滚 3
alert(event.detail);
//当鼠标滚轮向下滚动时,box1变长,当滚轮向上滚动时,box1变短
if(event.wheelDelta>0 || event.detail<0){
//向下滚动时,box1变短
box1.style.height=box1.clientHeight-10+"px";
}else{
//向下滚动时,box1变长
box1.style.height=box1.clientHeight+10+"px";
}
用addEventListener()方法绑定响应函数,取消默认行为时不能使用return false
需要使用event来取消默认行为event.preventDefault();
但是IE8不支持event.preventDefault();,如果直接调用会报错
event.preventDefault && event.preventDefault();
/*
当滚轮滚动时,如果浏览器有滚动条,滚动条会随之滚动,
这是浏览器的默认行为,如果不希望发生,则可以取消默认行为
return false;
*/
b.键盘事件
onkeydown
-按键被按下
-对于onkeydown来说如果一直按着某个按键不松手,则事件会一直触发
-当onkeydown连续触发时,第一次和第二次之间会间隔稍微长一点,其他的会非常的快
这种设计是为了防止误操作的发生。|
onkeyup
-按键被松开
键盘事件一般都会绑定给一些可以获取到焦点的对象或者是document
document.onkeydown=function(event){
event=event||window.event;
/*
可以通过keyCode来获取按键的编码
可以判断哪个按键被按下
除了keycode,事件对象中还提供了几个属性
altKey
ctrlKey
shiftKey
-这个三个用来判断alt ctrl 和shift是否被按下
如果按下则返回true,否则返回false
*/
if(event.keyCode===89 && event.ctrlKey){
alert("y和ctrl被按下");
}
console.log(event.keyCode);
console.log(event.key);
}
//获取input
var input=document.getElementsByTagName("input")[0];
input.onkeydown=function(event){
event=event||window.event;
//数字是48-57
//使文本框不能输入数字
console.log(event.keyCode);
if(event.keyCode>=97 && event.keyCode<=105){
return false;
}
//console.log("按键被按下");
//在文本框中输入内容,属于onkeydown的默认行为
//如果在onkeydown中取消了默认行为,则输入的内容,不会出现在文本框中
return false;
}
2).文档的加载
网页是自上向下加载的,如果将js代码编写到网页的上边,
JS代码在执行时,网页还没有加载完毕,这时会出现无法获取到DOM对象的情况
window.onload 事件会在窗口中的内容加载完毕之后才触发
document的DOMContentLoaded事件会在当前文档加载完毕之后触发
如何解决这个问题:
1.将script标签编写到body的最后(*****)
2.将代码编写到window.onload的回调函数中
window.onload = function () {
var btn = document.getElementById("btn");
btn.onclick = function () {
alert("hello");
}
}
window.addEventListener("load",function(){
var btn = document.getElementById("btn");
btn.onclick = function () {
alert("hello");
}
})
3.将代码编写到document对象的DOMContentLoaded的回调函数中(执行时机更早)
document.addEventListener("DOMContentLoaded",function(){
var btn = document.getElementById("btn");
btn.onclick = function () {
alert("hello");
}
})
4.将代码编写到外部的js文件中,然后以defer的形式进行引入(执行时机更早,早于DOMContentLoaded)(*****)
<script defer src="/JS/script.js"></script>
注意:
for循环会在页面加载完成之后立即执行,而响应函数会在超链接被点击时才执行当响应函数执行时,for循环早已执行完毕
4.元素的修改
<button id="btn1">点我一下1</button>
<button id="btn2">点我一下2</button>
<hr>
<ul id="list">
<li id="swk">孙悟空</li>
<li>猪八戒</li>
<li>沙悟净</li>
</url>
<script>
const btn1=document.getElementById("btn1")
const btn2=document.getElementById("btn2")
const list=document.getElementById("list")
btn1.addEventListener('click',function(){
const name="omg"
const li=document.createElement("li")
li.textContent="唐僧"
li.id="ts"
})
btn2.addEventListener('click',function(){
const li=document.createElement("li")
li.textContent="蜘蛛精"
li.id="zzj"
//获取swk
const swk=document.getElementById("swk")
//remove()用来删除当前元素
swk.remove()
})
</script>
①.appendChild()
用于给一个节点添加子节点
list.appendChild(li)
②.insertAdjacentElement()
-可以向元素的任何位置添加元素
-两个参数:1.要添加的位置2.要添加的元素
beforeend 标签的最后
afterbegin 标签的开始
beforebegin 在元素的前边插入元素(兄弟元素)
afterend 在元素的后边插入元素(兄弟元素)
list.insertAdjacentElement("beforebegin",li)
list.insertAdjacentHTML("beforeend","<li id="+name+">白骨精</li>")
insertBefore()
-可以在指定的子节点前插入新的子节点
语法:
父节点.insertBefore(新节点,旧节点);
③.replaceWith()
使用一个元素替换当前元素
swk.replaceWith(li)
replaceChild()
-可以使用指定的子节点替换已有的子节点
语法:
父节点.replaceChild(新节点,旧节点);
④.remove()
用来删除当前元素
swk.remove()
removeChild()
-可以删除一个子节点
语法:
父节点.removeChild(子节点);
子节点.parentNode.removeChild(子节点);
5.节点的复制
-使用cloneNode() 方法对节点进行复制时,它会复制节点的所有特点包括各种属性
-这个方法默认只会复制当前节点,而不会复制节点的子节点
-可以传递一个true作为参数, 这样该方法也会将元素的子节点一起复制
<button id="btn01">点我一下</button>
<ul id="list1">
<li id="l1">孙悟空</li>
<li id="l2">猪八戒</li>
<li id="l3">沙和尚</li>
</ul>
<ul id="list2">
<li>蜘蛛精</li>
</ul>
<script>
const list2=document.getElementById("list2")
const l1=document.getElementById("l1")
const btn01=document.getElementById("btn01")
btn01.onclick=function(){
// const newl1=l1.cloneNode()//用来对节点进行复制
const newl1=l1.cloneNode(true)
newl1.id="newl1"
list2.appendChild(newl1)
}
</script>
6.操作CSS样式
1).修改CSS样式
通过JS修改元素的样式:
通过style属性设置和读取的都是内联样式,无法读取样式表中的样式
语法:
元素.style.样式名=样式值
注意:
如果CSS的样式名中含有-,这种名称在JS中是不合法的比如background-color
需要将这种样式名修改为驼峰命名法,去掉-,然后将-后的字母大写
我们通过style属性设置的样式都是内联桂式,而内联样式有较高的优先级,所以通过JS修改的样式往往会立即显示
但是如果在样式中写了!important,则此时样式会有最高的优先级,
即使通过JS也不能覆盖该样式,此时将会导致JS修改样式失效,所以尽量不要为样式添加 !importantl
<style type="text/css">
#box1{
width: 200px;
height: 200px;
background-color: red;
}
</style>
<div id="box1"></div>
<button id="btn1">点我一下</button>
<script>
//获取box1
var box1=document.getElementById("box1");
//为按钮绑定单击响应事件
var btn1=document.getElementById("btn1");
btn1.onclick=function(){
box1.style.backgroundColor="yellow";
}
</script>
2).读取CSS样式
<style type="text/css">
#box1{
width: 200px;
height: 200px;
background-color: red;
}
#box1::before{
color: black;
}
</style>
<div id="box1"></div>
<button id="btn1">点我一下</button>
<script>
//点击按钮以后读取box1的样式
//获取box1
var box1=document.getElementById("box1");
//为按钮绑定单击响应事件
var btn1=document.getElementById("btn1");
btn1.onclick=function(){
}
</script>
获取元素的当前显示的样式:
语法:
元素.currentStyle.样式名
它可以用来读取当前元素正在显示的样式
如果当前元素没有设置该样式,则获取它的默认值
currentstyle只有IE浏览器支持,其他的浏览器都不支持
alert(box1.currentStyle.width); //200
在其他浏览器中可以使用
getComputedstyle()
-这个方法来获取元素当前的样式
-会返回一个一个对象,这个对象中包含了当前元素所有的生效的样式
-这个方法是window的方法,可以直接使用
-需要两个参数
第一个:要获取样式的对象
第二个:可以传递一个伪元素,一般都传null
-该方法会返回一个对象,对象中封装了当前元素对应的样式
可以通过对象.样式名来读取样式
如果获取的样式没有设置,则会获取到真实的值,而不是默认值
样式对象中返回的样式值,不一定能来拿来直接计算
所以使用时,一定要确保值是可以计算的才去计算
比如:没有设置width,它不会获取到auto,而是一个长度
-但是该方法不支持IE8及其以下的浏览器
alert((getComputedStyle(box1,null)).width);
alert((getComputedStyle(box1,"::before")).color);
alert(getStyle(box1,"width")); //200
通过currentStyle和getComputedStyle()读取到的样式都是只读的,不能修改,如果要修改必须通过style属性
定义一个函数,用来获取指定元素的当前的样式:
参数:
obj 要获取样式的元素
name 要获取的样式名
function getStyle(obj,name){
if(window.getComputedStyle){
//正常浏览器,具有getComputedStyle()方法
return getComputedStyle(obj,null)[name];
}else{
//IE8的方式,没有getComputedStyle()方法
return ob.currentStyle[name];
}
}
3).通过属性读取样式
<style>
#box1{
width: 200px;
height: 200px;
background-color: red;
padding: 10px;
border: 10px solid yellow;
}
#box2{
background-color: #bfa;
padding: 100px;
}
#box4{
width: 200px;
height: 300px;
background-color: #bfa;
overflow: auto;
}
#box5{
width: 600px;
height: 600px;
background-color: yellow;
}
</style>
<button id="btn1">点我一下</button>
<div id="box4">
<div id="box5"></div>
</div>
<br><br>
<div id="box3" style="position:relative;">
<div id="box2" style="position:relative;">
<div id="box1"></div>
</div>
</div>
<script>
var box1=document.getElementById("box1");
var btn1=document.getElementById("btn1");
btn1.onclick=function(){
}
</script>
①.clientwidth 和 clientHeight
-这两个属性可以获取元素的可见宽度和高度
获取元素内部的宽度和高度(包括内容区和内边距)
-这些属性都是不带px的,返回都是一个数字,可以直接进行计算
-会获取元素宽度和高度,包括内容区和内边距
-这些属性都是只读的,不能修改
alert(box1.clientWidth); //220
②.offsetwidth 和 offsetHeight
-获取元素的整个的宽度和高度,包括内容区、内边距和边框
alert(box1.offsetWidth); //220
③.offsetParent
-可以用来获取当前元素的定位父元素
-会获取到离当前元素最近的开启了定位的祖先元素
如果所有的祖先元素都没有开启定位,则返回body
alert(box1.offsetParent.id); //box2
④.offsetLeft 和 offsetTop
offsetLeft
-当前元素相对于其定位父元素的水平偏移量
offsetTop
-当前元素相对于其定位父元素的垂直偏移量
alert(box1.offsetLeft); //100
⑤.scrollHeight 和 scrollWidth
元素. scrollHeight
元素. scrollWidth
-获取元素滚动区域的大小
var box4=document.getElementById("box4");
alert(box4.clientHeight); //283
alert(box4.scrollHeight); //600
⑥.scrollLeft 和 scrollTop
scrollLeft
-可以获取水平滚动条滚动的距离
scrollTop
-可以获取垂直滚动条滚动的距离
alert(box4.scrollLeft);
alert(box4.scrollTop);
⑦.相等关系
当满足scrollHeight - scrollTop == clientHeight
说明垂直滚动条滚动到底了
当满足scrollwidth - scrollLeft == clientwidth
说明水平滚动条滚动到底
alert(box4.clientHeight); //283
alert(box4.scrollHeight-box4.scrollTop); //600
4).操作Class修改样式
<style>
.box1{
width: 400px;
height: 400px;
background-color: tomato;
}
.box2{
background-color: yellow;
}
</style>
<button id="btn01">点击</button>
<hr>
<div class="box1"></div>
<script>
const btn01=document.getElementById("btn01")
const box1=document.querySelector(".box1")
btn01.onclick=function(){
}
</script>
除了直接修改样式外,也可以通过修改class属性来间接的修改样式
-通过class修改样式的好处:
①.可以一次性修改多个样式
②.对JS和CSS进行解耦
box1.className += " box2"
元素.classList是一个对象,对象中提供了对当前元素的类的各种操作方法
元素.classList.add() 向元素中添加一个或多个class
元素.classList. remove() 移除元素中的一个或多个class
元素.classList. toggle() 切换元素中的class
元素.classList.replace() 替换class
元素.classList. contains() 检查lclass
box1.classList.add("box2")
console.log(box1.classList.contains("box2"));
7.事件对象
<style>
#areaDiv{
border: 2px solid black;
width: 400px;
height: 100px;
}
#showMsg{
border: 2px solid black;
width: 400px;
height: 50px;
}
</style>
<div id="box1"></div>
<div id="areaDiv"></div>
<br>
<div id="showMsg"></div>
event事件——事件对象
-事件对象是有浏览器在事件触发时所创建的对象,这个对象中封装了事件相关的各种信息
-通过事件对象可以获取到事件的详细信息,比如:鼠标的坐标、键盘的按键…
-浏览器在创建事件对象后,会将事件对象作为响应函数的参数传递,所以我们可以在事件的回调函数中定义一个形参来接收事件对象
-当事件的响应函数被触发时,浏览器每次都会将一个事件对象作为实参传递进响应函数,
在事件对象中封装了当前事件相关的一切信息,比如:鼠标的坐标键盘哪个按键被按下鼠标滚轮滚动的方向…
//获取两个div
var areaDiv=document.getElementById("areaDiv");
var showMsg=document.getElementById("showMsg");
areaDiv.onmousemove=function(event){
/*
在IE8中,响应函数被触发时,浏览器不会传递事件对象,
在IE8及以下的浏览器中,是将事件对象作为window对象的属性保存的
*/
//解决事件对象的兼容性问题
/* if(!event){
event=window.event;
} */
event=event||window.event;
/*
clientX可以获取鼠标指针的水平坐标
cilentY可以获取鼠标指针的垂直坐标
*/
var x=event.clientX;
var y=event.clientY;
//alert("我动了");
//在showMsg中显示鼠标的坐标
showMsg.innerHTML="x="+x+"y="+y;
}
}
8.Event对象
<style>
#box1{
width: 300px;
height: 300px;
background-color: #bfa;
}
#box2{
width: 200px;
height: 200px;
background-color: yellowgreen;
}
#box3{
width: 100px;
height: 100px;
background-color: tomato;
}
</style>
<div id="box1">
<div id="box2">
<div id="box3"></div>
</div>
</div>
<a id="chao" href="https://baidu.com">超链接</a>
在DOM中存在着多种不同类型的事件对象
-多种事件对象有一个共同的祖先 Event
- event.targetl 触发事件的对象
- event.currentTarget 绑定事件的对象(同this)
- event.stopPropagation() 停止事件的传导
- event.preventDefault( ) 取消默认行为
-event.cancelBubble 将事件对象cancelBubble设置为true,即可取消冒泡
const box1=document.getElementById("box1")
const box2=document.getElementById("box2")
const box3=document.getElementById("box3")
const chao=document.getElementById("chao")
chao.addEventListener("click",(event)=>{
event.preventDefault() //取消默认行为
alert("被点了");
/* return false; */
})
在事件的响应函数中:
event.target表示的是触发事件的对象(触发事件的对象不一定是box1,box2和box3一样可以触发)
this 表示绑定事件的对象
box1.addEventListener("click",function(event){
console.log(event.target);
console.log(this );
console.log("Hello 我是box1");
})
box2.addEventListener("click",function(event){
alert("我是box2")
})
box3.addEventListener("click",function(event){
event.stopPropagation()//取消事件的冒泡传递
alert("你好 我是box3")
})
点击box1:
点击box2:
点击box3:
9.事件的发生
1).事件的冒泡(bubble)
-事件的冒泡就是指事件的向上传到
-当元素上的某个事件被触发后,其祖先元素上的相同事件也会同时被触发
-冒泡的存在大大的简化了代码的编写,但是在一些场景下我们并不希望冒泡存在,不希望事件冒泡时,可以通过事件对象来取消冒泡
<div id="circle"></div>
<div id="box1"></div>
<!-- 事件的冒泡和元素的样式无关,之和结构相关 -->
<div id="box2" onclick="alert(2)">
<div id="box3" onclick="alert(3)"></div>
</div>
<script>
/* 使小绿球跟随鼠标移动 */
const circle=document.getElementById("circle")
const box1=document.getElementById("box1")
//事件需要绑定给document,绑定给小绿球太小了
document.addEventListener("mousemove",(event)=>{
circle.style.left=event.x+"px"
circle.style.top=event.y+"px"
})
box1.addEventListener("mousemove",(event)=>{
event.stopPropagation() //取消事件的传导
})
</script>
2).事件的委派
-指将事件统一绑定给元素的共同的祖先元素,这样当后代元素上的事件触发时,会一直冒泡到祖先元素
从而通过祖先元素的响应函数来处理事件。
-事件委派是利用了冒泡,通过委派可以减少事件绑定的次数,提高程序的性能
-委派就是将本该绑定给多个元素的事件,统一绑定给document,这样可以降低代码复杂度方便维护
<button id="btn">点击</button>
<hr>
<ul id="list">
<li><a href="javasctript:;">超链接一</a></li>
<li><a href="javasctript:;">超链接二</a></li>
<li><a href="javasctript:;">超链接三</a></li>
<li><a href="javasctript:;">超链接四</a></li>
</ul>
<script>
var u1=document.getElementById("u1");
//点击按钮后添加超链接
var btn01=document.getElementById("btn01");
btn01.onclick=function(){
//创建一个li
var li=document.createElement("li");
li.innerHTML="<a href='#' class='link'>超链接一</a>";
u1.appendChild(li);
}
/*
*/
//获取所有的a
var allA=document.getElementsByTagName("a");
//遍历
/* for(var i=0;i<allA.length;i++){
allA[i].οnclick=function(){
};
} */
</script>
为每一个超链接都绑定一个单击响应函数,这里我们为每一个超链接都绑定了一个单击响应函数,
这种操作比较麻烦,而且这些操作只能为已有的超链接设置事件,而新添加的超链接必须重新绑定新建一个子元素超链接便遍历一遍元素数组的话会影响程序的性能
思路:
可以将事件统一绑定给document,这样点击超链接时由于事件的冒泡,
会导致document上的点击事件被触发,这样只绑定一次,所有的超链接都会具有这些事件
const links=document.getElementsByTagName("a")
const list=document.getElementById("list")
const btn=document.getElementById("btn")
//只绑定一次事件,既可以让所有的超链接,包括当前的和未来新建的超链接都具有这些事件
document.addEventListener("click",(event)=>{
//console.log(Array.from(links));
if ([...links].includes(event.target)) {
alert(event.target.textContent)
}
})
//点击按钮后,在UI中添加一个新的li
btn.addEventListener("click",()=>{
list.insertAdjacentHTML("beforeend","<li><a href='javasctript:;'>新的超链接</a></li>")
})
3).事件的捕获
a.事件的捕获,指事件从外向内的传导,当前元素触发事件以后,会先从当前元素最大的祖先元素开始向当前元素进行事件的捕获
b.关于事件的传播网景公司和微软公司有不同的理解:
①.微软公司认为事件应该是由内向外传播,也就是当事件触发时,应该先触发当前元素上的事件,
然后再向当前元素的祖先元素上传播,也就说事件应该在冒泡阶段执行。
②.网景公司认为事件应该是由外向内传播的,也就是当前事件触发时,应该先触发当前元素的最外层的祖先元素的事件,
然后在向内传播给后代元素
c.W3C综合了两个公司的方案,将事件传播分成了三个阶段
①.捕获阶段——在捕获阶段时从最外层的祖先元素,向目标元素进行事件的捕获,但是默认此时不会触发事件
②.目标阶段——事件捕获到目标元素,捕获结束开始在目标元素上触发事件
③.冒泡阶段——事件从目标元素向他的祖先元素传递,依次触发祖先元素上的事件
d.如果希望在捕获阶段就触发事件,可以将addEventListener()的第三个参数设置为true,
一般情况下我们不会希望在捕获阶段触发事件,所以这个参数一般都是false
e.IE8及以下的浏览器中没有捕获阶段
<div id="box1">
<div id="box2">
<div id="box3"></div>
</div>
</div>
<script>
const box1=document.getElementById("box1")
const box2=document.getElementById("box2")
const box3=document.getElementById("box3")
box1.addEventListener("click",(event)=>{
//alert(1)
alert("1"+event.eventPhase)
//eventPhase 表示事件触发的阶段
//返回的是一个数值:1捕获阶段 2目标阶段 3冒泡阶段
})
box2.addEventListener("click",(event)=>{
//alert(2)
alert("2"+event.eventPhase)
})
box3.addEventListener("click",(event)=>{
//alert(3)
alert("3"+event.eventPhase)
})
</script>
10.项目练习
1).练习一:
学生信息添加
<div class="info">
<h1>学生信息添加</h1>
<div class="up">
<!-- autocomplete="off"取消文本框提示已输入过得文字 -->
<!-- placeholder=""设置文本框中灰色的提示文字 -->
<label for="姓名">
姓名:
<input type="text" value="" id="name" placeholder="请输入名字" autocomplete="off" />
</label>
<label for="">
性别:
<input type="text" value="" id="sex" placeholder="请输入性别" autocomplete="off" />
</label>
<label for="">
年龄:
<input type="number" value="" id="age" placeholder="请输入年龄" autocomplete="off" />
</label>
<label for="">
邮箱:
<input type="email" value="" id="email" placeholder="请输入邮箱" autocomplete="off" />
</label>
<input type="button" value="添加" id="add" />
</div>
<table>
<thead>
<tr>
<th>姓名</th>
<th>性别</th>
<th>年龄</th>
<th>邮箱</th>
<th>操作</th>
</tr>
</thead>
<tbody id="tbody">
<tr>
<td>孙悟空</td>
<td>男</td>
<td>26</td>
<td>swk@qq.com</td>
<td><a href="#">删除</a></td>
</tr>
<tr>
<td>猪八戒</td>
<td>男</td>
<td>28</td>
<td>zbj@qq.com</td>
<td><a href="#">删除</a></td>
</tr>
<tr>
<td>沙悟净</td>
<td>男</td>
<td>29</td>
<td>swj@qq.com</td>
<td><a href="#">删除</a></td>
</tr>
</tbody>
</table>
</div>
<script type="text/javascript">
const add = document.getElementById('add');
const tbody = document.getElementById('tbody');
// 添加按钮的点击事件
add.onclick = function () {
// 取输入文本框的值
const name = document.getElementById('name').value;
const sex = document.getElementById('sex').value;
const age = document.getElementById('age').value;
const email = document.getElementById('email').value;
//这种写法容易被xss攻击
/* tbody.insertAdjacentHTML("beforeend",
`
<tr>
<td>${name}</td>
<td>${sex}</td>
<td>${age}</td>
<td>${email}</td>
<td><a href="javascript:;">删除</a></td>
</tr>
`
) */
//方法二:
//创建元素
const tr=document.createElement("tr")
//创建td
const nameTd=document.createElement("td")
const sexTd=document.createElement("td")
const ageTd=document.createElement("td")
const emailTd=document.createElement("td")
//添加文本
nameTd.textContent=name
sexTd.textContent=sex
ageTd.textContent=age
emailTd.textContent=email
//将三个td添加到tr中
tr.appendChild(nameTd)
tr.appendChild(sexTd)
tr.appendChild(ageTd)
tr.appendChild(emailTd)
tr.insertAdjacentHTML("beforeend",'<td><a href="javascript:;">删除</a></td>')
tbody.appendChild(tr)
//由于上边的超链接是新添加的,所以它的上边并没有绑定单级响应函数,所以新添加的员工无法删除
//解决方式:为新添加的超链接单独绑定响应函数
links[links.length-1].onclick=delEmpHandler
// 点击添加后,清空输入框的内容
document.getElementById("name").value = "";
document.getElementById("sex").value = "";
document.getElementById("age").value = "";
document.getElementById("email").value = "";
}
// 获取所有的超链接
const links = document.links
//删除数据函数
function delEmpHandler(){
const tr=this.parentNode.parentNode
//获取要删除的员工的姓名
//const empName=tr.getElementByTagName("td")[0].textContent
const empName=tr.firstElementChild.textContent
//弹出提示
if (confirm("你确定要删除【"+empName+"】这条数据吗?")) {
//删除tr
tr.remove()
}
//本练习中的超链接,我们是不希望发生跳转,但是跳转行为是超链接的默认行为
//只要点击超链接就会触发页面的跳转,事件中可以通过取消默认行为来阻止超链接的跳转
//使用return false来取消默认行为,只在xxX.xXx = function()丹这种形式绑定的事件中才适用
//href:"javascript:;"
return false
}
//为其绑定单击响应函数
for (let i = 0; i < links.length; i++) {
links[i].onclick=delEmpHandler
}
</script>
2).练习二:
全选效果
<form action="">
<p>你的爱好是?</p>
<input type="checkbox" id="qx_qbx">全选/全不选<br>
<input type="checkbox" name="items" value="足球">足球<br>
<input type="checkbox" name="items" value="篮球">篮球<br>
<input type="checkbox" name="items" value="羽毛球">羽毛球<br>
<input type="checkbox" name="items" value="乒乓球">乒乓球<br>
<input type="button" id="qx" value="全选">
<input type="button" id="qbx" value="全不选">
<input type="button" id="fx" value="反选">
<input type="button" id="tj" value="提交">
</form>
<script type="text/javascript">
var qx=document.getElementById("qx");
var items=document.getElementsByName("items");
//获取全选/全不选的多选框
var qx_qbx=document.getElementById("qx_qbx");
var qx=document.getElementById("qx");
qx.onclick=function(){
for(var i=0;i<items.length;i++){
items[i].checked=true;
}
//将qx_qbx设置为选中状态
qx_qbx.checked=true;
};
var qbx=document.getElementById("qbx");
qbx.onclick=function(){
for(var i=0;i<items.length;i++){
items[i].checked=false;
}
//将qx_qbx设置为选中状态
qx_qbx.checked=false;
};
var fx=document.getElementById("fx");
fx.onclick=function(){
//将qx_qbx设置为选中状态
qx_qbx.checked=true;
for(var i=0;i<items.length;i++){
items[i].checked=!items[i].checked;
//在反选时也需要判断是否全都选中
//判断四个多选框是否全选
//只要有一个没选中则就不是全选
if(!items[i].checked){
//一旦进入判断,则证明不是全选状态
//将qx_qbx设置为没选中状态
qx_qbx.checked=false;
}
}
};
var tj=document.getElementById("tj");
tj.onclick=function(){
for(var i=0;i<items.length;i++){
if(items[i].checked){
alert(items[i].value);
}
}
};
qx_qbx.onclick=function(){
for(var i=0;i<items.length;i++){
items[i].checked=this.checked;
}
};
//为四个多选框分别绑定点击响应函数
for(var i=0;i<items.length;i++){
items[i].onclick=function(){
//将qx_qbx设置为选中状态
qx_qbx.checked=true;
for(var j=0;j<items.length;j++){
//判断四个多选框是否全选
//只要有一个没选中则就不是全选
if(!items[j].checked){
//一旦进入判断,则证明不是全选状态
//将qx_qbx设置为没选中状态
qx_qbx.checked=false;
//一旦进入判断,则已经得出结果,不用再继续执行循环
break;
}
}
}
};
</script>
3).练习三:
图片切换
<style type="text/css">
*{
margin: 0;
padding: 0;
}
#outer{
width: 500px;
margin: 50px auto;
padding: 10px;
background-color: aqua;
text-align:center ;
}
#outer img{
width: 500px;
}
</style>
<script type="text/javascript">
window.onload=function(){
//点击按钮切换图片
//要切换图片就是要修改标签的src属性
//获取img标签
var img=document.getElementsByTagName("img")[0];
//创建一个数组,用来保存图片的路径
var imgArr=["../img/1.jpg","../img/2.jpg","../img/3.jpg","../img/4.jpg","../img/5.jpg"];
//创建一个变量,来保存当前正在显示的图片的索引
var index=0;
//获取id为info的p元素
var info=document.getElementById("info");
//设置提示文字
info.innerHTML="一共"+imgArr.length+"张图片,当前是第"+(index+1)+"张";
//获取两个按钮
var prev=document.getElementById("prev");
var next=document.getElementById("next");
//分别为两个按钮绑定单击响应函数
prev.onclick=function(){
index--;
if(index<0){
index=imgArr.length-1;
}
img.src=imgArr[index];
info.innerHTML="一共"+imgArr.length+"张图片,当前是第"+(index+1)+"张";
};
next.onclick=function(){
index++;
if(index>imgArr.length-1){
index=0;
}
img.src=imgArr[index];
info.innerHTML="一共"+imgArr.length+"张图片,当前是第"+(index+1)+"张";
};
}
</script>
<p id="info"></p>
<div id="outer">
<img src="../img/1.jpg" alt="m1">
<button id="prev">上一张</button>
<button id="next">下一张</button>
</div>