Javascript 之 事件

事件流(描述的是从页面中接受事件的顺序)

1、IE 的事件冒泡流:即事件最开始由最具体的元素(文档中嵌套层次最深的那个点)接收,然后逐级向上传播至最不具体的那个节点(文档)。

2、Netscape 的事件捕获流:与 IE 的冒泡流截然相反,由不太具体的节点应该更早接收到事件,而最具体的节点最后接收到事件。

事件处理程序

1、HTML 事件处理程序,缺点: HTML 和 JS 代码紧密的耦合在一起了,修改时须多处修改,不方便

<button onclick="alert('Hello world!')">点击我</button>

=============================== 或者 ============================

<button onclick="hello();">点击我</button>
function hello () {
  alert("Hello world!");
}

2、DOM0 级事件处理程序,是一个比较传统的方式:把一个函数赋值给一个事件的处理程序属性,是用的比较多的方法。优点:简单、跨浏览器

<button id="btn">点击我</button>
var btn = document.getElementById("btn");
btn.onclick = function () {
  alert("Hello world!");
}

====================== 或者 ======================

btn.onclick = hello();
function hello () {
  alert("Hello world!");
}

btn.onclick = null;  // 取消元素绑定的 click 事件

3、DOM2 级事件处理程序。

   addEventListener 是添加事件绑定, removeEventListener 是取消事件绑定。

  都接收三个参数:要处理的事件名、作为事件处理程序的函数、布尔值(true 表示在捕获阶段调用事件处理程序, false 表示在冒泡阶段端用事件处理程序,默认为 fasle)

<button id="btn">点击我</button>

var btn = document.getElementById("btn");
btn.addEventListener("click", function(){
  alert("Hello world!");
}, false)

====================== 或者 ==========================

btn.addEventListener("click", hello, false);
function hello () {
  alert("Hello world!");
}

btn.removeEventListener("click", hello, false);  // 删除元素的事件绑定。
注意:通过什么方式添加的事件,就要通过什么方式删除事件

4、IE 事件处理程序。添加事件处理程序是 attachEvent(),删除事件处理程序是 detachEvent,接收两个相同的参数:事件处理程序的名称、事件处理程序的函数

  与 addEventListener 相比,没有第三个参数,因为 IE8 以及更早的浏览器版本只支持事件冒泡流

<button id="btn">点击我</button>

var btn = document.getElementById("btn");
btn.attachEvent("onclick", function () {  // 在 IE 下的事件需要加前缀 on
  alert("Hello world!");
})

================== 或者 =================
btn.attachEvent("onclick", hello);
function hello () {
  alert("Hello world");
}

btn.detachEvent("onclick", hello);   // 删除事件绑定程序

5、跨浏览器的事件处理程序,能力测试

var eventUtil = {
  // 添加事件
  addEvent: function (ele, type, fn) {  // ele:元素  type:事件类型名称  fn:事件处理程序函数
    if (ele.addEventListener) {  // 判断浏览器时候支持 addEventListener   
      ele.addEventListener(type, fn, false);
    } else if (ele.attachEvent) {  // 判断浏览器是否支持 IE 的 attachEvent
      ele.attachEvent(type, fn);
    } else {  // 通用的 DOM0 级方法
      ele["on" + type] = fn;
    }
  },
  // 删除事件
  removEvent: function (ele, type, false) {
    if (ele.removeEventListener) {
      ele.removeEventListener(type, fn, false);
    } else if (ele.detachEvent) {
      ele.detachEvent(type, fn);
    } else {
      ele["on" + type] = null;
    }    
  },
  // 获取 event
  getEvent: function (event) {
    return event?event:window.event;
  },
  // 获取事件目标元素
  getEventElement: function (event) {
    return event.target || event.srcElement;
  },
  // 阻止事件默认行为
  preventDefault: function (event) {
    if (event.preventDefault) {
      event.preventDefault();
    } else {
      event.returnValue = false;
    }
  },
  // 阻止事件冒泡
  stopPropagation: function (event) {
    if (event.stopPropagation) {
      event.stopPropation();
    } else {
      event.cancelBubble = true;
    }
  }
}

<button id="btn"></button>
var btn = document.getElementById("btn");
// 添加
event.addEvent(btn, "click", function () {
  alert("Hello world!");
});
// 删除
event.removeEvent(btn, "click", function () {
  alert("Hello world!");
})

==================== 或者 ======================
event.addEvent(btn, "click", hello);
event.removeEvent(btn, "click", hello);
function hello () {
  alert("Hello world")!
}

用例:
<a href="www.baidu.com" id="link"></a>
var link = document.getElementById("link");
eventUtil.addEvent(link, "click", funciton (e) {
  e = eventUtil.getEvent(e);  // 获取 event 对象
  alert(eventUtil.getEventElement(e).nodeName);  // 获取事件目标元素
  eventUtil.preventDefault(e);  // 阻止事件默认行为
  eventUtil.stopPropagation(e);  // 阻止事件冒泡
})

