【前端系列教程之JavaScript】10_DOM编程详解_表格操作和事件

一、JavaScript操作表格

1.1、操作表格API

1、HTML DOM中,给table操作提供了一些属性和方法。

table元素节点的独有属性和方法:

属性或方法说明
caption保存着<caption>元素的引用
tBodies保存着<tbody>元素的HTMLCollection 集合
tFoot保存着对<tfoot>元素的引用
tHead保存着对<thead>元素的引用
createTHead()创建<thead>元素,并返回引用
createTFoot()创建<tfoot>元素,并返回引用
createTBody()创建<tbody>元素,并且返回引用
createCaption()创建<caption>元素,并返回引用
deleteTHead()删除<thead>元素
deleteTFoot()删除<tfoot>元素
deleteCaption()删除<caption>元素

2、<tbody>/<thead>/<tfoot>/<table>元素添加的属性和方法

属性或方法说明
rows保存着指定元素中行的HTMLCollection
deleteRow(pos)删除指定位置的行
insertRow(pos)向rows 集合中的指定位置插入一行,并返回引用

3、<tr>元素添加的属性和方法

属性或方法说明
cells保存着<tr>元素中单元格的HTMLCollection
deleteCell(pos)删除指定位置的单元格
insertCell(pos)向cells集合的指定位置插入一个单元格,并返回引用

1.2、案例

使用新API实现动态创建表格;

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        /* 我们如果直接写标签名,这就是所谓的标签选择器 */
        /* 选择器{}     给指定的标签添加样式 */
        /* 选择器1,选择器2,...{}    给多个指定的元素添加样式 */
        table,
        td,
        th {
            border: 1px solid black;
        }

        /* 
            一般情况下: 我们给表格设置宽度;
            一般情况下: 我们给行设置高度;
         */
        table {
            /* border-collapse 边框合并 */
            border-collapse: collapse;
            margin: 0 auto;
            width: 800px;
            /* 我们给表格设置文本居中,那么td单元格会继承到该样式 */
            /* text-align: center;  */
        }

        tr {
            height: 38px;
        }

        caption {
            font-size: 40px;
            font-weight: bold;
            height: 80px;
            line-height: 80px;
        }

        /* tbody>tr    选择tbody里面所有的行 */
        tbody>tr:nth-child(odd) {
            background: #eee;
        }

        thead td {
            text-align: center;
            font-weight: bold;
        }
    </style>
</head>

<body>
    <table id="tab1">

    </table>
    <script>
        // 获取得到table元素节点
        var stuTable = document.getElementById("tab1");

        // 创建thead、tbody
        var thead = stuTable.createTHead();
        var tbody = stuTable.createTBody();


        // 表头数据
        var headDatas = ['姓名', '科目', '成绩', '操作'];

        // 在thead里面创建一行
        var theadTr = thead.insertRow(0);

        for (var i in headDatas) { //使用for in来遍历数组的时候,in前面的变量表示数组的下标
            // 获取每一条表头数据
            // theadTr.insertCell(i) 根据下标创建行,并且给行插入内容
            theadTr.insertCell(i).innerText = headDatas[i];;
        }

        // 模拟数据
        var datas = [{
                name: '张三',
                subject: '语文',
                score: 90
            },
            {
                name: '李四',
                subject: '数学',
                score: 80
            },
            {
                name: '王武',
                subject: '英语',
                score: 99
            },
            {
                name: '马六',
                subject: '英语',
                score: 100
            },
            {
                name: '田七',
                subject: '英语',
                score: 60
            },
            {
                name: '赵八',
                subject: '英语',
                score: 70
            }
        ];

        // 根据上述数据,在tbody里面生成行及其单元格和数据
        // datas数组里有几个元素,就应该创建几行; 每个元素里面的对象有多少个key-vaue就创建多少个单元格
        for (var i in datas) { // datas里面存储的6个学生对象
            // 通过下标获取到每个学生对象
            var stuInfo = datas[i];

            // 每个学生都对应了一行数据
            var tbodyTr = tbody.insertRow(i);

            var index = 0;
            // 学生的每一个数据就是一个单元格
            for (var j in stuInfo) { // j在遍历对象的时候,是字符串的key
                // j  "name"    "subject"   "score"
                tbodyTr.insertCell(index++).innerText = stuInfo[j];
            }

            tbodyTr.insertCell(index).innerHTML = "<a href='javascript:void(0)'>删除</a>";

        }
    </script>

</body>

</html>

二、JavaScript事件(重点)

2.1、事件介绍

        JavaScript与HTML之间的交互是通过事件来实现的,事件就是用户或浏览器自身执行的某种动作,比如click、mounseover、load……,而响应事件的函数就叫做事件处理函数

        事件(Event)是JavaScript应用跳动的心脏 ,也是把所有东西粘在一起的胶水。当我们与浏览器中Web页面进行某些类型的交互时,事件就发生了。事件可能是用户在某些内容上的点击、鼠标经过某个特定元素或按下键盘上的某些按键。事件还可能是Web浏览器中发生的事情,比如说某个Web页面加载完成,或者是用户滚动窗口或改变窗口大小。

事件:触发-响应机制

事件三要素:

  • 事件源:触发(被)事件的元素

  • 事件名称: click 点击事件

  • 事件处理程序:事件触发后要执行的代码(函数形式)

事件发生常见情况:

当鼠标点击某个地方的时候,发生了...

当鼠标移入某个地方的时候,发生了...

