js事件

241 阅读8分钟

事件模型是什么?我们有几种方式给元素添加事件?

  • 事件定义: 事件是您在编程时系统内发生的动作或者发生的事情,系统通过它来告诉您在您愿意的情况下您可以以某种方式对它做出回应。每个可用的事件都会有一个事件处理器,也就是事件触发时会运行的代码块。当我们定义了一个用来回应事件被激发的代码块的时候,我们说我们注册了一个事件处理器。注意事件处理器有时候被叫做事件监听器
  • 常用事件:
事件描述
onchangeHTML 表单元素内容改变
onclick用户点击 HTML 元素
onmouseover用户在一个HTML元素上移动鼠标
onmouseout用户从一个HTML元素上移开鼠标
onkeydown用户按下键盘按键
onload浏览器已完成页面的加载
onresize窗口或框架被重新调整大小
onscroll当文档被滚动时发生的事件
onblur元素失去焦点时触发
onfocus元素获取焦点时触发
  • 事件模型含义:
  1. JS的事件模型就是一种观察者模式体现,当对应的事件被触发时,监听该事件的所有监听函数都会被调用。
  2. 观察者模式又叫做发布订阅者模式(Publish/Subscribe),它可以让多个观察者对象同时监听某一个主题对象,这个主题对象的状态变化时会通知所有的订阅者,使得他们能够做出反应。
  • 添加事件方式:
  1. HTML绑定:(一般不倡导使用这个,HTML和JS耦合)
<a onclick="alert(1)">点击</a>
  1. 通过js代码绑定:
var btn=document.getElementById('id');
btn.onclick=alert(1);
//移除用btn.onclick=null
    • DOM Level 2 Events(事件模型):捕获阶段-目标阶段-冒泡阶段 使用addEventListener,可以绑定多个事件(移除removeEventListener) 第一个参数:事件类型,不需要添加上on 第二个参数:事件函数 第三个参数:是否捕获(布尔值),默认是false,为是冒泡。 同时存在捕获与冒泡时,捕获的优先级是高于冒泡的。没有捕获的时候,冒泡谁在前面就先执行谁。
    • Internet Explorer 8 及更早IE版本: 目标阶段-冒泡阶段 ie浏览器只能使用attachEvent(eventType, handler),移除用detachEvent
var btn=document.getElementById('id');
if (btn.addEventListener){
btn.addEventListener('click',function(){alert(1)});
btn.addEventListener('click',function(){alert(2)});
}else if(btn.attachEvent){
//ie添加
btn.attachEvent('onclick',function(){alert(1)});
btn.attachEvent('onclick',function(){alert(2)});
}

4.使用setAttribute,IE不支持用 setAttribute 设置某些属性。

    var obj = document.getElementById("obj");
    obj.setAttribute("onclick", "alert(1)");
    <!-- 与直接在HTML设置一样<a onclick="alert(1)">点击</a> -->
  • 事件流:
  1. 事件冒泡(Event Capturing): 是一种从下往上的传播方式。事件最开始由最具体的元素(文档中嵌套层次最深的那个节点接受, 也就是DOM最低层的子节点), 然后逐渐向上传播到最不具体的那个节点,也就是DOM中最高层的父节点。
  2. 事件捕获(Event Bubbling): 与事件冒泡相反。事件最开始由不太具体的节点最早接受事件, 而最具体的节点最后接受事件。

链接

事件对象都有哪些属性? 分别应用于哪些场景?

  • 事件对象:有时候在事件处理函数内部,您可能会看到一个固定指定名称的参数,例如event,evt或简单的e。 这被称为事件对象,它被自动传递给事件处理函数,以提供额外的功能和信息