事件对象(在触发 DOM 上的事件时都会产生一个对象,叫 event 对象)

1、DOM 中的事件对象

event.type  // 事件类型
event.target  // 事件目标
event.target.nodeName  // 事件目标的节点名称
event.target.nodeType  // 事件目标的节点类型
event.target.selectedIndex  // 事件目标 select 标签选中的索引值,也就是选中了哪一个 option 标签
event.keyCode  // 此属性用于得到键盘对应的键码值
event.stopPropagation()  // 这个方法用于阻止事件冒泡
event.preventDefault()  // 这个方法用于阻止事件的默认行为

2、IE 中的事件对象

event.type  // 事件类型
event.srcElement  // 事件目标
event.cancelBubble  // 阻止事件冒泡 event.cancelBubble = true; 取消阻止事件冒泡 event.cancelBubble = false;
event.returnValue  // 阻止事件的默认行为,默认值是 true 表示不阻止事件的默认行为, event.returnValue = false 可以设置阻止事件的默认行为


IE8 或者更低的浏览器
function ev (evetn) {
  event = event || window.event;
  var targetD = event.target || event.srcElement;
}

 事件类型

鼠标事件都是在浏览器窗口中的特定位置上发生的,这个位置信息保存在事件的 clientX 和 clientY 属性中,所有浏览器都支持这两个属性,它们的值表示事件发生时鼠标指针在视口中的水平和垂直坐标,不包括页面滚动的距离

event.clientX  // 鼠标指针在视口中的水平位置
event.clientY  // 鼠标指针在视口中的垂直位置
mousedown  // 在用户按下鼠标任意键时触发
mousemove  // 当鼠标指针在元素内部移动时重复地触发
mouseup  // 当用户释放鼠标按钮时触发
keydown  // 当用户按下键盘上的任意键时触发,而且如果按住不放的话,会重复触发此事件
keypress  // 当用户按下键盘上的字符键时触发,而且如果按住不放的发,会重复触发此事件
keyup  // 当用户释放键盘上的任意键时触发

element.offsetLeft  // 元素距离视口左边的距离
element.offsetTop  // 元素距离视口上边的距离
document.documentElement.clientWidth || document.body.clientWidth  // 窗口的宽度
document.documentElement.clientHeight || document.body.clientHeight  // 窗口的高度

取消默认行为

<a href="http://www.baidu.com" id="link">百度</a>
var link = document.getElementById('link');
link.addEventListener('click', function (e) {
  e.preventDefault();  // 取消 a 链接的默认跳转
  console.log('clicked!');
})

阻止事件冒泡

<div id="dom">
  <a id="link" href="http://www.baidu.com">激活</a>
  <a href="http://www.qq.com">取消</a>
</div>
var link = document.getElementById('link');
link.addEventListener('click', function (e) {
  e.preventDefault();
  e.stopPropagation();  // 阻止内层 a 的事件冒泡到父元素 div 上
  alert('激活');
})
var dom = document.getElementById('dom');
dom.addEventListener('click', function (e) {
  var target = e.target;
  e.preventDefault();
  if (target.nodeName === 'A') {
    alert('取消');
  }
})

事件代理

<div id="dom">
  <a href="http://www.baidu.com">百度</a>
  <a href="http://www.qq.com">腾讯</a>
</div>
var dom = document.getElementById('dom');
dom.addEventListener('click', function (e) {
  e.preventDefault();
  var target = e.target;  // 代理的重点,在获取 e.target 对象
  if (target.nodeName === 'A') {
    alert(target.innerHTML);
  }
})

代理的好处: 1、代码简介; 2、减少浏览器内存占用
具体应用: 对于一个无限下拉加载图片的页面,如何给每个图片绑定事件,就是使用事件代理。给父元素绑定事件,通过冒泡机制,获取点击目标的 nodeName 是否是 img,再执行相应的函数

通用事件绑定

<div id="dom">
  <a id="link" href="http://www.baidu.com">百度</a>
  <a href="http://www.baidu.com">百度</a>
</div>
var dom = document.getElementById('dom');
bindEvent(dom, 'click', 'a', function (e) {
  e.preventDefault();
  console.log(this.innerHTML);
})
var link = document.getElementById('link');
bindEvent(link, 'click', function (e) {
  e.preventDefault();
  e.stopPropagation();
  console.log(this.innerHTML);
})

function bindEvent (elem, type, selector, fn) {
  if (fn == null) {
    fn = selector;
    selector = null;
  }
  elem.addEventListener(type, function (e) {
    var target;
    if (selector) {
      // 使用代理
      target = e.target;
      if (target.matches(selector)) {  // 在点击目标的元素中,是否存在 selector 选择器,有返回true,无返回false
        fn.call(target, e);  // 用 call 方法调用,把 target 当 this
      }
    } else {
      // 不使用代理
      fn(e);
    }
  })
}

var result = element.matches(selectorString);  // selectorString 是一个 css 选择器字符串
原文地址:https://www.cnblogs.com/joffe/p/7675228.html