页面载入完毕的时候,发生了...

当输入框获取光标的时候,发生了...

当输入框失去光标的时候,发生了...

当按下某个键的时候,发生了...

        通过使用 JavaScript ,你可以监听特定事件的发生,并规定让某些事件发生以对这些事件做出响应。JavaScript 事件是由访问Web页面的用户引起的一系列操作,例如:用户点击。当用户执行某些操作的时候,再去执行一系列代码。

        注意:事件通常与函数配合使用,当事件发生时函数才会执行。

2.2、事件三种使用方式

        事件一般是用于浏览器和用户操作进行交互。

        使用JavaScript有三种方法:内联(行内)、脚本和事件侦听;

2.2.1、内联方式

        这种模型是最传统简单的一种处理事件的方法。在内联模型中,事件处理函数是HTML标签的一个属性,用于处理指定事件。

        虽然内联在早期使用较多,但它是和HTML混写的,并没有与HTML分离。

//在HTML 中把事件处理函数作为属性执行JS代码
<div id="div1" onclick="this.style.background='blue'">我是div里面的文字</div>

//在HTML 中把事件处理函数作为属性执行JS 函数1
<div id="div1" onclick="myfun1(this)">我是div里面的文字</div>

<script>
    function myfun1(t){t.style.background='blue'}
</script>

        注:内联可以给一个元素定义的同一事件添加多个执行函数

<div id="div1" onclick="myfun1(),myfun2()">我是div里面的文字</div>

2.2.2、脚本方式

        由于内联模型违反了HTML 与JavaScript 代码层次分离的原则。为了解决这个问题,我们可以在JavaScript 中处理事件。这种处理方式就是脚本模型。

        获取元素,给其加事件,事件赋值某个函数名,切记不加括号.

// 获取页面元素
var div1 = document.getElementById("div1");

// 定义事件函数
function myfun1(){
  div1.style.background="blue";
}

// 给元素绑定事件及其事件函数
div1.onclick=myfun1;

匿名函数写法:

var div1 = document.getElementById("div1");

div1.onclick=function(){
	div1.style.background="blue";
}

 // 脚本方式移除事件处理函数
div1.onclick = null;

        通过匿名函数,可以直接触发对应的代码。

        注:脚本形式不可以给同一事件添加多个执行函数;

2.2.3、事件监听(侦听)方式

        JavaScript中定义了两个方法,用于添加事件和删除事件处理程序的操作:addEventListener()和removeEventListener()。

        所有DOM 节点中都包含这两个方法,并且它们都接受3个参数;事件类型、事件处理函数、冒泡或捕获的布尔值(true 表示捕获,false 表示冒泡)。

        标准的浏览器使用W3C定义的addEventListener函数绑定,函数定义如下:

        function addEventListener(string eventType, function eventFunc, [bool useCapture=false]) ​ ​                 参数eventType: 事件名称,如click、mouseover… ​ ​

                eventFunc: 绑定到事件中执行的动作 ​ ​

                useCapture: 指定是否绑定在捕获阶段,true为是,false为否,默认为false。 ​

        注:事件冒泡和捕捉在之后讲!

document.addEventListener('click', function () {
  alert('document');
});

box.addEventListener('click', function () {
  alert('Lee');
});

        事件侦听的优势:

        1、当同一个对象使用.onclick的写法触发多个方法的时候,后一个方法会把前一个方法覆盖掉,也就是说,在对象的onclick事件发生时,只会执行最后绑定的方法。

        而用事件监听则不会有覆盖的现象,每个绑定的事件都会被执行。如下:

window.onload = function(){

  var btn = document.getElementById("yuanEvent");
  btn.onclick = function(){
    alert("第一个事件");
  }

  btn.onclick = function(){
    alert("第二个事件");
  }

  btn.onclick = function(){
    alert("第三个事件");
  }
}

最后只输出:第三个事件,因为后一个方法都把前一个方法覆盖掉。

原生态的事件绑定函数addEventListener:
function eventOne (){
  alert("第一个监听事件");
}

function eventTwo(){
  alert("第二个监听事件");
}

window.onload = function(){
  var btn = document.getElementById("yuanEvent");
  //addEventListener:绑定函数
  btn.addEventListener("click",eventOne);
  btn.addEventListener("click",eventTwo);
}
输出:第一个监听事件和第二个监听事件

        2、采用事件监听给对象绑定方法后,可以解除相应的绑定,写法如下:

var eventOne = function(){
  alert("第一个监听事件");
}
function eventTwo(){
  alert("第二个监听事件");
}
  var btn = document.getElementById("yuanEvent");
  btn.addEventListener("click",eventOne);
  btn.addEventListener("click",eventTwo);
  btn.removeEventListener("click",eventOne);
输出:第二个监听事件
解除绑定事件的时候一定要用函数的句柄,把整个函数写上是无法解除绑定的。

2.2.4、IE兼容

        IE9以前不支持addEventListener和removeEventListener方法;

        实现了两个类似的方法:

  • attachEvent

  • detachEvent

// 第一个参数,事件的名称前要加 on
box.attachEvent('onclick', eventCode);
box.detachEvent('onclick', eventCode);

function eventCode() {
  console.log('点击后执行');
}

        这两个方法都接受两个相同的参数。

                1.事件处理程序名称

                2.事件处理程序方法

        attachEvent——兼容:IE7、IE8;不兼容firefox、chrome、IE9、IE10、IE11、safari、opera

        addEventListener——兼容:firefox、chrome、IE、safari、opera;不兼容IE7、IE8

