【JS交互篇】事件和元素操作

一、事件 和 元素相关属性

事件是用户或者浏览器自身执行的某种动作,如:click、load和mouseover等,都是事件的名字;

原生JavaScipt案例合集
JavaScript +DOM基础
JavaScript 基础到高级
Canvas游戏开发

1.1 窗口或元素相关事件:

事件 描述
onload 页面或图像加载完成后触发的事件
onresize 窗口发生改变时触发的事件
onscroll 窗口滚动条滚动时触发的事件

1.2 元素相关属性

属性 描述
element.clientHeight 在页面上返回内容的可视高度(不包括边框,边距或滚动条)
element.clientWidth 在页面上返回内容的可视宽度(不包括边框,边距或滚动条)
element.offsetHeight 返回任何一个元素的高度包括边框和填充,但不是边距
element.offsetWidth 返回元素的宽度,包括边框和填充,但不是边距
element.scrollTop 页面或元素顶部被卷去的距离
element.scrollLeft 页面或元素左边被卷去的距离
element.offsetLeft 返回当前元素的相对水平偏移位置的偏移容器
element.offsetTop 返回当前元素的相对垂直偏移位置的偏移容器
element.offsetParent 返回元素的偏移容器

1.4 鼠标事件

事件 描述
onclick 鼠标单击触发的事件
ondblclick 鼠标双击触发的事件
oncontextmenu 鼠标右键触发的事件
onmouseover 鼠标滑过触发的事件
onmoseout 鼠标离开触发的事件
onmouseenter 鼠标指针移动到当前元素上触发的事件
onmouseleave 鼠标指针移出当前元素触发的事件
onmousedown 鼠标被按下触发的事件
onmouseup 鼠标被松开触发的事件
onmousemove 鼠标移动事件

  1. onmouseenter 和 onmouseleave 不支持事件冒泡;而onmouseout 和 onmouseover支持事件冒泡
  2. mouseenter 事件只有在鼠标指针进入被选元素时被触发,mouseover事件在鼠标指针进入任意子元素时也会被触发;
  3. mouseleave 事件只有在鼠标指针离开被选元素时被触发,mouseout事件在鼠标指针离开任意子元素时也会被触发
  4. onclick、onmousedown、onmouseup执行的顺序 onmousedown --> onmouseup --> onclick

1.6 键盘事件

事件 描述
onkeydown 键盘按键被按下触发的事件
onkeyup 键盘按键被松开触发的事件
onkeypress 键盘按键(数字和字母)被按下并松开时触发的事件

  1. 三个键盘事件触发的顺序 onkeydown --> onkeypress --> onkeyup
  2. keypress 主要用来接收字母、数字等ANSI字符,箭头键和功能键不能被触发,如:Alt、Ctrl、Shift、Esc 等功能键
  3. keydown和keyup事件可以处理任何不被keypress识别的击键。诸如:功能键(F1-F12),编辑键、定位键以及任何这些键和键盘换档键的组合键

1.8 表单事件

事件 描述
onfocus 获取表单控件的焦点
onblur 失去表单控件的焦点
oninput 获取用户在控件中正在输入的事件
onchange 获取表单发生改变的事件
onselect 表单中内容被选中的事件
onsubmit 表单提交事件
onreset 表单信息重置事件

注:onsubmit、onreset 事件由 form 元素驱动

二、事件对象

事件对象是用来记录事件发生时相关信息的对象。如: 鼠标事件发生后,获取鼠标的横坐标 event.clientX ( clientX用来获取鼠标距离浏览器左边的距离 ) 。

事件对象只有在事件被触发时才会产生,并且只能在事件处理函数内部访问;当事件处理函数运行结束后,事件对象就被销毁。

对于不同的浏览器,事件对象获取的方式不同:

  • IE8及以下浏览器版本中,只能直接从全局中获取事件对象 window.event

  • 火狐(Firefox,简称FF)低版本浏览器中,只能通过事件传递的方式,在事件函数中传递事件对象

  • 所有高版本浏览器两者都兼容

获取事件对象的兼容性写法:

element["on" + 事件类型] = function(e){
	var ev = e || window.event;
}

事件对象通用属性:

