DOM
浏览器已经为我们提供文档节点对象,这个对象是window属性,可以在页面中直接使用,文档节点代表的是整个页面
document.getElementById("");
事件就是用户和浏览器之间的交互行为,如:点击按钮、鼠标移动、关闭窗口
我们可以在事件对应的属性中设置一些js代码,这样当事件被触法时,这些代码将会执行
这种写法我们称为结构和行为耦合,不方便维护,不推荐使用
<button onclick="alert('弹一弹');">我是一个按钮</button>
可以为按钮的对应事件绑定处理函数的形式来响应事件,这样当事件被触发时,其对应的函数将会被调用
像这种单击事件绑定的函数,我们称为单击响应函数
<button id="btn" onclick="alert('弹一弹');">我是一个按钮</button>
<script type="text/javascript">
var btn = document.getElementById("btn"); //获取按钮对象
//绑定一个单击事件
btn.onclick = function () {
alert("点我干嘛")
};
</script>
浏览器在加载一个页面时,是按照自上向下的顺序加载的,读取到一行就运行一行,如果将script标签写到页面的上面,在代码执行时,页面还没有加载,DOM对象也没有加载,会导致无法获取到DOM对象
将js代码编写到页面的下部就是为了可以在页面加载完毕以后再执行js代码
onload事件会在整个页面加载完成之后才触发
该事件对应的响应函数将会在页面加载完成之后执行,这样可以确保我们的代码执行时所以的DOM对象已经加载完毕了
为window绑定onload事件
window.onload = function (){
};
innerHTML 通过这个属性可以获取到元素内部的html代码
对于自结束标签,这个属性没有意义(没有内部)
getElementsByTagName() 这个方法会给我们返回一个类数组,所以查询到的元素都会封装到对象中,即使查询到的元素只有一个,也会封装到数组中返回
getElementsByName()
如果需要读取元素节点的属性,直接使用 元素. 属性名,例如:元素.id、元素.name、元素.value
注意:class属性不能采用这种方式,读取class属性时需要使用 元素.className
练习:图库训练
E:\web\basic\pic
childNodes属性会获取包括文本节点在内的所有节点
根据DOM标准,标签间空白也会当成文本节点
注意:在IE8及以下的浏览器中,不会将空白文本当成子节点
children属性可以获取当前元素的所有子元素(推荐使用)
子节点与子元素不同,子节点会包括空白节点(Text)
firstElementChild获取当前元素的第一个子元素。该属性不支持IE8及以下的浏览器,如果需要兼容,尽量不要使用
函数也可以作为一个函数的参数
innerText该属性可以获取到元素内部的文本内容
与innerHTML类似,不同的是它会自动将html标签去除
pre.viousElenmntSibling获取前一个兄弟元素,不包括文本节点。IE8及以下不支持
文本框的value属性值,就是文本框中填写的内容
DOM查询笔记不方便做,对于各个属性的用法可以两倍速过一下
练习:全选练习
E:\web\basic\select
checkedAllBox单选框写错了,复习时可多注意下
通过多选框的checked属性可以来获取或设置多选框的选中状态
在事件的响应函数中,响应函数是给谁绑定的this就是谁
var checkedAllBox=document.getElementById("checkedAllBox");
checkedAllBox.onclick=function(){
for(var i=0;i<items.length;i++){
items[i].checked=this.checked;
}
};
未考虑这几种情况
如果四个多选框全都选中,则checkedAllBox也应该选中
如果四个多选框没有都选中,则checkedAllBox也不应该选中
所以需要遍历四个球
//为四个多选框分别绑定点击响应函数
for(var i=0;i<items.length;i++){
items[i].onclick=function(){
//将checkedAllBox设置为选中状态
checkedAllBox.checked=true;
//判断四个多选框是否全选
for(var j=0;j<items.length;j++){
if(!items[j].checked){
checkedAllBox.checked=false;
//一旦进入判断,则已经得出结果,不用再继续执行循环,提升性能
break;
}
}
};
};
注意:需要将此代码放在window.onload里面才可执行该遍历
//点击全选btn时,全选/全不选box也应该被选中,所以应该在checkedAllBtn中添加
checkedAllBox.checked = true;
//点击全不选btn时,全选/全不选box也应该不选中,所以应该在checkedNoBtn中添加
checkedAllBox.checked = false;
//在反选时也需要判断四个多选框是否全都选中,最简单的方法,将判断每个单选框是否被选中代码复制到checkedResBtn中
checkedAllBox.checked = true;
//判断四个多选框是否全选
for (var j = 0; j < items.length; j++) {
if (!items[j].checked) {
checkedAllBox.checked = false;
//一旦进入判断,则已经得出结果,不用再继续执行循环,提升性能
break;
}
}
//checkedERevBtn中已经有一个for循环,所以可以直接在这个for循环里判断其他各个多选框的选中状态
checkedRevBtn.onclick = function () {
//将checkedAllBox设置为选中状态
checkedAllBox.checked = true;
for (var i = 0; i < items.length; i++) {
/* if (items[i].checked) {
items[i].checked = false;
} else {
items[i].checked = true;
}
*/
items[i].checked = !items[i].checked;
if (!items[i].checked) {
checkedAllBox.checked = false;
//一旦进入判断,则已经得出结果,不用再继续执行循环,提升性能
}
}
};
在document中有一个属性body,它保存的是body的引用
var body = document . body ;
document.documentElement保存的是html标签
document.all 代表页面中所有的元素
document.getElementByTagName(" * "); 与all效果相同
getElementByClassName(" “) 根据元素的class属性值查询一组元素节点对象,但是不支持IE8及以下浏览器。但是可以使用querySelector代替
querySelector(” ") 需要一个选择器的字符串作为参数,可以根据一个CSS选择器来查询一个元素节点对象,如 ( " .box1 div “)
使用该方法总会返回唯一的一个元素,如果满足条件的元素有多个,那么它只会返回第一个
querySelectorAll(” " ) 该方法与querySelector()用法类似,不同的是它会将符合条件的元素封装到一个数组中返回
即使符合条件的元素只有一个,它也会返回数组
createElement(" “) 可以用于创建一个元素节点对象,它需要一个标签名作为参数,将会根据该标签名创建元素节点对象,并将创建好的对象作为返回值返回
createTextNode(” “) 可以用来创建一个文本节点对象,需要一个文本内容作为参数,将会根据内容创建文本节点,并将新的节点返回
appendChild() 向一个父节点中添加一个新的子节点
用法:父节点 . appendChild (子节点) ;
insertBefore() 可以在指定的子节点前插入新的子节点
语法: 父节点 . insertBefore( 新节点 , 旧节点 );
replaceChild() 可以使用指定的子节点替换已有的子节点
语法: 父节点 . replaceChild ( 新节点 , 旧节点 );
removeChild() 可以删除一个子节点
语法: 父节点 . removeChild( 子节点 );
更方便的写法:子节点 . parentNode . removeChild( 子节点 ); 这样就不用单独去获取父节点
使用innerHTML也可以完成DOM的增删改查的相关操作,一般我们会两种方式结合使用
var li = document.createElement(“li”);
li.innerHTML=” 广州 " ;
练习:添加删除记录 E:\web\basic\3.table
删除
点击超链接以后,超链接会跳转页面,这个是超链接的默认行为,但是这个练习中我们不希望出现默认行为,可以通过在响应函数的最后return false来取消默认行为
或者href=“ javascript : ; "
confirm() 用于弹出一个带有确认和取消按钮的提示框,需要一个字符串作为参数,该字符串将会作为提示文字显示出来
如果用户点击确认则会返回true,如果点击取消则返回false
for (var i = 0; i < allA.length; i++) {
allA[i].onclick = function(){
var tr = this.parentNode.parentNode; //获取当前tr
var flag = confirm("是否删除" + tr.children[0].innerHTML); //删除之前弹出提示框
if (flag) {
tr.parentNode.removeChild(tr);
}
return false;
添加:
此处新增员工信息并没有用form标签,所以点击submit后不用取消默认行为,如果用来form标签,则要取消
原有的职员是在
创建了td和a后,点击a并不会删除职员信息,,第一次创建的删除响应函数是在页面一加载就绑定好了,而之后新增的员工信息是在已经绑定删除响应函数之后才增加上的,所以新增的员工需要再次绑定删除响应函数,还需为新增加的a绑定响应函数
var addEmpBtn = document.getElementById("addEmpBtn");
addEmpBtn.onclick = function () {
var name = document.getElementById("empName").value;
var email = document.getElementById("email").value;
var salary = document.getElementById("salary").value;
var employeeTable = document.getElementById("employeeTable");
var tbody = employeeTable.getElementsByTagName("tbody")[0];
var tr = document.createElement("tr");
tbody.appendChild(tr);
tr.innerHTML = "" + name + " " + email + " " + salary + " Delete ";
var a = tr.getElementsByTagName("a")[0];
//为新添加的a再绑定一次单击响应函数
a.onclick = function(){
var tr = this.parentNode.parentNode; //获取当前tr
var flag = confirm("是否删除" + tr.children[0].innerHTML);
if (flag) {
tr.parentNode.removeChild(tr);
}
return false;
};
};
新增的a的响应函数与原有的a的响应函数相同,所以可以为该响应函数取个名字,与onclick绑定
function delA () {
//点击超链接以后需要删除超链接所在的那行
//这里我们点击哪个超链接this就是谁
var tr = this.parentNode.parentNode; //获取当前tr
//删除之前弹出提示框
var flag = confirm("是否删除" + tr.children[0].innerHTML);
if (flag) {
tr.parentNode.removeChild(tr);
}
return false;
};
var addEmpBtn = document.getElementById("addEmpBtn");
addEmpBtn.onclick = function () {
var name = document.getElementById("empName").value;
var email = document.getElementById("email").value;
var salary = document.getElementById("salary").value;
var employeeTable = document.getElementById("employeeTable");
var tbody = employeeTable.getElementsByTagName("tbody")[0];
var tr = document.createElement("tr");
tbody.appendChild(tr);
tr.innerHTML = "" + name + " " + email + " " + salary + " Delete ";
var a = tr.getElementsByTagName("a")[0];
//为新添加的a再绑定一次单击响应函数
a.onclick = delA;
a的索引问题
此处的this并不是allA[ i ] ,在响应函数中输出i为3,而allA数组的最大索引为2
for循环会在页面加载完成之后立即执行,而响应函数会在超链接被点击时才执行,当响应函数执行时,for循环早已执行完毕
==有一个问题:i=3,所以点击第四个超链接才可以删除,为什么点任意的超链接都会被删除 ==
看到有解释说:这个遍历是将所以的超链接都赋予响应事件
for (var i = 0; i < allA.length; i++) {
allA[i].onclick = function(){
//点击超链接以后需要删除超链接所在的那行
//这里我们点击哪个超链接this就是谁
var tr = this.parentNode.parentNode; //获取当前tr
//删除之前弹出提示框
var flag = confirm("是否删除" + tr.children[0].innerHTML);
if (flag) {
tr.parentNode.removeChild(tr);
}
return false;};
}
使用DOM操作CSS
通过JS修改元素的样式
语法: 元素 . style . 样式名 = 样式值
注意:如果CSS样式名中含有杠(-),这种名称在JS中是不合法的,比如background-color,需要将这种样式名修改为驼峰命名法
通过style属性设置的样式都是内联样式,而内联样式有较高的优先级,所以通过JS修改的样式往往会立即显示,但是如果在样式中写了!important,则此时样式会有最高的优先级,即使通过JS 也不能覆盖该样式,此时将会导致JS修改样式失效,所以尽量不要为样式添加!important
读取元素的样式
语法:元素 . style . 样式名
通过style属性设置和读取的都是内联样式,无法读取样式表中的样式
获取元素当前显示的样式
语法:元素 . currentStyle . 样式名
可以用来读取当前元素显示的样式,如果当前元素没有设置该样式,则获取它的默认值
currentStyle只有IE浏览器支持,其他浏览器都不支持
<style>
#box1{
width:200px;
height:200px !important;
background-color:red;
</style>
<script>
window.onload = function(){
var box1 = document.getElementById("box1");
var brn1 = document.getElementById("btn1");
var brn2 = document.getElementById("btn2");
btn1.onclick = function(){
//通过JS修改元素的样式
box1.style.width="300px";
box1.style.background-color = "yellow"; //含有 - 不合法
box1.style.backgroundColor = "yellow";
box1.style.height="300px"; //高度并不改变,因为用了!important
};
btn1.onclick = function(){
alert(box1.style.width); //输出300px,不能直接加数字,不然会做拼串操作
alert(box1.style.height); //弹出空提示,此读取方法只能读内联样式
};
var brn3 = document.getElementById("btn3");
btn3.onclick = function(){
alert(box1.style.height); //弹出200px
alert(box1.style.backgroundColor); //弹出yellow
//如果没有设置背景颜色
alert(box1.style.backgroundColor); //弹出背景颜色的默认值:transparent
};
};
</script>
<body>
<button id="btn1"></button>
<button id="btn2"></button>
<button id="btn3"></button>
<div id="#box1"> </div>
</body>
在其他浏览器中可以使用 getComputedStyle() 这个方法来获取元素当前的样式,这个方法是window的方法,可以直接使用
需要两个参数:
1、要获取样式的元素
2、可以传递一个伪元素,一般都传null
如 getComputedStyle( box1 , null ) . width;
该方法会返回一个对象,对象中封装了当前元素对应的样式
可以通过对象点(.)样式名来读取样式,如果获取的样式没有设置,则会获取到真实的值,而不是默认值,如:没有设置width,它不会获取到auto,而是一个具体页面长度
但是该方法不支持IE8 及以下的浏览器
通过currentStyle和getComputedStyle() 读取到的样式都是只读的,不能修改,如果要修改必须通过style属性
兼容性问题处理
定义一个函数,用来获取指定元素当前的样式
参数:
obj 要获取样式的元素
name 要获取的样式名
alert(getComputedStyle(box1,null).backgroundColor); //正常浏览器的方式
alert(box1.currentStyle.backgroundColor); //IE8的方式
function getStyle(obj,name){
if(window.getComputedStyle){ //不加window,getComputedStyle相当于是一个变量,需要去作用域寻找,而加上window就成为一个对象的属性。变量如果没找到会报错,而属性没找到会报错
getCompuutedStyle(obj,null)[name]; //不能使用 .name,这代表着获取名字为name的样式,而function中的name是个变量
}else {
obj.currentStyle[name];
}
if(obj.currentStyle){ //同理,但是建议使用上面的方法,宽度输出具体数值会更有用
return obj.currentStyle[name];
} else {
return getComputedStyle(obj,null)[name];
}
return window.getCompuutedStyle? getCompuutedStyle(obj,null)[name]:obj.currentStyle[name] //同理,但是没有第一种结构清晰
}
其他样式的相关属性
clientWidth
clientHeight
这两个属性可以获取元素的可见宽度和高度,包括内容区和内边距
这些属性都是不带px的,返回的都是一个数字,可以直接进行计算,这些属性都是只读的,不能修改
offsetWidth
offsetHeight
获取元素的整个宽度和高度,包括内容区、内边距和边框
offsetParent
可以用来获取当前元素的定位父元素,会获取到离当前元素最近的开启了定位的祖先元素,如果所有的祖先元素都没有开启定位,则返回body
offsetLeft 当前元素相对于其定位父元素(它的offsetParent)的水平偏移量
offsetTop 当前元素相对于其定位父元素的垂直偏移量
scrollWidth
scrollHeight
可以获取元素整个滚动区域的宽度和高度
scrollLeft 可以获取水平滚动条滚动的距离
scrollTop 可以获取垂直滚动条滚动的距离
当满足scrollHeight - scrollTop ==clientHeight时,说明垂直滚动条滚动到底了
当满足scrollWidth - scrollLeft ==clientWidth时,说明水平滚动条滚动到底了
练习:强制阅读协议 E:\web\basic\4.read
如果为表单项添加disabled=“disabled”,则表单项会变成不可用状态
disabled属性可以设置一个元素是否禁用,如果设置为true,则元素禁用,如果设置为false,则元素可用
onscroll 该事件会在元素的滚动条滚动时触发
出现一个问题,滚动条滑到底部,info.scrollHeight - info.scrollTop为300.4000,两个值始终不等。使用了parseInt()
事件对象
练习:显示鼠标XY坐标 E:\web\basic\5.clientXY
问题:显示的鼠标坐标是对于页面的,放在角落里鼠标的坐标不是(0,0)
onmousemove 该事件将会在鼠标在元素中移动时被触发
当事件的响应函数被触发时,浏览器每次都会将一个事件对象作为实参传递进响应函数。在事件对象中封装了当前事件相关的一切信息,如:鼠标的坐标、键盘的哪个按键被按下,鼠标滚轮滚动的方向等
定义一个形参event,可以获取事件相关信息或者对事件进行相关操作
clientX可以获取鼠标指针的水平坐标
cilentY可以获取鼠标指针的垂直坐标
这两个都是用于获取鼠标在当前的可见窗口的坐标
在IE8中,响应函数被触发时,浏览器不会传递事件对象,在IE8及以下的浏览器中,是将事件对象作为window对象的属性保存的
var x = event.clientX; //兼容除IE8及以下浏览器
var x = window.event.clientX ; //兼容IE
目前所有浏览器都可以使用window.event.clientX方式
兼容性问题示例:
//window.event.clientX方式可以兼容所有浏览器,此处仅为示范兼容方法
//第一种方法
if(!event){
event = window.event;
}
//第二种方法
event = event || window.event;
练习:div跟随鼠标移动 E:\web\basic\6.move
div跟随鼠标移动,所以box1.style.left=left,应该是设置偏移量,且left是一个数值,并没有单位,所以应该+“px”
设置偏移量只对开启定位起作用,所以还需开启定位,在#box1中添加position:absolute 绝对定位,大多数都是设置绝对定位,设置为相对定位也可以,但是我们这里没必要将它的位置留着
这个鼠标移动事件应该给document绑定,这样鼠标在整个文档中任意位置移动都可以改变box1的位移量
如果给body设置高度1000px,鼠标和box1会出现位移差,因为clientX和clientY获取的鼠标位置是在当前可见窗口的坐标,div的偏移量是相对于整个页面的
pageX和pageY
可以获取鼠标相对于当前页面的坐标
但是这两个属性在IE8中不支持,所以如果需要兼容IE8,不要使用
chrome认为浏览器的滚动条是body的,可以通过body.scrollTop来获取,火狐等浏览器认为浏览器的滚动条是html的(已兼容,chrome也认为是html的了)
给body设置高度1000px,是body的父元素html容不下body才会出现滚条,所以应该是读取html的滚动条滚动距离
var st = document.body.scrollTop;
console.log(st); //所以浏览器,包括chrome都没有变化
var st = document.documentElement.scrollTop;
console.log(st);
var st = document.documentElement.scrollTop;
这里的documentElement不是很理解
解决兼容性问题(chrome也已兼容,此处示例解决兼容问题)
var st = document.body.scrollTop|| document.documentElement.scrollTop;
var row = document.body.scrollLeft||document.documentElement.scrollLeft;
console.log(st);
var x=window.event.clientX+row;
var y=window.event.clientY+st;
var box1 = document.getElementById("box1");
box1.style.left=x+"px";
box1.style.top=y+"px";
最终解决方法
var st = document.documentElement.scrollTop;
var row = document.documentElement.scrollLeft;
console.log(st);
var x=window.event.clientX+row;
var y=window.event.clientY+st;
var box1 = document.getElementById("box1");
box1.style.left=x+"px";
box1.style.top=y+"px";
事件的冒泡(Bubble)
所谓的冒泡指的就是事件的向上传导,当后代元素上的事件被触发时,其祖先元素的相同事件也会被触发
在开发中大部分冒泡都市有用的,如果不希望发生事件冒泡,可以通过事件对象来取消冒泡
将事件对象的cancelBubble设置为true,即可取消冒泡
event . cancelBubble = true;
点击span,div和body的响应函数也会执行,点击div,body的响应函数也执行
<div id="box1">
我是box1
<span id="s1">我是一个span</span>
</div>
window.onload = function () {
var s1 = document.getElementById("s1");
s1.onclick = function (event) {
alert("我是span");
event.cancelBubble=true; //取消冒泡
}
var box1 = document.getElementById("box1");
box1.onclick = function () {
alert("我是box1");
}
document.body.onclick = function () {
alert("我是body");
}
};
取消div1跟随鼠标移动在另一个div2上的冒泡事件:当鼠标移动到div2上时,向上传导到document,触发document的onmousemove,而document的移动鼠标事件是伴随着div1移动的,所以在div2上也会出现div1移动的情况
事件的委派
为每一个超链接都绑定一个单击响应函数,这里我们为每一个超链接都绑定了一个单击响应函数,这种操作比较麻烦,而且这些操作都只能为已有的超链接设置事件,而新添加的超链接必须重新绑定
我们希望只绑定一次事件,即可应用到多个元素上,即使元素是后添加的,我们可以尝试将其绑定给元素共同的祖先元素
事件的委派值将事件统一绑定给元素共同的祖先元素,这样当后代元素上的事件触发时,会一直冒泡到祖先元素,从而通过祖先元素的响应函数来处理事件
事件委派是利用了冒泡,通过委派可以减少事件绑定的次数,提高程序的性能
如果触发事件的对象是我们期望的元素,则执行,否则不执行
target event中的target表示的触发事件的对象
<button id="btn">添加超链接<button>
<ul id="u1">
<li><a href="javascript:;" class="link">超链接一</a></li>
<li><a href="javascript:;" class="link">超链接二</a></li>
<li><a href="javascript:;" class="link">超链接三</a></li>
</ul>
window.onload = function(){
var u1=document.getElementById("u1");
var btn=document.getElementById("btn");
btn.onclick=function(){
var li = document.createElement("li");
li.innerHTML="<a href='javascript:;' class='link'>新建的超链接</a>
u1.appendChild(li);
//还需为每一个超链接绑定单击响应函数,为a标签一个一个的绑定响应函数麻烦且性能不高
};
var allA = document.getElementdByTagName("a");
for(var i = 0;i<allA.length;i++){
alert("我是a的单击函数");
};
//将响应事件绑定到共同的祖先元素
u1.onclick=function(event){
//alert("this"); 事件是给谁绑定的,this就是谁,所以 this是ul
if(event.target.className=="link"){
alert("我是ul的单击响应函数“);
};
};
事件的绑定
使用 对象 . 事件 = 函数 的形式绑定响应函数,它只能同时为一个元素的一个事件绑定一个响应函数,不能绑定多个,如果绑定了多个,则后面会覆盖掉前面的
addEventListrner() 通过这个方法也可以为元素绑定响应函数
参数:
1、事件的字符串,不要on
2、回调函数,当事件被触发时该函数会被调用
3、是否在捕获阶段触发事件,需要一个布尔值,一般都传false
使用addEventListener() 可以同时为一个元素的相同事件同时绑定多个响应函数,这样当事件被触发时,响应函数将会按照函数的绑定顺序执行
这个方法不支持IE8及以下的浏览器
在IE8中可以使用attachEvent() 来绑定事件
参数:
1、事件的字符串,要on
2、回调函数
这个方法也可以同时为一个事件绑定多个处理函数,不同的是它是后绑定先执行,执行顺序和assEventListener() 相反
addEventListener() 中的this,是绑定事件的对象
attachEvent() 中的this,是window
this是谁由调用方式决定
btn01.addEventListener("click",function(){
alert(1);
},false);
btn01.addEventListener("click",function(){
alert(2);
},false);
btn01.addEventListener("click",function(){
alert(3);
},false);
//点击按钮后会依次弹出1、2、3
btn01.attachEvent("onclick",function(){
alert(1);
});
btn01.attachEvent("onclick",function(){
alert(2);
});
btn01.attachEvent("onclick",function(){
alert(3);
});
//考虑兼容性
//定义一个函数,用来为指定元素绑定响应函数
//参数: obj:要绑定事件的对象、eventStr:事件的字符串、callback:回调函数
function binf(obj,eventStr,callback){
if(obj.addEventListener){
obj.addEventListener(eventStr,callback,false);
}else {
//this是谁由调用方式决定,这里的callback是浏览器采用函数形式调用的,callback.call(obj)中的this就与其他浏览器中的相同
obj.attachEvent("on"+eventStr,callback);
//将callback改为一个匿名函数,浏览器window调用是这个匿名函数
obj.attachEvent("on"+eventStr,function(){
//在匿名函数中调用回调函数,就可以指定this了
callback.call(obj);
});
}
};
bind(btn01,"click",function(){
alert(this); //其他浏览器输出button,因为是给button绑定的,IE8输出window,所以在IE8中使用这种方法不能用this
};
事件的传播
关于事件的传播网景公司和微软公司有不同的理解
微软公司认为事件应该是由内向外传播,也就是当事件触发时,应该先触发当前元素上的事件,然后再向当前元素的祖先元素上传播,也就是说事件应该在冒泡阶段执行
网景公司认为事件应该是由外向内传播的,也就是当前事件触发时,应该先触发当前元素的最外层的祖先元素的事件,然后再向内传播给后代元素
W3C中和了两个公司的方案,将事件的传播分成了3个阶段
1、捕获阶段:在捕获阶段时从最外层的祖先元素,向目标元素进行事件的捕获,但是默认此时不会触发事件
2、目标阶段:事件捕获到目标元素,捕获结束,开始在目标元素上触发事件
3、冒泡阶段:事件从没目标元素向它的祖先元素传递,依次触发祖先元素上的事件
如果希望在捕获阶段就触发事件,可以将addEventListener()的第三个参数设置为true,一般情况下我们不会希望在捕获阶段触发事件,所以这个参数一般都是false
IE8及以下浏览器没有捕获阶段
<div id="box1">
<div id="box2">
<div id="box3"></div>
</div>
</div>
<style>
#box1{
width:300px;
height:300px;
background-color:yellow;
};
box2{
width:300px;
height:300px;
background-color:yellow;
};
#box3{
width:300px;
height:300px;
background-color:yellow;
};
</style>
<script>
window.onload = function () {
var box1 = document.getElementById("box1");
var box2 = document.getElementById("box2");
var box3 = document.getElementById("box3");
bind(box1,"click",function(){
alert("我是box1的响应函数");
})
bind(box2,"click",function(){
alert("我是box2的响应函数");
})
bind(box3,"click",function(){
alert("我是box3的响应函数");
})
function bind(obj,eventstr,callback){
if(obj.addEventListener){
//将第三个参数设置为true,从捕获阶段就开始触发事件,祖先元素先触发,目标元素最后触发
obj.addEventListener(eventstr,callback,true);
}else{
obj.attachEvent("on"+eventstr,function(){
callback.call(obj);
})
}
}
};
</script>
<div id="box1"></div>
<div id="box2"></div> //新添加一个div,当box1移动到box2的位置时,松开鼠标box1依旧跟着鼠标一起移动,因为此时box2将box1覆盖,鼠标位于box2上
//所以鼠标松开事件应该给document绑定,这样无论鼠标在哪里松开,box1都可以被停下
#box1 {
width: 100px;
height: 100px;
background-color: yellow;
position: absolute;
}
#box2 {
width: 100px;
height: 100px;
background-color: red;
position: absolute;
left: 200px;
top: 200px;
}
var box1 = document.getElementById("box1");
box1.onmousedown = function () {
document.onmousemove = function (event) {
var x = window.event.clientX;
var y = window.event.clientY;
box1.style.left = x + "px";
box1.style.top = y + "px";
};
//应该将事件绑定给document,这样box1的移动不会受到其他元素的影响。又因为鼠标松开事件是给document绑定的,
//只要鼠标松开了该事件就执行一次,是没有必要的,所以松开鼠标事件在执行完后也应该取消
document.onmouseup = function () {
document.onmousemove = null;
document.onmouseup=null;
alert("www");
};
};
上面这种方法拖拽时鼠标用于处于box1的左上角,需要实现鼠标在哪里点的,移动时鼠标还是在box上的那个位置拖拽box
div的偏移量:鼠标 . clientX - 元素 . offsetLeft
有两个事件对象,但是两个事件对象并不一样,第一个是onmousedown的事件对象,第二个是onmousemove的事件对象
偏移量在鼠标点下时就已经确定了,拖拽过程中距离不变所以最好在onmousedown里面计算偏移量,只要求一次,如果在onmousemove里求,鼠标移动一次就得求一次,性能较差
box1.onmousedown = function (event) {
var ol = event.clientX - box1.offsetLeft;
var os = event.clientY - box1.offsetTop;
document.onmousemove = function (event) {
var x = window.event.clientX-ol;
var y = window.event.clientY-os;
box1.style.left = x + "px";
box1.style.top = y + "px";
};
给页面添加一段文字,全选ctrl+A,拖拽box1,松开鼠标后box1依旧跟着鼠标移动
当我们拖拽一个网页中的内容时,浏览器会默认去搜索引擎中搜索内容,此时会导致拖拽功能异常,这个是浏览器提供的默认行为,如果不希望发生这个行为,则可以通过return false来取消默认行为,但是对IE8 不起作用
设置btn01对鼠标按下相关的事件进行捕获
当调用一个元素的setCapture()方法以后,这个元素将会把下一次所有的鼠标按下相关的事件捕获到自己身上(仅IE),并且是一次性的,点击页面任何地方后会 执行一次btn1的点击事件
<button id="btn1">按钮1</button>
<button id="btn2">按钮2</button>
window.onload = function () {
var btn1 = document.getElementById("btn1");
var btn2 = document.getElementById("btn2");
btn1.onclick=function(){
alert(1);
};
btn2.onclick=function(){
alert(2);
};
btn1.setCapture();
};
对于IE
继续拖拽练习,全选后,之所以会拖动文字是因为onmousedown事件也传到文字上了,在开始拖拽的时候,给box1设置setCapture,这样所有的事件都会揽到box1上,不会到其他元素上。当鼠标松开时,取消对事件的捕获,releaseCapture
setCapture() 只要IE支持,但是在火狐中调用时不会报错,而如果使用chrome调用,会报错,所以还需要判断是否有捕捉和释放捕捉函数
box1.onmousedown = function (){
/*
if(setCapture){
box1.setCapture();
}
*/
box1.setCapture&&box1.setCapture();
.
var ol = event.clientX - obj.offsetLeft;
var os = event.clientY - obj.offsetTop;
document.onmousemove = function (event) {
var x = window.event.clientX-ol;
var y = window.event.clientY-os;
box1.style.left = x + "px";
box1.style.top = y + "px";
};
//应该将事件绑定给document,这样box1的移动不会受到其他元素的影响。又因为鼠标松开事件是给document绑定的,
//只要鼠标松开了该事件就执行一次,是没有必要的,所以松开鼠标事件在执行完后也应该取消
document.onmouseup = function () {
document.onmousemove = null;
document.onmouseup=null;
/*
if(releaseCapture){
box1.releaseCapture();
}
*/
box1.releaseCapture&&box1.releaseCapture();
alert("www");
};
return false;
};
如果希望box2也有拖拽功能,可以拖拽封装到一个函数里面
参数:开启拖拽的元素
一定要给元素开启绝对定位才可以拖动
window.onload = function () {
var box1 = document.getElementById("box1");
var box2 = document.getElementById("box2");
drag(box1);
drag(box2);
};
function drag(obj) {
obj.onmousedown = function (event) {
/*
if(setCapture){
obj.setCapture();
}
*/
obj.setCapture && obj.setCapture();
var ol = event.clientX - obj.offsetLeft;
var os = event.clientY - obj.offsetTop;
document.onmousemove = function (event) {
var x = window.event.clientX - ol;
var y = window.event.clientY - os;
obj.style.left = x + "px";
obj.style.top = y + "px";
};
//应该将事件绑定给document,这样obj的移动不会受到其他元素的影响。又因为鼠标松开事件是给document绑定的,
//只要鼠标松开了该事件就执行一次,是没有必要的,所以松开鼠标事件在执行完后也应该取消
document.onmouseup = function () {
document.onmousemove = null;
document.onmouseup = null;
/*
if(releaseCapture){
obj.releaseCapture();
}
*/
obj.releaseCapture && obj.releaseCapture();
alert("www");
};
return false;
};
}
鼠标的滚轮事件
练习:E:\web\basic\8.run
视频用到了onmousewheel方法,看弹幕说这种方法已经被弃用。使用onwheel代替。但是onwheel在IE中不能直接使用,有另外一个思路是当鼠标放在box1上,滚动的距离可以获取html的scrolltop来获取,与box1原长度相加。
这一集视频跳过了,等到以后学习的更深入了再看这个
键盘事件
onkeydowm 键盘被按下
对于onkeydown来说,如果一直按着某个按键不松手,则事件会一直触发。当onkeydown连续触发时,第一次和第二次之间会间隔稍微长一点,其它的会非常快,这种设计是为了防止误操作的发生
onkeyup 键盘被松开
键盘事件一般都会绑定给一些可以获取到焦点的对象,或者document
可以通过keyCode来获取按键的编码,通过它可以判断哪个按键被按下
除了keyCode,事件对象还提供了几个属性:altKey、ctrlKey、shiftKey
这三个用来判断alt、ctrl和shift是否被按下,如果按下则返回true,否则返回false
<input type="text"/>
window.onload = function(){
document.onkeydown=function(event){
console.log("dd"); //只要按下键盘就会输出dd。即使是在输入框中
console.log(event.keyCode);
//通过keyCode判断y是否被按下
if(event.keyCode===89){
console.log("y被按下了");
}
//判断y和ctrl是否被同时按下
if(event.keyCode===89&&event.keyCode===17) //不能这么写,因为event.keCode不能既等于89也等于17,这是一个永远为假的式子
if(event.keyCode===89&&event.ctrlKey){
console.log("y和ctrl被同时按下了");
}
};
};
在文本框中输入内容,属于onkeydown的默认行为,如果在onkeydown中取消了默认行为,则输入的内容,不会出现在文本框中
return false; //控制台中输出,但是文本框中不显示按下的按键
<input type="text"/>
window.onload = function(){
var input=document.getElementsByTagName("input")[0];
input.onkeydown=function(event){
console.log("dd");
if( event.keyCode===89&&event.ctrlKey){
console.log("y被按下了");
}
//使文本框中不能输入数字
if(event.keyCode>=48&&event.keyCode<=57){
return false;
}
};
};
练习:键盘移动Div E:\web\basic\9.key
使用的是if语句,但是在这里switch语句会更容易一点
修改元素的样式,一定要使用 元素 . style . 样式名
移动时第一次与第二次移动间会稍微停顿一下,这个问题还没有解决