function addEventListener(element, type, fn) {
    if (element.addEventListener) {
        element.addEventListener(type, fn, false);
    } else if (element.attachEvent) {
        element.attachEvent('on' + type, fn);
    } else {
        element['on' + type] = fn;
    }
}

function removeEventListener(element, type, fn) {
    if (element.removeEventListener) {
        element.removeEventListener(type, fn, false);
    } else if (element.detachEvent) {
        element.detachEvent('on' + type, fn);
    } else {
        element['on' + type] = null;
    }
}

addEventListener(box, "click", function () {
    console.log("兼容解决策略")
})

2.3、常用的事件类型

        JavaScript 可以处理的事件类型为:鼠标事件、键盘事件、框架事件、表单事件。

        所有的事件处理函数都会都有两个部分组成,on + 事件名称,例如click 事件的事件处理函数就是:onclick。在这里,我们主要谈论脚本的方式来构建事件,违反分离原则的内联,我们忽略掉。

鼠标事件:

        1、click 当用户点击某个对象时调用的事件句柄。

        2、dblclick 当用户双击某个对象时调用的事件句柄。

        3、mousedown 鼠标按钮被按下。

        4、mouseup 当用户释放鼠标按钮时触发[鼠标左右键]。

        5、mouseover 鼠标移到某元素之上。

        6、mouseout 鼠标从某元素移开。

        7、mousemove 当鼠标被移动。

        8、mouseenter 当鼠标指针移动到元素上时触发。

        9、mouseleave 当鼠标指针移出元素时触发

        onmouseenter 事件类似于 onmouseover 事件。 唯一的区别是 onmouseenter 事件不支持冒泡 。

键盘事件:

        1、keydown:当用户按下键盘上任意键触发,如果按住不放,会重复触发。

        onkeydown = function () {alert('Lee');};

        2、keypress:当用户按下键盘上的字符键触发,如果按住不放,会重复触发。

        onkeypress = function () {alert('Lee');};

        3、keyup:当用户释放键盘上的键触发。

        onkeyup = function () {alert('Lee');};

        注:键盘事件一般用在可获取焦点的元素上或doument对象上

        在发生键盘事件时,键盘事件对象的keyCode 属性中会包含一个代码,与键盘上一个特定的键对应。

keypress 和 keydown 是有区别?

        1.keydown 先于 keypress 发生。

        2.keypress 无法系统按钮。

        3.keydown 捕获的 keyCode 不区分字母大小,而 keypress 区分。

        4.keypress是在用户按下并放开任何字母数字键时发生。系统按钮(例如,箭头键和功能键)无法得到识别。

        5.keyup 是在用户放开任何先前按下的键盘键时发生。

        6.keydown 是在用户按下任何键盘键(包括系统按钮,如箭头键和功能键)时发生。

框架/对象(Frame/Object)事件

        1、load 当页面完全加载后在window 上面触发,或当框架集加载完毕后在框架集上触发。当页面完全加载所有内容(包括图像、脚本文件、CSS 文件等)执行

        2、unload 用户退出页面。 ( <body><frameset>)

<div id="box">
    
</div>
<script>
    // BOM   onload  页面加载完成之后执行  
    // 页面加载完成 页面上所有的元素创建完毕,并且引用的外部资源下载完毕(js,css,图片)
    // window.onload = function () {
    onload = function () {
    var box = document.getElementById('box');
    console.log(box);
    }

    // window.onunload = function () {
    // 页面卸载的时候执行
    onunload = function () {
    // 在onunload中所有的对话框都无法使用 window 对象被冻结
    // Blocked alert('欢迎下次再来') during unload.
    // alert('欢迎下次再来');
    console.log('bye bye');
    }
    
    // f5 刷新页面  
    // 1 卸载页面
    // 2 重新加载页面
</script>

        3、beforeunload 该事件在即将离开页面(刷新或关闭)时触发

        4、hashchange 该事件在当前 URL 的锚部分发生修改时触发。

        5、resize 当窗口或框架的大小变化时在window 或框架上触发。

        6、scroll 当用户滚动时触发。

表单事件:

        1、select 当用户选择文本框(input 或textarea)中的一个或多个字符触发。

        2、change 该事件在表单元素的内容改变时触发( <input>, <keygen>, <select>, 和 <textarea>)。

        3、focus 元素获取焦点时触发。

        4、blur 元素失去焦点时触发。

        5、submit 当用户点击提交按钮在<form>元素上触发。

        6、reset 当用户点击重置按钮在<form>元素上触发。

<form action="" onsubmit="myFun1()" onreset="myfun2()">
    <input type="text" name="userName" placeholder="请输入您的名字" > <br>
    <input type="submit" value="提交表单"><input type="reset" value="重置表单">
</form>
<script>
    function myFun1(){
        var userName = document.getElementsByTagName("input")[0].value;
        alert("hello,"+userName);
    }
    function myfun2(){alert("您重置了表单");}
</script>

        7、input 元素获取用户输入时触发

2.4、事件的冒泡和捕获