属性 描述
type 返回事件的类型

鼠标事件对象相关属性:

属性 描述
clientX 返回当事件被触发时,鼠标指针的水平坐标。
clientY 返回当事件被触发时,鼠标指针的垂直坐标
screenX 返回当某个事件被触发时,鼠标指针的水平坐标。
screenY 返回当某个事件被触发时,鼠标指针的垂直坐标。
pageX 鼠标相对于浏览器(这里说的是浏览器的有效区域)左上角x轴的坐标; 随滚动条滚动而改变
pageY 鼠标相对于浏览器(这里说的是浏览器的有效区域)左上角y轴的坐标; 随滚动条滚动而改变
offsetX 鼠标相对于事件源左上角X轴的坐标
offsetY 鼠标相对于事件源左上角Y轴的坐标

键盘事件对象相关属性:

属性 描述
key 在按下按键时返回按键的标识符
keyCode 返回onkeypress事件触发的键的值的字符代码,或者 onkeydown 或 onkeyup 事件的键的代码
which 返回onkeypress事件触发的键的值的字符代码,或者 onkeydown 或 onkeyup 事件的键的代码

注:onkeypress 事件触发的键的值的字符代码(表示ASCII字符的数字),或者 onkeydownonkeyup 事件的键的代码(表示键盘上真实键的数字)。

三、事件处理程序

事件处理程序指的是当HTML中发生某些事件时所调用的方法。

事件处理程序分为以下几种:

  1. HTML事件处理程序

    直接在html元素中使用"on"+事件类型实现,如:onclick ,使得html元素具有能够执行js 代码的特性

    这种事件处理程序有两个缺点:

    • 存在一个时差的问题,如果将 script 程序写在页面的最底部,而用户又在 html 元素一出现就触发相应的事件,这个时候事件处理程序有可能尚不具备执行条件(即:script 程序未加载,dom 从上往下依次解析html 文件)
    • html 代码与 JS 代码紧密耦合,这样在维护或修改时需要修改两个地方。所以建议摒弃 html 事件处理程序,进而使用 JS 指定事件处理程序
  2. DOM0级事件处理程序

    通过 JS 指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性。这种操作简单且具有跨浏览器的优势。

    • 使用DOM0级方法指定的事件处理程序被认为是元素的方法;因此这时候的事件处理程序是在元素的作用域中运行;即:程序中的this引用的是当前元素
    • 这种方式添加的事件处理程序会在事件流的冒泡阶段被处理

    添加方式:element[‘on’ + 事件类型] = function(){}

    移除方式:element[‘on’ + 事件类型] = null

  3. DOM2级事件处理程序

    添加方式:element.addEventListener(type,handler,boolean)

    移除方式: element.removeEventListener(type,handler,boolean)

    注意:移除时三个对应参数事件类型、事件函数、事件冒泡或捕获阶段要一一对应,匿名函数没有办法被移除。

    IE8及以下浏览器报错: 对象不支持 “addEventListener” 属性或方法;

    可以给同一个元素定义多个同种类型的事件函数;

    第三个参数默认是false,事件冒泡流阶段。多个同种类型的事件,按照渲染顺序,依次执行。

  4. IE事件处理程序

    添加方式:element.attachEvent(‘on’+事件类型,事件函数)

    移除方式:element.detachEvent(‘on’+事件类型,事件函数)

    只能在IE10及以下浏览器版本中使用,其它浏览器报错:btn.attachEvent is not a function

    也可以添加多个同种类型的事件,执行顺序按照渲染顺序逆向执行。

    与DOM0级方法的区别在于事件处理程序的作用域不同:DOM0级是在其所属元素的作用域内运行,而IE事件处理程序是在全局作用域运行,因此this等于window。

    与DOM2级方法一样,可以为同一个按钮添加两个不同事件处理程序,但是IE事件处理程序的执行顺序是以相反的顺序触发,即先触发的是后面添加的事件。

    使用detachEvent()来移除通过attachEvent()方法添加的事件,与DOM2级方法一样,匿名函数无法被移除

添加事件的事件处理程序兼容性代码:

function addEvent(elem, type, fn) {
    if (elem.addEventListener) { //支持DOM2
        elem.addEventListener(type, fn);
    } else if (elem.attachEvent) { //IE8及以下
        elem.attachEvent('on' + type, fn)
    } else {
        elem['on' + type] = fn
    }
}

移除事件的事件处理程序兼容性代码

function removeEvent(elem, type, fn) {
    if (elem.removeEventListener) {
        elem.removeEventListener(type, fn)
    } else if (elem.detachEvent) {
        elem.detachEvent("on" + type, fn)
    } else {
        elem["on" + type] = null
    }
}

添加和移除事件处理程序的兼容性代码:

var oEvent = {
        /**
         * 添加事件的函数
         * @elem 添加事件的元素
         * @type 事件类型
         * @fn  事件执行函数
         * */
        addEvent: function (elem, type, fn) {
            if (elem.addEventListener) {
                elem.addEventListener(type, fn);
            } else if (elem.attachEvent) {
                elem.attachEvent('on' + type, fn)
            } else {
                elem["on" + type] = fn;
            }
        },
        /**
         * 添加事件的函数
         * @elem 添加事件的元素
         * @type 事件类型
         * @fn  事件执行函数
         * */
        removeEvent: function (elem, type, fn) {
            if (elem.removeEventListener) {
                elem.removeEventListener(type, fn)
            } else if (elem.detachEvent) {
                elem.detachEvent("on" + type, fn)
            } else {
                elem["on" + type] = null
            }
        },
        /**
         * 定义一个函数,元素只能执行一次事件;如:点击事件,第一次点击有效,第二次点击无效
         * */
        once: function () {

        }
}

四、事件流

事件流用来描述从页面中接收事件的顺序。

事件流分为两种:事件冒泡流和事件捕获流。这两种事件流的概念几乎完全相反。

事件流出现的前提:嵌套元素中定义了同种类型的事件。

事件捕获流只能通过 DOM2 级事件处理程序实现

4.1 事件冒泡流

IE事件流叫做事件冒泡,即事件最开始由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播至最不具体的那个节点(文档);也就是说一个标签元素处理完事件之后,它的父元素、父元素的父元素也都会处理事件(从内到外)。

4.2 事件捕获流

Netscape(网景)的事件流叫做事件捕获,不太具体的节点(最外层标签)应该更早接收到事件,而最具体的节点(最内层标签)最后接收到事件(事件执行的顺序是从外到内);由于老版本浏览器不支持,因此很少有人使用事件捕获。建议大家放心地使用事件冒泡,在有特殊需要时再使用事件捕获。

DOM2级事件处理程序添加事件 element.addEventListener(type, callbackfn, boolean),第三个参数boolean是一个布尔值,默认是false,表示绑定到冒泡阶段,如果是true则表示绑定到捕获阶段。

4.3 阻止事件冒泡或事件捕获的兼容代码

if(ev.stopPropagation){//DOM推荐标准
    ev.stopPropagation()
}else{//兼容IE678
    ev.cancelBubble = true;
}

IE8及以下浏览器:对象不支持“stopPropagation”属性或方法

4.4 阻止元素默认行为的兼容性代码:

只有有默认行为的对象,才有阻止的必要性。如:页面鼠标右键事件、a标签href链接、img标签中的src请求等等…

// 兼容性写法
if (event.preventDefault) {//dom标准写法
    event.preventDefault()
} else {//兼容IE678
    event.returnValue = false;
}

// 利用return false也能阻止默认行为,没有兼容问题(只限传统注册方式DOM0级事件处理程序)

对象不支持 “preventDefault” 属性或方法

4.5 事件代理