var btn=document.getElementById('id');
btn.onclick=function(e){console.log(e};
属性描述
type获取到事件的类型
currentTarget返回其事件监听器触发该事件的元素。
target获取到事件的目标元素,IE用srcElement获取事件目标。表示为当前的事件操作的dom,但是不是真正操作dom
relatedTarget返回与事件的目标节点相关的节点。
stopPropagation()阻止事件冒泡,IE用cancelBubble阻止事件冒泡
preventDefault()阻止事件默认行为,IE用returnValue阻止事件默认行为
altKey返回当事件被触发时,”ALT” 是否被按下。
button返回当事件被触发时,哪个鼠标按钮被点击。
ctrlKey返回当事件被触发时,”CTRL” 键是否被按下。
metaKey返回当事件被触发时,”meta” 键是否被按下。
shiftKey返回当事件被触发时,”SHIFT” 键是否被按下。
clientX返回当事件被触发时,鼠标指针的水平坐标,事件发生时鼠标在浏览器内容区域的X坐标(不包含滚动条)。浏览器内容区域即浏览器窗口中用来显示网页的可视区域,注意这个可视,也就是说需要拖动滚动条才能看到的区域不算。
clientY返回当事件被触发时,鼠标指针的垂直坐标,事件发生时鼠标在浏览器内容区域的Y坐标(不包含滚动条)。浏览器内容区域即浏览器窗口中用来显示网页的可视区域,注意这个可视,也就是说需要拖动滚动条才能看到的区域不算。
screenX返回当某个事件被触发时,鼠标指针的水平坐标,距屏幕左边界的距离。
screenY返回当某个事件被触发时,鼠标指针的垂直坐标,距屏幕上边界的距离。
pageX距左边界的距离,IE8不支持,鼠标相对于整个页面的X坐标。
pageY距上边界的距离,IE8不支持,鼠标相对于整个页面的Y坐标。

坐标

事件委托(代理)是什么?有哪些优势?应用场景?

  • 事件委托定义:事件委托就是利用事件冒泡,指定最外层的一个事件处理程序,就可以管理某一类型的所有事件。

  • 通俗解释:有三个同事预计会在周一收到快递。为签收快递,有两种办法:一是三个人在公司门口等快递;二是委托给前台MM代为签收。现实当中,我们大都采用委托的方案(公司也不会容忍那么多员工站在门口就为了等快递)。前台MM收到快递后,她会判断收件人是谁,然后按照收件人的要求签收,甚至代为付款。这种方案还有一个优势,那就是即使公司里来了新员工(不管多少),前台MM也会在收到寄给新员工的快递后核实并代为签收。

    • 第一,现在委托前台的同事是可以代为签收的,即程序中的现有的dom节点是有事件的;
    • 第二,新员工也是可以被前台MM代为签收的,即程序中新添加的dom节点也是有事件的;
  • 不使用委托缺点:(我们有100个li,每个li都有相同的click点击事件,可能我们会用for循环的方法,来遍历所有的li,然后给它们添加事件)

    1. 在JavaScript中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能,因为需要不断的与dom节点进行交互,访问dom的次数越多,引起浏览器重绘与重排的次数也就越多,就会延长整个页面的交互就绪时间。
    2. 每个函数都是一个对象,是对象就会占用内存,对象越多,内存占用率就越大,自然性能就越差了(内存不够用),比如上面的100个li,就要占用100个内存空间,如果是1000个,10000个呢,内存就会占用越大
<ul id="a">
  <li class="child1">1</li>
  <li class="child2">2</li>
  <li class="child3">3</li>
</ul>
<script type="text/javascript">
  //父元素
  var dom= document.getElementById('a');
  //父元素绑定事件,代理子元素的点击事件
  dom.onclick= function(event) {
    var event= event || window.event;
    var curTarget= event.target || event.srcElement;

    if (curTarget.tagName.toLowerCase() == 'li') {
      //事件处理
    }
  }
</script>

  • 优势:
    1. 节省内存占用,减少事件注册
    2. 新增子对象时无需再次对其绑定事件,适合动态添加元素
  • 应用场景:
    1. 适合用事件委托的事件:click,mousedown,mouseup,keydown,keyup,keypress。
    2. 只在必须的时候,比如Ajax局部刷新区域
    3. 绑定层级比较低的时候,不在body上绑定
    4. 绑定次数较少的时候,把多个事件绑定合并到一次事件委托中,由这个事件委托的回调,来进行分发

习题:以下输出什么? 为什么?

<label>Click me <input type="text"></label>
<script>
    document.querySelector('label').addEventListener('click',function () {
        console.log(1)
    })
    document.querySelector('input').addEventListener('click',function () {
        console.log(2)
    })
</script>
  • 因为label标签的特性,点击标签,浏览器会自动把焦点转移到和标签相关的表单上(为鼠标用户改进了可用性,可以关联特定的表单控件)。
  • label标签的关联方式主要有两种,显示关联和隐式关联:
    1. 显示关联:使用for属性,属性值为关联表单元素的id。
    2. 隐式关联:不使用for,label标签只能包含一个表单元素,包含多个只对第一个有效。
  • 点击lable,输出1 2 1,因为label点击输出1,由于label绑定到input,input发生点击输出2,由于冒泡label再次点击输出1
  • 点击input,输出 2 1 ,因为由于冒泡,label会触发点击事件会输出2,