2.4.1、事件冒泡和捕获

        什么是事件冒泡?

        在一个对象上触发某类事件(比如单击click事件),如果此对象定义了此事件的处理程序,那么此事件就会调用这个处理程序,如果没有定义此事件处理程序或者事件返回true,那么这个事件会向这个对象的父级对象传播,从里到外,直至它被处理(父级对象所有同类事件都将被激活),或者它到达了对象层次的最顶层,即document对象(有些浏览器是window)。

        事件的冒泡:事件按照从最特定的事件目标到最不特定的事件目标的顺序触发。

        事件冒泡可以形象地比喻为把一颗石头投入水中,泡泡会一直从水底冒出水面。也就是说,事件会从最内层的元素开始发生,一直向上传播,直到document对象。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
        <style>
            #box1 {
                width: 300px;
                height: 300px;
                background-color: red;
            }

            #box2 {
                width: 200px;
                height: 200px;
                background-color: green;
            }

            #box3 {
                width: 100px;
                height: 100px;
                background-color: blue;
            }
        </style>
    </head>
    <body>
        <div id="box1">
            <div id="box2">
                <div id="box3">
                </div>
            </div>
        </div>
        <script>
            // addEventListener 的第三个参数的作用
            var box1 = document.getElementById('box1');
            var box2 = document.getElementById('box2');
            var box3 = document.getElementById('box3');

            var array = [box1, box2, box3];

            // addEventListener 的第三个参数为false的时候 : 事件冒泡
            // addEventListener 的第三个参数为true的时候 :  事件捕获

            for (var i = 0; i < array.length; i++) {
                array[i].addEventListener('click', function () {
                    console.log(this.id);
                }, false);
            }

            document.body.addEventListener('click', function () {
                console.log('body');
            }, false);
        </script> 
    </body>
</html>

        事件的捕获:与事件冒泡相反,事件会从最外层开始发生,直到最具体的元素。

        事件捕获时,父级元素先触发,子级元素后触发。

        可以自己选择绑定事件时采用事件捕获还是事件冒泡,方法就是绑定事件时通过addEventListener函数,它有三个参数,第三个参数若是true,则表示采用事件捕获,若是false,则表示采用事件冒泡。

        IE9以前的版本只支持事件冒泡,不支持事件捕获,它也不支持addEventListener函数,不会用第三个参数来表示是冒泡还是捕获,它提供了另一个函数attachEvent。

2.4.2、事件的三个阶段

        事件的处理过程主要有三个阶段:捕获阶段,目标阶段,冒泡阶段;

        捕获阶段:当我们在 DOM 树的某个节点发生了一些操作(例如单击、鼠标移动上去),就会有一个事件发射过去。这个事件从 Window 发出,不断经过下级节点直到触发的目标节点。在到达目标节点之前的过程,就是捕获阶段(Capture Phase)。事件由页面元素接收,逐级向下,到具体的元素。

         目标阶段:当事件不断的传递直到目标节点的时候,最终在目标节点上触发这个事件,就是目标阶段。具体的元素本身。

        冒泡阶段:事件冒泡即事件开始时,由最具体的元素接收(也就是事件发生所在的节点),然后逐级传播到较为不具体的节点。跟捕获相反,具体元素本身,逐级向上,到页面元素(我们平时用的事件绑定就是利用的事件冒泡的原理)。

         W3C : 任何事件发生时,先从顶层开始进行事件捕获,直到事件触发到达事件源,再从事件源向上进行事件捕获(事件冒泡)。

        事件对象.eventPhase属性可以查看事件触发时所处的阶段

2.4.3、事件对象的属性和方法(核心)

        在标准的DOM 事件中,event 对象包含创建它的特定事件有关的属性和方法。Event 对象代表事件的状态,比如事件在其中发生的元素、键盘按键的状态、鼠标的位置、鼠标按钮的状态。

        什么时候会产生Event 对象呢? 
                事件对象,我们一般称作为event 对象,这个对象是浏览器通过函数把这个对象作为参数传递过来的。通过事件绑定的执行函数是可以得到一个隐藏参数的,这个参数其实就是event对象。

        事件通常与函数结合使用,函数不会在事件发生前被执行!

var oDIv = document.getElementById('box');

oDiv.onclick = function(event){
    .........
}
  • event.eventPhase 返回事件传播的当前阶段。

  • event.target || event.srcElement 返回触发此事件的元素(事件的目标节点)。

  • event.currentTarget 返回其事件监听器触发该事件的元素

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style>
    #box1 {
      width: 300px;
      height: 300px;
      background-color: red;
    }

    #box2 {
      width: 200px;
      height: 200px;
      background-color: green;
    }

    #box3 {
      width: 100px;
      height: 100px;
      background-color: blue;
    }
  </style>
</head>

<body>
  <input type="button" value="按钮" id="btn">
  <div id="box1">
    <div id="box2">
      <div id="box3">
      </div>
    </div>
  </div>
  <script>
    // 通过事件对象,可以获取到事件发生的时候和事件相关的一些数据
    var btn = document.getElementById('btn');
    btn.onclick = function (e) {
      // DOM标准中,是给事件处理函数一个参数
      // e就是事件对象
      // 在老版本的IE中获取事件对象的方式  window.event
      // 处理事件对象的浏览器兼容性
      e = e || window.event;

      // 事件的阶段:1  捕获阶段   2  目标阶段  3 冒泡阶段    了解
      // console.log(e.eventPhase);

      // e.target  获取真正触发事件的对象  浏览器兼容问题
      // 在老版本的IE中  srcElement
      // 处理兼容性问题
      var target = e.target || e.srcElement;
      console.log(e.target);
        
      // e.currentTarget  事件处理函数所属的对象this
      console.log(e.currentTarget);
    }

    var box1 = document.getElementById('box1');
    var box2 = document.getElementById('box2');
    var box3 = document.getElementById('box3');
    var array = [box1, box2, box3];
    for (var i = 0; i < array.length; i++) {
      var box = array[i];
      box.onclick = function (e) {
        e = e || window.event;
        // 事件的阶段
        console.log(e.eventPhase);
        // e.target 获取真正触发事件的对象
        var target = e.target || e.srcElement;
        console.log(target);
        // e.curentTarget   和this一样 获取事件处理函数所属的对象
        console.log(e.currentTarget);
        console.log(this);
      }
    }
  </script>
