一.焦点事件(焦点就是光标所在的位置)
1.焦点事件的作用:使浏览器能够区分用户输入的对象,当一个元素有焦点的时候,那么它就可以等待用户的输入;
2.可以切换焦点的方法:
A.点击;
B.tab键;
C.javascript
注意:不是所有的元素都有焦点,能响应用户操作的元素才有焦点(比如div就没有焦点,a就有焦点)
3.onfocus
事件:当元素获取到焦点时触发;
4.onblur
事件:当元素失去焦点时触发;
练习1:写一个placeholder的兼容函数,value为“请输入内容”。当获取焦点时清空,失去焦点且为空时恢复。
<body>
<input type="text" value="请输入内容" id="text1" />
<script>
var oText1=document.getElementById("text1");
oText1.onfocus=function(){
if(this.value=="请输入内容"){
this.value='';
}
}
oText1.onblur=function(){
if(this.value==''){
this.value=="请输入内容";
}
}
</script>
</body>
5.focus()
方法:让元素获得焦点。例如百度页面刷新出来后会立即在搜索框里获得焦点;
6.blur()
方法:让元素取消焦点
7.select()
方法:选择指定元素里的文本内容(但是只能选择能够获取焦点元素的内容)
二.event
1.什么是event:
event是事件对象。当一个事件发生时,和当前这个对象发生的事件有关的一些信息都会被临时保存到一个指定地方(以json的格式保存)——event对象里,供我们需要的时候调用。
对事件对象作出如下几点说明:
A.类似飞机的黑匣子——记录飞机飞行的状况信息以供需要的时候找到这些信息;
B.事件对象必须在一个事件函数(由某个事件触发时调用的函数叫事件函数)里面使用才有内容。函数是不是事件函数,不是定义时决定,而是调用时决定,例如:
function fn1(){alert(event);}
fn1();//此时fn1不是事件函数,故弹出内容为undefined
document.onclick = fn1;//此时fn1是事件函数,故弹出内容为object MouseEvent
2.event兼容问题:
A.event对象在IE和chrome里面是内置的全局变量,像window一样定义好的,弹出为空和未定义;
B.但是event在火狐里会报错:事件对象是通过事件函数的第一个参数定义的。
function fn1(ev){
alert(ev);
}
//标准浏览器下的用法,在标准的IE和chrome也是有效
解决兼容性问题的方案:
function fn1(ev){
var ev = ev || event;
//在非标准浏览器下ev是未定义,就会取到后面的event
...
}
//同样是用在事件函数时
3.查看event里的内容:
for(var attr in ev){
console.log(attr +'='+ev[attr])
}
4.event的鼠标位置:
A.ev.clientX/ev.clientY
:鼠标在可视区的X/Y的坐标
练习2:做div(宽高100PX,背景为红色的方块)跟着鼠标移动的效果
(注意:当有滚动条的时候会出现问题。因为clientX和clientY是可视区为对照的。以下假设是有Y向的滚动条)
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>无标题文档</title>
<style type="text/css">
body{margin:0;}
#div1{width:100px; height:100px; background:red; position:relative;}
</style>
</head>
<body>
<div id="div1"></div>
<script>
var oDiv=document.getElementById("div1");
function move(ev){
var ev = ev || event;
var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
oDiv.style.left=ev.clientX+'px';
oDiv.style.top=ev.clientY+scrollTop+'px';
}
document.onmousemove=move;
</script>
</body>
三.事件流
在总结这一块知识之前,先推荐一篇很优秀的关于事件流知识讲解的博客
1.事件流概念:事件发生时会在元素节点与根节点之间按照特定的顺序传播,路径所经过的所有节点都会收到该事件,这个传播过程即DOM事件流。
2.根据事件传播的顺序可以分为两种事件流模型:冒泡型事件流和捕获型事件流。(捕获和冒泡的前提是所有层级的元素都必须绑定同一种事件,如下例:)
<body>
<div id="outer">
<div id="middle">
<div id="inner">
click me!
</div>
</div>
</div>
<script>
var innerCircle= document.getElementById("inner");
innerCircle.onclick = function(){
alert("123");
}
</script>
</body>
此时运行结果是点击最内圈的圆弹出一个“123”。有人可能迷惑,按照事件冒泡原理,为什么点击第二层圆和第一层圆没有反应呢?没有给给第二层圆和第一层圆绑定点击事件函数当然点击时无反应了!而其实当最内层圆执行点击事件的时候,已经将事件依次冒泡给了父元素,只不过父级元素没有绑定相关的事件函数,即执行为空,当然没有任何行为。
A.事件冒泡:事件的传播是从最特定的事件目标到最不特定的事件目标。即从DOM树的叶子到根。
a.先看一个例子:做3个div嵌套,并都绑定一个事件,点击某个div时,弹出这个div的id
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>无标题文档</title>
<style type="text/css">
#div1{background-color:#00C; padding:40px;}
#div2{background-color:#933; padding:40px;}
#div3{background-color:#3F3; padding:40px;}
</style>
</head>
<body>
<div id="div1">
<div id="div2">
<div id="div3"></div>
</div>
</div>
<script>
var aDiv=document.getElementsByTagName("div");
for(var i=0;i<aDiv.length;i++){
aDiv[i].onclick=function(ev){
var ev=ev||event;
alert(this.id);
}
}
</script>
</body>
</html>
测试结果:
当点击绿色div时,依次弹出div3,div2,div1;
当点击红色div时,依次弹出div2,div1;
当点击蓝色div时,弹出div1
b.分析上例:上例最终是想实现点击哪个div就仅弹出与之对应的那个id名,可是明显第二个和第三个div出现了问题。产生问题的原因和页面的html结构相关。原理是事件冒泡机制:当一个元素接受到事件的时候,会把他接收的所有传播给他的父级,父级继续传播到顶层。
c.注:给一个元素绑定事件实际上是绑定事件所处理的函数。意思是,对于一个元素,假如进行点击操作,即使它没加点击事件onclick也会触发,只是不绑定事件处理函数时,这个事件将不会执行任何操作。
d.再看一个例子:做一个下拉菜单,在点击时,让该菜单展示出来,再次点击或者点击页面的其他位置时,菜单隐藏
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>无标题文档</title>
<style type="text/css">
body{margin:0;}
ul li{list-style:none;}
#ul1{display:none;}
#ul1 li{width:200px; height:100px; border:1px #ccc solid;}
</style>
</head>
<body>
<button id="btn1">点击展开</button>
<ul id="ul1">
<li>公司简介</li>
<li>公司简介</li>
<li>公司简介</li>
<li>公司简介</li>
<li>公司简介</li>
</ul>
<script>
var oBtn=document.getElementById("btn1");
var oUl1=document.getElementById("ul1");
var onoff=true;
oBtn.onclick=function(ev){
if(onoff){
var ev=ev||event;
oUl1.style.display='block';
onoff=false;
}else{
oUl1.style.display='none';
onoff=true;
}
}
document.onclick=function(){
oUl1.style.display='none';
onoff=true;
}
</script>
</body>
</html>
此时,点击“展开”按钮并没有什么反应。原因是给按钮绑定的点击时下拉框弹出的事件冒泡给了按钮的父级元素,(按照button》body》document》html的顺序进行事件的传播),与下面“点击页面其他位置时下拉框收起”事件发生冲突,所以点击按钮时下拉框无法正常弹出。
为了解决上述问题,只需要阻止事件冒泡就可以了。
var ev = ev||event;
ev.cancleBubble = true;//阻止当前对象的当前事件冒泡,其他的事件冒泡不会阻止
改正以上代码:
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>无标题文档</title>
<style type="text/css">
body{margin:0;}
ul li{list-style:none;}
#ul1{display:none;}
#ul1 li{width:200px; height:100px; border:1px #ccc solid;}
</style>
</head>
<body>
<button id="btn1">点击展开</button>
<ul id="ul1">
<li>公司简介</li>
<li>公司简介</li>
<li>公司简介</li>
<li>公司简介</li>
<li>公司简介</li>
</ul>
<script>
var oBtn=document.getElementById("btn1");
var oUl1=document.getElementById("ul1");
var onoff=true;
oBtn.onclick=function(ev){
if(onoff){
var ev=ev||event;
ev.cancelBubble = true;
oUl1.style.display='block';
onoff=false;
event.cancelBubble = true;
}else{
oUl1.style.display='none';
onoff=true;
}
}
document.onclick=function(){
oUl1.style.display='none';
onoff=true;
}
</script>
</body>
</html>
B.事件捕获:事件的传播是从最不特定的事件目标到最特定的时间目标。即从DOM树的根到叶子。
a.例如在document上绑定两个点击事件函数,会让后面的覆盖前面的(尤其多人一起做的项目),在一个大型项目中很可能出现一个元素需要绑定多个不同的函数。因此为了能够给一个对象的同一个事件绑定多个不同的函数,加了一个元素绑定事件函数的第二种形式:
1>在IE下:
obj.attachEvent(事件名称,事件函数);
特点:
(1)非标准浏览器下没有事件捕获;
(2)事件名称有on,如onclick、onmouseover等事件;
(3)事件执行顺序倒序;
(4)this指向window
2>标准浏览器下:
obj.addEventListener(事件名称,事件函数,是否捕获【不写时默认false。false:冒泡;true:捕获】)
特点:
(1)有捕获;
(2)事件名称没有on,如将onclick写成click;
(3)事件执行是顺序;
(4)this指向该事件的对象
b.综合上述两种写法,可以封装成如下函数:
function bind(obj,evname,fn){
if(obj.addEventListener){
obj.addEventListener(evname,fn,false);
}else{
obj.attachEvent('on'+evname,function(){
fn.call(obj);//调用call方法,在IE下将this从指向window改为指向当前对象
}
)}
}
补充:当定义了函数时,函数会有个call()方法,用法类似于直接调用该函数,例如:fn1()相当于fn1.call()。区别是用call()方法的第一个参数可以改变内部this的指向(null时为原本该指向的),后面的参数是正常的参数。
3.事件捕获和事件冒泡的执行顺序:
总体来说就是先捕获,再冒泡。具体内容可以参照文章开头介绍的那篇博客。例如:
var oDiv1 = document.getElementById('div1');
var oDiv2 = document.getElementById('div2');
var oDiv3 = document.getElementById('div3');
function bind(obj,evname,fn){
if(obj.addEventListener){
obj.addEventListener(evname,fn,false);
}else{
obj.attachEvent('on'+evname,function(){
fn.call(obj);
}
)}
}
function fn1(){
alert(1);
}
function fn2(){
alert(2);
}
function fn3(){
alert(3);
}
oDiv3.addEventListener('click',fn1,false);
oDiv2.addEventListener('click',fn2,true);
oDiv1.addEventListener('click',fn3,false);
当点击div3时,依次弹出2(先是div2的点击事件在捕获时被触发)、1(再是div3的点击事件在冒泡时被触发)、3(最后是div1的点击事件在冒泡时被触发)
最后看一个详细的例子:(本例摘自上面介绍的博客)
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<style>
#outer{
position: absolute;
width: 400px;
height: 400px;
top:0;
left: 0;
bottom:0;
right: 0;
margin: auto;
background-color: deeppink;
}
#middle{
position: absolute;
width: 300px;
height:300px;
top:50%;
left: 50%;
margin-left: -150px;
margin-top: -150px;
background-color: deepskyblue;
}
#inner{
position: absolute;
width: 100px;
height:100px;
top:50%;
left:50%;
margin-left: -50px;
margin-top: -50px;;
background-color: darkgreen;
text-align: center;
line-height: 100px;
color:white;
}
#outer,#middle,#inner{
border-radius:100%;
}
</style>
<body>
<div id="outer">
<div id="middle">
<div id="inner">
click me!
</div>
</div>
</div>
<script>
var innerCircle= document.getElementById("inner");
innerCircle.addEventListener("click", function () {
alert("innerCircle的click事件在捕获阶段被触发");
},true);
innerCircle.addEventListener("click", function () {
alert("innerCircle的click事件在冒泡阶段被触发");
},false);
var middleCircle= document.getElementById("middle");
middleCircle.addEventListener("click", function () {
alert("middleCircle的click事件在捕获阶段被触发");
},true);
middleCircle.addEventListener("click", function () {
alert("middleCircle的click事件在冒泡阶段被触发");
},false);
var outerCircle= document.getElementById("outer");
outerCircle.addEventListener("click", function () {
alert("outerCircle的click事件在捕获阶段被触发");
},true);
outerCircle.addEventListener("click", function () {
alert("outerCircle的click事件在冒泡阶段被触发");
},false);
</script>
</body>
</html>
运行结果:
练习一:做一个选择输入法的输入框。当点击输入框时,包含输入法内容的下拉框出现,再次点击输入框或者点击页面其他位置时下拉框收起。当鼠标在下拉框内指向哪行内容,当前行背景色变化,鼠标移开后又复原。并且鼠标最后选中的内容要显示在输入框内。
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>无标题文档</title>
<style type="text/css">
*{box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;color:#000;}
body{margin:10px 0;}
ul,li{margin:0; padding:0;}
ul li{list-style:none;}
#div1{width:300px; margin:0 auto;}
#input1{width:300px; height:50px; border:1px #F90 solid;}
#ul1{border:1px #F90 solid; display:none;}
#ul1 li{width:298px; height:50px;line-height:50px; cursor:pointer;}
</style>
</head>
<body>
<div id="div1">
<input id="input1" type="text" />
<ul id="ul1">
<li>宋体</li>
<li>黑体</li>
<li>楷体</li>
<li>微软雅黑</li>
<li>新宋体</li>
<li>仿宋</li>
</ul>
</div>
<script>
var oInput1=document.getElementById("input1");
var oUl1=document.getElementById("ul1");
var aLi=oUl1.getElementsByTagName("li");
var onoff=true;
oInput1.onclick=function(ev){
if(onoff){
var ev=ev||event;
oUl1.style.display='block';
onoff=false;
ev.cancelBubble = true;
}else{
oUl1.style.display='none';
onoff=true;
}
}
document.onclick=function(){
oUl1.style.display='none';
onoff=true;
}
for(var i=0;i<aLi.length;i++){
aLi[i].onmouseover=function(){
for(var j=0;j<aLi.length;j++){
aLi[j].style.backgroundColor='#fff';
}
this.style.backgroundColor='#F90';
}
aLi[i].onmousedown=function(){
this.style.backgroundColor='#F90';
oInput1.value=this.innerHTML;
}
}
</script>
</body>
</html>
练习二:有三个不同的主题,之下有很多不同的选项,将点击选择之后的选择结果汇总显示在下方。
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>无标题文档</title>
<style type="text/css">
*{box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;color:#000;}
body{margin:10px 0;}
ul,li{margin:0; padding:40;}
ul li{list-style:none; cursor:pointer;}
p{padding-left:20px; font-size:24px; font-weight:bold;}
</style>
</head>
<body>
<p id="p1">地区</p>
<ul id="ul1">
<li>东城</li>
<li>西城</li>
<li>朝阳</li>
<li>昌平</li>
</ul>
<p id="p2">职位</p>
<ul id="ul2">
<li>美工设计</li>
<li>前端工程师</li>
<li>后台开发</li>
</ul>
<p id="p3">薪资</p>
<ul id="ul3">
<li>4000</li>
<li>5000</li>
<li>6000</li>
</ul>
<p id="p4">
<span id="span1"></span>
<span id="span2"></span>
<span id="span3"></span>
</p>
<script>
var oUl1=document.getElementById("ul1");
var oUl2=document.getElementById("ul2");
var oUl3=document.getElementById("ul3");
var aLi1=oUl1.getElementsByTagName("li");
var aLi2=oUl2.getElementsByTagName("li");
var aLi3=oUl3.getElementsByTagName("li");
var oP4=document.getElementById("p4");
var aSpan=oP4.getElementsByTagName("span");
for(var i=0;i<aLi1.length;i++){
aLi1[i].onclick=function(){
aSpan[0].innerHTML=this.innerHTML;
}
}
for(var i=0;i<aLi2.length;i++){
aLi2[i].onclick=function(){
aSpan[1].innerHTML=this.innerHTML;
}
}
for(var i=0;i<aLi3.length;i++){
aLi3[i].onclick=function(){
aSpan[2].innerHTML=this.innerHTML;
}
/*下面是为同一对象同一事件绑定不同函数的写法,但此题不需要
function fn1(){
aSpan[0].innerHTML=this.innerHTML;
}
function fn2(){
aSpan[1].innerHTML=this.innerHTML;
}
function fn3(){
aSpan[2].innerHTML=this.innerHTML;
}
function bind(obj,evname,fn){
if(obj.addEventListener){
obj.addEventListener(evname,fn,false);
}else{
obj.attachEvent('on'+evname,function(){
fn.call(obj);
}
)}
}
for(var i=0;i<aLi1.length;i++){
bind(aLi1[i],'click',fn1);
}
for(var i=0;i<aLi2.length;i++){
bind(aLi2[i],'click',fn2);
}
for(var i=0;i<aLi3.length;i++){
bind(aLi3[i],'click',fn3);
}*/
}
</script>
</body>
</html>