事件代理也叫事件委托。利用事件冒泡机制,给父元素指定一个事件处理程序,就可以管理父元素及其后代元素某一类型的所有事件;如:子元素要触发的事件交由父元素代为触发执行。

  1. 为什么要用事件委托?

    一般来说,dom需要有事件处理程序,我们都会直接给它设事件处理程序就好了,那如果是很多的dom需要添加事件处理呢?比如我们有100个li,每个li都有相同的click点击事件,可能我们会用for循环的方法,来遍历所有的li,然后给它们添加事件,那这么做会存在什么影响呢?

    • 操作DOM次数过多,造成浏览器的重排和重绘就越多;
      在JavaScript中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能,因为需要不断的与dom节点进行交互,访问dom的次数越多,引起浏览器重绘与重排的次数也就越多,就会延长整个页面的交互就绪时间,这就是为什么性能优化的主要思想之一就是减少DOM操作的原因;如果要用事件委托,就会将所有的操作放到js程序里面,与dom的操作就只需要交互一次,这样就能大大的减少与dom的交互次数,提高性能;

    • 每个事件都是一个对象,事件处理程序越多,占用的内存越多,影响前端性能;每个函数都是一个对象,是对象就会占用内存,对象越多,内存占用率就越大,自然性能就越差了(内存不够用,是硬伤,哈哈)比如上面的100个li,就要占用100个内存空间,如果是1000个,10000个呢,那只能说呵呵了,如果用事件委托,那么我们就可以只对它的父级(如果只有一个父级)这一个对象进行操作,这样我们就需要一个内存空间就够了,是不是省了很多,自然性能就会更好。

  2. 事件委托(事件代理)原理

    对“事件处理程序过多”问题的解决方案就是事件委托,事件委托利用事件冒泡(从最深的节点开始,然后逐步向上传播事件)只在他们的父素上指定一个事件处理程序,就可以管理某一类型的的所有事件。这就是事件委托,委托它们父级代为执行事件。

  3. 事件委托的实现:查找事件源,IE和DOM事件处理程序获取方式如下:

    • DOM中的事件对象:事件对象.target 获取事件目标(事件源)

    • IE中的事件对象: 事件对象.srcElement 获取事件目标(事件源)

    • 获取事件目标兼容性写法:

      element["on"+事件类型] = function(e){
          var ev = e || event;
          //获取目标对象
          //支持DOM事件的浏览器使用target来获取事件目标
          //IE8及以前版本浏览器通过srcElement来获取事件目标
          var target = ev.target || ev.srcElement; 
      }
      
  4. 事件委托的使用场景

    • 为DOM中的很多元素绑定相同事件;

    • 为DOM中尚不存在的元素绑定事件。

  5. 事件委托的优点

    • 适合事件委托的事件有:click,mousedown,mouseup,keydown,keyup,keypress
    • 减少DOM操作:减少浏览器的重绘(repaint)和重排(reflow),从而提高性能;
    • 减少内存空间的占用率,因为每一个函数都是一个对象,对象越多,内存占有率就越大,自然性能就越差,使用事件委托,只需要在其父元素中定义一个事件就可以。

五、鼠标滚轮事件

代码:

// 鼠标滚轮事件   
// onmousewheel  谷歌和IE8以上支持的鼠标滚轮事件;火狐浏览器不支持onmousewheel形式,无响应
// window.onmousewheel = function(e){
//     var ev = e || event;
//     console.log(ev);//WheelEvent {isTrusted: true, deltaX: -0, deltaY: 100, deltaZ: 0, deltaMode: 0, …}
//     console.log(ev.type);//mousewheel
// }

// DOMMouseScroll  只能使用DOM2级事件处理程序;火狐浏览器可以正常使用;谷歌和IE8以上不支持,无响应
// window.addEventListener("DOMMouseScroll",function(e){
//     console.log(e)
// })

// 鼠标滚轮事件的兼容性代码
window.onmousewheel = wheelHandler;
window.addEventListener('DOMMouseScroll', wheelHandler);
function wheelHandler(e) { //事件对象传参是DOM标准推荐的方式
    console.log(e)
}

注:鼠标滚轮事件的属性

  1. 谷歌浏览器:属性 deltaY 垂直方向滚动的距离,滚轮向下滚动,每次滚动的距离是 100px,向上滚动,每次滚动的距离是 -100px。它是固定的值。
  2. 谷歌浏览器、IE8以上浏览器,属性 wheelDelta 固定值 120,向下是负值 -120,向上是正值 +120
  3. 火狐浏览器:属性 detail 固定值 3,向上是负值 -3 ,向下是正值 +3

你可能感兴趣的:(前端开发,JavaScript,javascript,交互,前端,DOM,事件,元素)