</body>

</html>
  • event.bubbles 返回布尔值,指示事件是否是冒泡事件类型

  • event.cancelable 返回布尔值,指示事件是否可拥可取消的默认动作。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style>
    #box1 {
      width: 300px;
      height: 300px;
      background-color: red;
    }

    #box2 {
      width: 200px;
      height: 200px;
      background-color: green;
    }

    #box3 {
      width: 100px;
      height: 100px;
      background-color: blue;
    }
  </style>
</head>

<body>
  <input type="button" value="按钮" id="btn">
  <div id="box1">
    <div id="box2">
      <div id="box3">
      </div>
    </div>
  </div>

  <input type="text" id="input1">
  <script>
    var box1 = document.getElementById('box1');
    var box2 = document.getElementById('box2');
    var box3 = document.getElementById('box3');
    var input1 = document.getElementById('input1');
    var array = [box1, box2, box3];
    for (var i = 0; i < array.length; i++) {
      var box = array[i];
      //冒泡事件类型:click,mousedown,mouseup,keydown,keyup,keypress等
      box.onclick = function (e) {
        e = e || window.event;
        console.log(e);
        // 是否是冒泡事件类型
        console.log(e.bubbles);
        // 是否可拥可取消的默认动作
        console.log(e.cancelable);
      }

    }

    input1.onfocus = function (e) {
      e = e || window.event;
      // 是否是冒泡事件类型
      console.log(e.bubbles);
      // 是否可拥可取消的默认动作
      console.log(e.cancelable);
    }
  </script>
</body>

</html>
  • event.type 获取事件类型

  • event.timeStamp 返回事件生成的日期和时间。

var box1 = document.getElementById('box1');
var box2 = document.getElementById('box2');
var box3 = document.getElementById('box3');
var input1 = document.getElementById('input1');
var array = [box1, box2, box3];
for (var i = 0; i < array.length; i++) {
    var box = array[i];
    //冒泡事件类型:click,mousedown,mouseup,keydown,keyup,keypress等
    box.onclick = function (e) {
        e = e || window.event;
        // 获取事件类型
        console.log(e.type);
        //   返回事件生成的日期和时间。
        console.log(e.timeStamp)
    }

}

input1.onfocus = function (e) {
    e = e || window.event;
    // 获取事件类型
    console.log(e.type);
    //   返回事件生成的日期和时间。
    console.log(e.timeStamp)
}

// 获取时间类型的作用: 可以给多个事件绑定一个事件函数,提升性能
var box = document.getElementById('box');
// box.onclick = function (e) {
//   e = e || window.event;
//   // 获取事件名称
//   console.log(e.type);
// }

box.onclick = fn;
box.onmouseover = fn;
box.onmouseout = fn;

function fn(e) {
    e = e || window.event;
    switch (e.type) {
        case 'click': 
            console.log('点击box');
            break;
        case 'mouseover': 
            console.log('鼠标经过box');
            break;
        case 'mouseout': 
            console.log('鼠标离开box');
            break;
    }
}
  • event.clientX/clientY 所有浏览器都支持,窗口位置

  • event.pageX/pageY IE9以前不支持,页面位置

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style>
    body {
      margin: 0;
      height: 1000px;
    }
    #box {
      margin: 100px;
      margin-top: 500px;
      width: 200px;
      height: 200px;
      background-color: red;
    }
  </style>
</head>
<body>
  <div id="box">
  </div> 
  <script>
    var box = document.getElementById('box');
    box.onclick = function (e) {
      e = e || window.event;

      // 获取的鼠标在浏览器的可视区域的坐标
      // console.log(e.clientX);
      // console.log(e.clientY);

      // 鼠标在当前页面的位置
      console.log(e.pageX);
      console.log(e.pageY);

    }

  </script>
</body>
</html>

pageX和pageY兼容性解决

        e.clientX/e.clientY 鼠标在可视区域中的位置

        e.pageX/e.pageY 鼠标在页面中的位置 有兼容性问题 从IE9以后才支持

        pageY = clientY + 页面滚动出去的距离

步骤1:获取页面滚动出去的距离

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style>
    body {
      height: 1000px;
    }

    div {
      width: 300px;
      height: 300px;
      background: yellow;
      margin-top: 360px;
    }
  </style>
</head>

<body>
  <div>

  </div>
  <script>
    document.onclick = function () {
      // 输出页面滚动出去的距离
      console.log(document.body.scrollLeft);
      console.log(document.body.scrollTop);

      // documentElement  文档的根元素  html标签
      // console.log(document.documentElement);
      // 有些浏览器 是使用这两个属性来获取的
      console.log(document.documentElement.scrollLeft);
      console.log(document.documentElement.scrollTop);

    }

    // 获取页面滚动距离的浏览器兼容性问题
    // 获取页面滚动出去的距离
    function getScroll() {
      var scrollLeft = document.body.scrollLeft || document.documentElement.scrollLeft;
      var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
      return {
        scrollLeft: scrollLeft,
        scrollTop: scrollTop
      }
    }
  </script>
</body>

</html>
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style>
    body {
      height: 1000px;
    }
  </style>
</head>

<body>

  <script>
    // e.clientX/e.clientY   鼠标在可视区域中的位置
    // e.pageX/e.pageY       鼠标在页面中的位置 有兼容性问题  从IE9以后才支持
    // pageY = clientY + 页面滚动出去的距离
    document.onclick = function (e) {
      e = e || window.event;
      console.log(getPage(e).pageX);
      console.log(getPage(e).pageY);

    }

    // 获取鼠标在页面的位置,处理浏览器兼容性
    function getPage(e) {
      var pageX = e.pageX || e.clientX + getScroll().scrollLeft;
      var pageY = e.pageY || e.clientY + getScroll().scrollTop;
      return {
        pageX: pageX,
        pageY: pageY
      }
    }

    // 获取页面滚动距离的浏览器兼容性问题
    // 获取页面滚动出去的距离
    function getScroll() {
      var scrollLeft = document.body.scrollLeft || document.documentElement.scrollLeft;
      var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
      return {
        scrollLeft: scrollLeft,
        scrollTop: scrollTop
      }
    }
  </script>
</body>

</html>
  • 修改键

        有时,我们需要通过键盘上的某些键来配合鼠标来触发一些特殊的事件。这些键为:Shfit、Ctrl、Alt,它们经常被用来修改鼠标事件和行为,所以叫修改键。

        属性                 说明

        shiftKey         判断是否按下了Shfit 键

        ctrlKey           判断是否按下了ctrlKey 键

        altKey            判断是否按下了alt 键

1、判断是否按下alt键、shift键、ctrl键

document.onkeydown = function (e) {
    if(e.altKey){
        alert("alt按下");
    }else if(e.shiftKey){
        alert("shift按下");
    }else if(e.ctrlKey){
        alert("ctrl按下");
    }else{
        alert("按下的不是alt/shift/ctrl");
    }
}

2、判断alt键、shift键、ctrl键是否同时按下

document.onkeydown = function (e) {
    if(e.altKey && e.shiftKey ){
        alert("同时按下");
    }
}


document.onkeydown = function (e) {
    if(e.altKey && e.shiftKey && e.ctrlKey){
        alert("同时按下");
    }
}
  • 键码

        在发生keydown 和keyup 事件时,event 对象的keyCode 属性中会包含一个代码,与键盘上一个特定的键对应。对数字字母字符集,keyCode 属性的值与ASCII 码中对应小写字母或数字的编码相同。字母中大小写不影响。

document.onkeydown = function (evt) {
	alert(evt.keyCode); //按任意键,得到相应的keyCode
};

// 在键盘上每个键都有一个keyCode,可以通过如下方法获取每个键对应的keyCode。
document.onkeydown = function (e) {
   alert(e.keyCode);
}

document.onkeypress = function (e) {
   alert(e.keyCode);
}

        Firefox、Chrome和Safari 的event 对象都支持一个charCode 属性,这个属性只有在发生keypress 事件时才包含值,而且这个值是按下的那个键所代表字符的ASCII 编码。此时的keyCode 通常等于0 或者也可能等于所按键的编码。IE 和Opera 则是在keyCode 中保存字符的ASCII 编码。

document.onkeypress = function (e) {
   var key = String.fromCharCode(e.charCode);
    alert(key);
}

        需要注意的是,可以使用String.fromCharCode()将ASCII 编码转换成实际的字符。

2.4.4、事件委托

什么叫事件委托呢?

        JavaScript高级程序设计上讲:事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。

举例:

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

这里其实还有2层意思的:

        第一,现在委托前台的同事是可以代为签收的,即程序中的现有的dom节点是有事件的;

        第二,新员工也是可以被前台MM代为签收的,即程序中新添加的dom节点也是有事件的。

为什么要用事件委托?

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

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

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

事件委托的原理:

        事件委托是利用事件的冒泡原理来实现的

事件委托怎么实现:

<ul id="ul1">
    <li>111</li>
    <li>222</li>
    <li>333</li>
    <li>444</li>
</ul>

实现功能是点击li,弹出123:

window.onload = function(){
    var oUl = document.getElementById("ul1");
    var aLi = oUl.getElementsByTagName('li');
    for(var i=0;i<aLi.length;i++){
        aLi[i].onclick = function(){
            alert(123);
        }
    }
}

        上面的代码的意思很简单,相信很多人都是这么实现的,我们看看有多少次的dom操作,首先要找到ul,然后遍历li,然后点击li的时候,又要找一次目标的li的位置,才能执行最后的操作,每次点击都要找一次li;

        那么我们用事件委托的方式做又会怎么样呢?

window.onload = function(){
    var oUl = document.getElementById("ul1");
   oUl.onclick = function(){
        alert(123);
    }
}

        这里用父级ul做事件处理,当li被点击时,由于冒泡原理,事件就会冒泡到ul上,因为ul上有点击事件,所以事件就会触发,当然,这里当点击ul的时候,也是会触发的,那么问题就来了,如果我想让事件代理的效果跟直接给节点的事件效果一样怎么办,比如说只有点击li才会触发,不怕,我们有绝招:

        Event对象提供了一个属性叫target,可以返回事件的目标节点,我们成为事件源,也就是说,target就可以表示为当前的事件操作的dom,但是不是真正操作dom,当然,这个是有兼容性的,标准浏览器用ev.target,IE浏览器用event.srcElement,此时只是获取了当前节点的位置,并不知道是什么节点名称,这里我们用nodeName来获取具体是什么标签名,这个返回的是一个大写的,我们需要转成小写再做比较(习惯问题):

window.onload = function(){
  var oUl = document.getElementById("ul1");
  oUl.onclick = function(ev){
    var ev = ev || window.event;
    var target = ev.target || ev.srcElement;
       // 检查事件源e.target是否为Li 
    if(target.nodeName.toLowerCase() == 'li'){
         alert(123);
         alert(target.innerHTML);
    }
  }
}

        这样改下就只有点击li会触发事件了,且每次只执行一次dom操作,如果li数量很多的话,将大大减少dom的操作,优化的性能可想而知!

        上面的例子是说li操作的是同样的效果,要是每个li被点击的效果都不一样,那么用事件委托还有用吗?

<div id="box">
    <input type="button" id="add" value="添加" />
    <input type="button" id="remove" value="删除" />
    <input type="button" id="move" value="移动" />
    <input type="button" id="select" value="选择" />
</div>
window.onload = function(){
    var Add = document.getElementById("add");
    var Remove = document.getElementById("remove");
    var Move = document.getElementById("move");
    var Select = document.getElementById("select");

    Add.onclick = function(){
        alert('添加');
    };
    Remove.onclick = function(){
        alert('删除');
    };
    Move.onclick = function(){
        alert('移动');
    };
    Select.onclick = function(){
        alert('选择');
    }

}

        上面实现的效果我就不多说了,很简单,4个按钮,点击每一个做不同的操作,那么至少需要4次dom操作,如果用事件委托,能进行优化吗?

window.onload = function(){
    var oBox = document.getElementById("box");
    oBox.onclick = function (ev) {
        var ev = ev || window.event;
        var target = ev.target || ev.srcElement;
        if(target.nodeName.toLocaleLowerCase() == 'input'){
            switch(target.id){
                case 'add' :
                    alert('添加');
                    break;
                case 'remove' :
                    alert('删除');
                    break;
                case 'move' :
                    alert('移动');
                    break;
                case 'select' :
                    alert('选择');
                    break;
            }
        }
    }

}

        用事件委托就可以只用一次dom操作就能完成所有的效果,比上面的性能肯定是要好一些的

现在讲的都是document加载完成的现有dom节点下的操作,那么如果是新增的节点,新增的节点会有事件吗?也就是说,一个新员工来了,他能收到快递吗?

        看一下正常的添加节点的方法:

<input type="button" name="" id="btn" value="添加" />
<ul id="ul1">
    <li>111</li>
    <li>222</li>
    <li>333</li>
    <li>444</li>
</ul>

        现在是移入li,li变红,移出li,li变白,这么一个效果,然后点击按钮,可以向ul中添加一个li子节点

window.onload = function(){
    var oBtn = document.getElementById("btn");
    var oUl = document.getElementById("ul1");
    var aLi = oUl.getElementsByTagName('li');
    var num = 4;

    //鼠标移入变红,移出变白
    for(var i=0; i<aLi.length;i++){
        aLi[i].onmouseover = function(){
            this.style.background = 'red';
        };
        aLi[i].onmouseout = function(){
            this.style.background = '#fff';
        }
    }
    //添加新节点
    oBtn.onclick = function(){
        num++;
        var oLi = document.createElement('li');
        oLi.innerHTML = 111*num;
        oUl.appendChild(oLi);
    };
}

        这是一般的做法,但是你会发现,新增的li是没有事件的,说明添加子节点的时候,事件没有一起添加进去,这不是我们想要的结果,那怎么做呢?一般的解决方案会是这样,将for循环用一个函数包起来,命名为mHover,如下:

window.onload = function(){
    var oBtn = document.getElementById("btn");
    var oUl = document.getElementById("ul1");
    var aLi = oUl.getElementsByTagName('li');
    var num = 4;

    function mHover () {
        //鼠标移入变红,移出变白
        for(var i=0; i<aLi.length;i++){
            aLi[i].onmouseover = function(){
                this.style.background = 'red';
            };
            aLi[i].onmouseout = function(){
                this.style.background = '#fff';
            }
        }
    }
    mHover ();
    //添加新节点
    oBtn.onclick = function(){
        num++;
        var oLi = document.createElement('li');
        oLi.innerHTML = 111*num;
        oUl.appendChild(oLi);
        mHover ();
    };
}

        虽然功能实现了,看着还挺好,但实际上无疑是又增加了一个dom操作,在优化性能方面是不可取的,那么有事件委托的方式,能做到优化吗?

window.onload = function(){
    var oBtn = document.getElementById("btn");
    var oUl = document.getElementById("ul1");
    var aLi = oUl.getElementsByTagName('li');
    var num = 4;

    //事件委托,添加的子元素也有事件
    oUl.onmouseover = function(ev){
        var ev = ev || window.event;
        var target = ev.target || ev.srcElement;
        if(target.nodeName.toLowerCase() == 'li'){
            target.style.background = "red";
        }

    };
    oUl.onmouseout = function(ev){
        var ev = ev || window.event;
        var target = ev.target || ev.srcElement;
        if(target.nodeName.toLowerCase() == 'li'){
            target.style.background = "#fff";
        }

    };

    //添加新节点
    oBtn.onclick = function(){
        num++;
        var oLi = document.createElement('li');
        oLi.innerHTML = 111*num;
        oUl.appendChild(oLi);
    };
}

        看,上面是用事件委托的方式,新添加的子元素是带有事件效果的,我们可以发现,当用事件委托的时候,根本就不需要去遍历元素的子节点,只需要给父级元素添加事件就好了,其他的都是在js里面的执行,这样可以大大的减少dom操作,这才是事件委托的精髓所在。

        适合用事件委托的事件:click,mousedown,mouseup,keydown,keyup,keypress。

        值得注意的是,mouseover和mouseout虽然也有事件冒泡,但是处理它们的时候需要特别的注意,因为需要经常计算它们的位置,处理起来不太容易。

        不适合的就有很多了,举个例子,mousemove,每次都要计算它的位置,非常不好把控,在不如说focus,blur之类的,本身就没用冒泡的特性,自然就不能用事件委托了。

2.4.5、阻止事件

        event.stopPropagation()方法阻止事件在 DOM 中继续传播,防止再触发定义在别的节点上的监听函数,但是不包括在当前节点上其他的事件监听函数。

function stopEvent(e) {
  // 标准的DOM方法
  e.stopPropagation();
    
  // 取消冒泡  非标准的方式 老版本的IE支持
  e.cancelBubble = true;
}

el.addEventListener('click', stopEvent, false);

        上面代码中,click事件将不会进一步冒泡到el节点的父节点。

//阻止事件冒泡的兼容写法
function stopBubble(e){
    //如果提供了事件对象,则是一个非IE浏览器
    if(e && e.stopPropagation)
        //因此它支持W3C的stopPropagation()方法
        e.stopPropagation();
    else
        //否则,我们需要使用IE的方式来取消事件冒泡
        window.event.cancelBubble = true;
}

        event.preventDefault()方法取消浏览器对当前事件的默认行为这是阻止默认事件的方法,调用此方法是,连接不会被打开,但是会发生冒泡,冒泡会传递到上一层的父元素;

        比如点击链接后,浏览器默认会跳转到另一个页面,使用这个方法以后,就不会跳转了;

        该方法生效的前提是,事件对象的cancelable属性为true,如果为false,调用该方法没有任何效果。

        阻止事件冒泡的方法,其中e是function中的参数;

<input type="checkbox" id="my-checkbox" />

<script>
    var cb = document.getElementById('my-checkbox');
    cb.addEventListener(
        'click',
        function (e) {
            // DOM标准方法
            e.preventDefault();

            // IE的老版本,非标准方式
      		e.returnValue = false;
        },
        false
    );
</script>

        上面代码中,浏览器的默认行为是单击会选中单选框,取消这个行为,就导致无法选中单选框。

        return false,这个方法也会阻止默认事件;

<div id="div1">
    <a href="http://www.baidu.com" id="myA">我是超链接</a>
</div>

<script>
    var myA = document.getElementById('myA');
    var div1 = document.getElementById("div1");
    div1.onclick = function () {
        alert("div1触发了事件");
    }

    myA.onclick = function () {
        return false;
    }
</script>
//阻止事件默认行为的兼容写法
function stopDefault(e){
    //如果提供了事件对象,则是一个非IE浏览器
    if(e && e.preventDefault)
        //因此它支持W3C的preventDefault()方法
        e.preventDefault();
    else
        //否则,我们需要使用IE的方式来取消事件默认行为
        window.event.returnValue = false;
}

2.5、事件流

        很久以前有个叫Netscape的姑娘,她制订了Javascript的一套事件驱动机制(即事件捕获)。后来又有一个叫“IE”的小子,这孩子比较傲气,他认为“凭什么我要依照你的规则走”,于是他又创造了一套自己的规则(事件冒泡)。再后来,有个叫W3C的媒婆,想撮合这两个孩子,将他们的特点融合在了一起,这下,事件产生的顺序变成:事件从根节点开始,逐级派送到子节点,若节点绑定了事件动作,则执行动作,然后继续走,这个阶段称为“捕获阶段(Capture)”;执行完捕获阶段后,事件由子节点往根节点派送,若节点绑定了事件动作,则执行动作,然后继续走,这个阶段称为“冒泡阶段(Bubble)”。

        事件流是描述的从页面接受事件的顺序,当几个都具有相同事件的元素层叠在一起的时候,比如点击事件,那么你点击其中一个元素,并不是只有当前被点击的元素会触发事件,而层叠在你点击范围的所有元素都会触发事件。事件流包括两种模式:冒泡和捕获(捕捉)

        事件冒泡,是从里往外逐个触发。事件捕获,是从外往里逐个触发。那么现代的浏览器默认情况下都是冒泡模型,而捕获模式则是早期的Netscape 默认情况

        当我们在 DOM 树的某个节点发生了一些操作(例如单击、鼠标移动上去),就会有一个事件发射过去。这个事件从 Window 发出,不断经过下级节点直到目标节点。在到达目标节点之前的过程,就是捕获阶段(Capture Phase)。所有经过的节点,都会触发这个事件。捕获阶段的任务就是建立这个事件传递路线,以便后面冒泡阶段顺着这条路线返回 Window。

        监听某个在捕获阶段触发的事件,需要在事件监听函数传递第三个参数 true。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我是波哩个波

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值