# 事件进阶

目前已知:

  • 事件是可以被JS所侦听到的一个行为。
  • 事件分为事件源,事件类型,事件处理函数三部分。
  • 事件可以作用在DOM元素上(onclick),可以作用在浏览器上(onload,onscroll)。

# - 注册事件的三种方式与移除

注册事件:为元素增加上事件,注册事件也被称为添加事件,监听事件,绑定事件。

移除事件:将元素增加的事件进行移除。移除事件也被称为删除事件,销毁事件,解除事件。

# - 内联属性注册与移除

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <!--  增加了单击事件:当该事件触发后,会将其值作为JS代码执行。  -->
    <button onclick="alert(123);">按钮下标为0</button>
    <!--  事件属性是不区分大小写,建议全部小写  -->
    <button ONcLICK = "console.log('按钮下标为1');">按钮下标为1</button>
    <!--  同样的事件属性可以写多个,但只有第一个起作用  -->
    <button onclick="console.log(1);"  onclick="console.log(2);">按钮下标为2</button>
    <!--  事件属性的值,可以是多条JS语句  -->
    <button onclick="console.log(1);console.log(2);">按钮下标为3</button>
    <!--  事件属性的值,常被用来调用函数。  -->
    <button onclick="fn()">按钮下标为4</button>
    <!--  传递参数:函数体内的this为window  -->
    <button onclick="fn5(1,2,3,4,5)">按钮下标为5</button>
    <!--  将函数体内的this指向当前事件源:
            内联属性注册实现的原理:
                1-会将事件属性的值作为隐式创建函数体内的语句.
                    function eventFn(){fn5(1,2,3,4,5,this)};
                2-点点击按钮时,会执行隐式创建的函数
                    onclick = eventFn();

      -->
    <button onclick="fn6(1,2,3,4,5,this)">按钮下标为6</button>
    <button onclick="fn71.call(this,1,2,3,4)">按钮下标为7</button>

    <!--  如果事件属性的值为调用函数,调用的函数必须全局定义,即可以通过window.fn8  -->
    <button onclick="fn8()">按钮下标为8</button>

    <button onclick="move();">移除事件</button>

</body>
<script>
    // 内联属性注册事件即是在编写HTML代码时,直接写入元素的事件属性(以on开头,不区分大小写),
    // 事件属性的值是JS代码。
    function fn(){
        console.log("按钮下标为4")
    }

    function fn5(a,b,c,d,e){
        console.log(a,b,c,d,e);
        console.log(this);// window
    }

    function fn6(a,b,c,d,e,_this){
        console.log(a,b,c,d,e);// 1 2 3 4 5 6
        console.log(_this);// 事件源
        console.log(_this.getAttribute("onclick"));
        console.log(_this.onclick)// onclick(event) {fn6(1,2,3,4,5,this)};
    }

    // 总结:内联属性与之前的事件定义:
    // 1-内联属性注册
    // <button onClick="fn6(1,2,3,4,5,this)">按钮下标为6</button>
    // 2-以上代码相当于:
    // var btn = document.querySelectorAll("button")[6];
    // btn.onclick = function (){
    //     fn6(1,2,3,4,5,this)
    // }


    // var btn = document.querySelectorAll("button")[7];
    // btn.onclick = function (){
    //     // console.log(this);// button
    //     // fn7(this);
    //     fn71.call(this,1,2,3,4);// call的第一个参数指定fn71的this指向
    // }
    // function fn7(_this){
    //     console.log(this,_this);// window button
    // }
    function fn71(a,b,c,d){
        console.log(this,a,b,c,d);// button
    }


    function fn8(){
        console.log("fn8")
    }

    var obj = {
        fn8:function (){
            console.log("obj->fn8")
        }
    }

    function move(){
        // console.log("move");
        // 移除下标为1的按钮
        var btn = document.querySelectorAll("button")[1];
        // btn.onclick = null;// 移除事件方式1
        // btn.setAttribute("onclick",null);// // 移除事件方式2


        // 移除所有按钮的事件
        document.querySelectorAll("button").forEach(function (item){
            item.onclick = null;
        })
    }
</script>
</html>

# - DOM属性注册与移除

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <button>点我增加事件</button>
    <button>移除事件</button>
</body>
<script>
    // DOM元素属性注册的流程:
    // 1- 获取DOM元素
    // 2- 指定事件的类型
    // 3- 必须指定事件处理函数。

    // 特点:
    // 1- 必须以on开头,且必须全部小写。
    // 2- 同一元素上的同一事件,处理函数只允许写一个。如果出现多个,后者会将前者覆盖。
    // 3- 事件处理函数中的this指向事件源
    var btns = document.querySelectorAll("button");
    btns[0].onclick = function(){
        console.log("单击执行了1")
    }
    btns[0].onclick = function(){
        console.log("单击执行了2")
    }
    btns[0].onclick = function(){
        console.log("单击执行了3",this,this===btns[0])
    }
    btns[0].ondblclick = function (){
        console.log("双击执行了")
    }

    // 移除btns[0]事件
    btns[1].onclick = function(){
        // btns[0].onclick = null;// 移除单击事件
        // btns[0].ondblclick = null;// 移除双击事件

        // 将上方两行代码可以写为一行代码:
        btns[0].onclick = btns[0].ondblclick = null;
    }
</script>
</html>

# - 事件监听注册与移除

  • 注册

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <button id="btn">注册事件</button>
    </body>
    <script>
        // 监听注册事件的流程:
        // 1- 获得事件源
        // 2- 指定事件类型
        // 3- 指定处理函数
        // addEventListener方法有三个参数:
        // 第一个参数:事件类型。不需要带”on"
        // 第二个参数:事件处理函数。
        // 第三个参数是一个布尔值,也可以设置为对象(后面再学)
    
        // 特点:
        //  1- 指定事件类型,不要带on,且必须小写。
        //  2- 可以为同一个事件源,同一种事件类型,增加多个事件处理函数。
        //     哪个事件处理函数先指定,会先执行哪一个。
        //  3- 不支持IE678
        var button = document.getElementById("btn");
        // 增加单击事件:当点击按钮后,会触发事件处理函数
        button.addEventListener("click",function (){
            console.log("点击啦1!")
        })
        button.addEventListener("click",function (){
            console.log("点击啦2!")
        })
        button.addEventListener("click",function (){
            console.log("点击啦3!")
        })
    </script>
    </html>
    
  • 移除

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <button id="btn">注册事件</button>
    <button id="move">移除事件</button>
    </body>
    <script>
        var btn = document.getElementById("btn");
        var move = document.getElementById("move");
        var fn = function (){
            console.log("执行啦,我是先定义的")
        }
        // btn.addEventListener("click",function (){
        //     console.log("执行啦")
        // })
        btn.addEventListener("click",fn)
        move.addEventListener("click",function (){
            // 将id为btn元素的监听事件移除掉。
            // btn.removeEventListener("click");// 无法删除
    
            // removeEventListener需要传递二个参数:参数1事件类型,参数2事件处理函数(注册事件时指定的函数)
            // 无法移除:原因是指定的第二个参数与注册事件时指定的参数的引用不同。
            // btn.removeEventListener("click",function (){
            //     console.log("执行啦")
            // })
    
            // 正确方案:
            // 1- 注册事件时,需要将指定的事件处理函数先进行定义,然后再指定
            // 2- 移除时,第二个参数直接写成定义的函数即可
            btn.removeEventListener("click",fn);
    
        })
    
    
        // // 函数也是引用类型
        // var fn = function (){
        //
        // }
        // var fn2 = function (){
        //
        // }
        // console.log(fn === fn2);// false
    </script>
    </html>
    

# - 事件对象

官方解释:事件对象代表的是事件的状态。

​ 比如键盘事件,那么代表的是键盘事件的状态。

理解:事件发生后,事件对象代表的是与该事件相关的数据集合。

# - 事件对象的获取

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <button>dom属性注册事件</button>
    <button onclick="fn(event)">标签内联属性注册事件</button>
    <button>dom监听注册事件</button>
</body>
<script>
    /*总结:
    *   1- 获取事件对象除内联属性事件以外有两种方法
    *       1-1:在函数体内的event即是事件对象(三种注册事件的方法均支持)
    *       2-2:事件处理函数接收的第一个参数即是事件对象(内联属性事件不支持);
    *           注意:接收事件对象的名字一般设置为 event/e/eve/_e/_event
    *   2- 在获得事件对象时,建议采用形参接收的形式。
    *      如果事件是通过内联属性注册的,那么建议传递event
    * */
    var btns = document.querySelectorAll("button");
    btns[0].onclick = function(e){
        // console.log(event);// 不推荐使用。
        // console.log(e);
        // console.log(e === event);// true
    }
    function fn(e){
        // console.log(event);// 事件对象
        // console.log(e);
        console.log(e===event);// true
    }
    btns[2].addEventListener("click",function (e){
        // console.log(event);// 事件对象
        // console.log(e);
        console.log(e===event);// true
    })
</script>
</html>

# - 事件对象常见属性和方法

事件对象的属性和方法 说明
e.target 返回触发事件的对象
e.currentTarget 始终指向添加监听事件的那个对象(事件源)
e.type 返回事件的类型,不带on
e.preventDefault() 该方法阻止默认事件(默认行为)标准比如不让链接跳转
e.stopPropagation() 阻止冒泡标准
e.clientX 返回鼠标相对于浏览器窗口可视区的X坐标
e.clientY 返回鼠标相对于浏览器窗口可视区的Y坐标
e.pageX 返回鼠标相对于文档页面的X坐标
e.pageY 返回鼠标相对于文档页面的Y坐标
e.screenX 返回鼠标相对于电脑屏幕的X坐标
e.screenY 返回鼠标相对于电脑屏幕的Y坐标
e.offsetX 返回鼠标相对于事件源的X坐标
e.offsetY 返回鼠标相对于事件源的Y坐标
  • type target currentTarget

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <style>
            *{
                padding:0;
                margin:0;
            }
            #app{
                width: 300px;
                height: 300px;
                background: skyblue;
            }
            #child {
                width: 100px;
                height: 100px;
                background: gray;
            }
        </style>
    </head>
    <body>
        <div id="app">
            <div id="child"></div>
        </div>
    </body>
    <script>
        var app = document.querySelector("#app");
        var child = document.querySelector("#child");
        // 1-e.type
        // function fn(e){
        //     // 通过该属性可以判断事件类型。
        //     console.log(e.type);// 获得事件的类型。 click
        //     if(e.type === "click") console.log("单击")
        //     else if(e.type === "dblclick") console.log("双击")
        // }
        // // 注册单击事件
        // app.onclick = fn;
        // app.ondblclick = fn;
    
    
        // 2- e.target 与  e.currentTarget
        // e.target:可以用于事件委托。
        // e.currentTarget与this相同,一般写this即可
        app.onclick = function(e){
            // 指向触发该事件的DOM元素
            // console.log(e.target);// 指向的不是事件源对象,而是触发该事件的dom元素
    
            // this指向的是事件源对象
            // console.log(this);
    
            // 与this相同,指向的也是事件源对象
            console.log(e.currentTarget,this === e.currentTarget);
        }
    </script>
    </html>
    
  • 通过事件对象获得鼠标位置

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <style>
            *{
                padding:0;
                margin:0;
            }
            #app{
                width: 200px;
                height: 300px;
                background: skyblue;
                margin:100px;
            }
        </style>
    </head>
    <body style="height:1000px;">
        <div id="app"></div>
    </body>
    <script>
        var app = document.querySelector("#app");
        app.onclick = function(e){
            // e.clientX/e.clientY:事件触发时鼠标在浏览器内容区域的坐标。原点在浏览器视口左上角
            // console.log("clientX:"+e.clientX,"clientY:"+e.clientY);
    
            // e.screenX/e.screenY:事件触发时鼠标所在电脑屏幕的坐标。原点在屏幕左上角
            // console.log("screenX:"+e.screenX,"screenY:"+e.screenY);
    
            // e.pageX/e.pageY:事件触发时鼠标所在页面的坐标。原点在页面的左上角
            // console.log("pageX:"+e.pageX,"pageY:"+e.pageY);
    
            // e.offsetX/e.offsetY:事件触发时鼠标所在事件源的坐标。原点在事件源的左上角
            console.log("offsetX:"+e.offsetX,"offsetY:"+e.offsetY);
    
        }
    </script>
    </html>
    
  • preventDefault

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <style>
            a{
                margin:10px;
            }
        </style>
    </head>
    <body>
    <a href="http://www.baidu.com">百度</a>
    <a href="http://www.taobao.com">淘宝</a>
    <a href="http://www.jd.com" onclick="go(event,this)">京东</a>
    </body>
    <script>
        // a标签默认会跳转至href地址。
        var a = document.querySelectorAll("a");
        // 单击之后,会执行事件处理函数,事件处理函数执行完毕之后,会执行默认行为。
        // 去除默认事件。
        //   1- 可以使用事件对象中的.preventDefault。
        //      preventDefault是一个函数,该函数可以旋转在函数体内的任何位置 。
        //   2- 使用return false;(只有在DOM元素属性注册的事件中有效果)
        //      后续代码不会执行。
        a[0].onclick = function(e){
            return false;
            alert(this.innerText);
            // e.preventDefault();// 去除默认事件。
        }
        a[1].addEventListener("click",function (e){
            // return false;// 不支持取消默认事件。
            alert(e.currentTarget.innerText);
            // e.preventDefault();
            // return false;// 不支持取消默认事件。
        })
    
        function go(event,_this){
            // return false;// 不支持取消默认事件。
            // alert(_this.innerText);
            event.preventDefault();
        }
    
    </script>
    </html>
    

# - 事件委托

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <button>点我增加LI</button>
    <ul>
        <li>高大帅</li>
        <li>白富美</li>
        <li>帅气</li>
        <li>漂亮</li>
        <li>强大</li>
    </ul>
</body>
<script>
    // 事件委托:也被称为事件代理,在JQ称为事件委派。
    // 1-不会将事件定义在自身,而是定义在父级,
    // 2-通过事件冒泡来判断点击的具体是哪一个元素来实现相对应的功能。

    // 将li的单击事件定义在ur上,那么点击li,通过冒泡会触发ul的单击事件
    // 在ul的单击事件处理函数中可以通过e.target判断点击的是哪个li
    // e.target:触发事件的元素,比如点击li,通过冒泡会触发ul的点击事件,
    //          在事件的处理函数中,通过e.target得到的即是该li
    // 由于 只是增加了一个事件,效率会更高。
    var ul = document.querySelector("ul");
    var btn = document.querySelector("button");
    ul.onclick = function(e){
        // console.log("ul");
        // console.log(e.target)
        // 判断标签名是否为li
        console.log(e.target.nodeName)
        if(e.target.nodeName === "LI"){
            e.target.style.background = "green";
        }
    }
    btn.onclick = function(){
        var li = document.createElement("li");
        li.innerText = "四库全书";
        ul.appendChild(li);
    }
</script>
</html>

# - DOM事件流

为DOM绑定好事件,事件被触发时,会在元素之间产生一个执行的顺序,那么执行事件的顺序称为事件流。

事件流:可以认为是事件的传播,传播的过程:

1- 事件会从最外层向最内层进行遍历,直到所要操作的元素(捕获阶段),

2-所要操作的元素会触发事件(目标阶段)

3-会向回进行事件的触发。(冒泡阶段)

顺序:捕获-》目标-》冒泡

# - 事件冒泡(重点)

IE提出的。事件开始时由一个具体的元素来接收,然后逐层向上传播到最顶层这个过程称为冒泡。

  • 产生冒泡

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <style>
            *{
                padding:0;
                margin:0;
            }
            body,html{
                width: 100%;
                height: 100%;
            }
            #father{
                width: 300px;
                height: 300px;
                background: red;
            }
            #son{
                width: 200px;
                height: 200px;
                background: yellow;
            }
        </style>
    </head>
    <body>
        <div id="father">
            <div id="son"></div>
        </div>
    </body>
    <script>
        // 注意注意注意:DOM属性注册的事件只会产生冒泡,不会产生捕获。
        // 1- 通过onclick增加事件,产生冒泡
        // 事件冒泡:事件向上传递
        // var son = document.querySelector("#son");
        // var father = document.querySelector("#father");
        // son.onclick = function(){
        //     console.log("son");
        // }
        // father.onclick = function(){
        //     console.log("father");
        // }
        // document.body.onclick = function(){
        //     console.log("body");
        // }
        // document.documentElement.onclick = function(){
        //     console.log("html");
        // }
    
        // 2- 通过addEventListener产生冒泡
        //    需要省略第三个参数或将第三个参数设置为false
        var son = document.querySelector("#son");
        var father = document.querySelector("#father");
        son.addEventListener("click",function (){
            console.log("son");
        })
        father.addEventListener("click",function (){
            console.log("father");
        })
        document.body.addEventListener("click",function (){
            console.log("body");
        })
        document.documentElement.addEventListener("click",function (){
            console.log("html");
        })
    </script>
    </html>
    

# - 事件捕获

网景提出的。由DOM最顶层节点开始,然后逐层向下传播到具体的元素接收的过程,称为捕获

  • 事件捕获

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <style>
            *{
                padding:0;
                margin:0;
            }
            body,html{
                width: 100%;
                height: 100%;
            }
            #father{
                width: 300px;
                height: 300px;
                background: red;
            }
            #son{
                width: 200px;
                height: 200px;
                background: yellow;
            }
        </style>
    </head>
    <body>
    <div id="father">
        <div id="son"></div>
    </div>
    </body>
    <script>
    
        // 1- 通过addEventListener产生捕获。
        //    需要将第三个参数设置为true
        var son = document.querySelector("#son");
        var father = document.querySelector("#father");
        son.addEventListener("click",function (){
            console.log("son");
        },true)
        father.addEventListener("click",function (){
            console.log("father");
        },true)
        document.body.addEventListener("click",function (){
            console.log("body");
        },true)
    </script>
    </html>
    
  • 阻止捕获

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    *{
      padding:0;
      margin:0;
    }
    body,html{
      width: 100%;
      height: 100%;
    }
    #father{
      width: 300px;
      height: 300px;
      background: red;
    }
    #son{
      width: 200px;
      height: 200px;
      background: yellow;
    }
  </style>
</head>
<body>
<div id="father">
  <div id="son"></div>
</div>
</body>
<script>

  // 1- 通过addEventListener产生捕获。
  //    需要将第三个参数设置为true
  var son = document.querySelector("#son");
  var father = document.querySelector("#father");
  son.addEventListener("click",function (){
    console.log("son");
  },true)
  father.addEventListener("click",function (){
    console.log("father");
  },true)
  document.body.addEventListener("click",function (e){
    e.stopPropagation();
    console.log("body");
  },true)
</script>
</html>

# - 常用事件类型

  • 鼠标事件

    事件 说明
    onclick 点击某个对象时触发
    ondblclick 双击某个对象时触发
    onmouseover 鼠标移入某个元素时触发
    onmouseout 鼠标移出某个元素时触发
    onmouseenter 鼠标进入某个元素时触发
    onmouseleave 鼠标离开某个元素时触发
    onmousedown 鼠标按下时触发
    onmouseup 鼠标抬起时触发
    onmousemove 鼠标被移动时触发
    onwheel 鼠标滚轮滚动时触发
    oncontextmenu 点击鼠标右键时触发
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <button>单击</button>
    <button>右击</button>
    <button>双击</button>
    <button>按下 抬起</button>
    <button>移入  移出  1</button>
    <button>移入  移出  2</button>
    <button>移动</button>
    <button>滚轮</button>
</body>
<script>
    var btns = document.querySelectorAll("button");
    btns[0].addEventListener("click",function (){
        console.log("单击")
    })
    btns[1].addEventListener("contextmenu",function (e){
        // 可以通过事件对象去除默认事件
        e.preventDefault();
        console.log("右击")
    })
    btns[2].ondblclick = function(){
        console.log("双击")
    }
    btns[3].onmouseup = function(){
        console.log("抬起")
    }
    btns[3].onmousedown = function(){
        console.log("按下")
    }

    // 移入移出1
    btns[4].onmouseenter = function(){
        console.log("当鼠标进入该元素会触发,移入")
    }
    btns[4].onmouseleave = function(){
        console.log("当鼠标离开该元素会触发,移出")
    }

    // 移入移出2
    btns[5].onmouseover = function(){
        console.log("当鼠标进入该元素会触发,移入")
    }
    btns[5].onmouseout = function(){
        console.log("当鼠标离开该元素会触发,移出")
    }

    btns[6].onmousemove = function(){
        console.log("当鼠标在该元素上进行移动时触发")
    }

    btns[7].onwheel =function (){
        console.log("当滚轮进行滚动时触发")
    }

</script>
</html>
  • 键盘事件

    事件 说明
    onkeydown 键盘的键按下时触发
    onkeyup 键盘的键放开时触发
    onkeypress 在键盘按键被按下并释放一个键时发生。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

</body>
<script>
    // 执行顺序:keyup-->keypress->keydown
    // keypress:只有按下输入键时触发。
    // keydown:不管按下的是哪一个键均会触发。
    // 也是在键盘按下时触发:按下不放会一直触发
    document.onkeypress = function(){
        console.log("onkeypress按下")
    }
    // 监听键盘按下:按下不放会一直触发。
    document.onkeydown = function(){
        console.log("onkeydown按下")
    }
    // 监听键盘抬起
    document.onkeyup = function(){
        console.log("onkeyup抬起")
    }

</script>
</html>
  • 窗口事件

    事件 说明
    onresize 浏览器窗口大小改变时触发
    onscroll 文档滚动时触发
    onload 当页面中所有资源加载完毕触发
  • 加载事件

    事件 说明
    onload 元素加载完时触发
  • 表单事件

    事件 说明
    onfocus 元素获得焦点时触发
    onblur 元素失去焦点时触发
    onchange 离开输入框后且内容改变时触发
    oninput 元素获取用户输入时触发
    onsubmit 提交按钮时触发
    onreset 重置按钮时触发
    onselect 文本被选中时触发
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <form name="myForm" target="_blank" action="https://www.baidu.com/s">
        <p>关键字:<input autocomplete="off" name="wd" type="text"></p>
        <p>
            <select name="city" id="">
                <option value="1">北京</option>
                <option value="2">上海</option>
            </select>
        </p>
        <!-- 点击button默认会提交表单,会触发表单的submit事件,提交至form->action   -->
        <button>提交表单1</button>
        <!-- type值为submit也会提交表单,会触发表单的submit事件   -->
        <input type="submit" value="提交表单2">
        <!-- type值为button不会提交表单,不会触发表单的submit事件   -->
        <input type="button" name="btn" value="提交表单3">
        <!--  type值为reset会触发表单当中reset事件  -->
        <input type="reset" value="重置表单">
    </form>
    </body>
    <script>
        // 获得表单
        var myForm = document.myForm;
        myForm.onsubmit = function(e){
            // 当提交表单时,会执行该处理函数。
            e.preventDefault();// 去除默认事件:跳转至action地址
            alert("执行啦")
        }
        myForm.btn.onclick =function(){
            // myForm.onsubmit();
            myForm.onreset();
        }
        myForm.onreset = function(e){
            console.log(123)
            // e.preventDefault();// 去除默认事件:重置 表单。
            // alert("reset");
        }
    
        // 获得焦点事件
        myForm.wd.onfocus = function(){
            // console.log("获得焦点事件")
            myForm.wd.style.background = "red";
        }
        // 失去焦点事件
        myForm.wd.onblur = function(){
            // console.log("失去焦点事件")
            myForm.wd.style.background = "";
        }
        myForm.wd.onchange = function(){
            // 写入的内容必须为小写
            this.value = this.value.toLowerCase();
            console.log("当写入的内容发生变化且失去焦点后执行。")
        }
        myForm.wd.onselect = function(){
            console.log("文本被选中执行")
        }
        myForm.wd.oninput = function(){
            console.log("元素获取用户输入的信息时触发");
        }
    
        myForm.city.onchange = function(){
            console.log("当下拉列表的内容发生变化后执行",this.value)
        }
    </script>
    </html>
    
  • 多媒体事件

    事件 说明
    onplay 在视频/音频开始播放时触发
    onended 在视频/音频播放结束时触发
    onpause 在视频/音频暂停时触发

# - 案例-滚轮放大缩小

  • 预览效果 (opens new window)

  • 代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>缩放图片-前端猫</title>
        <style>
            *{
                padding:0;
                margin:0;
            }
            img{
                height: 200px;
                position: absolute;
                left:0;
                top:0;
                bottom: 0;
                right: 0;
                margin: auto;
            }
        </style>
    </head>
    <body>
        <img src="./img/10.jpg" alt="">
    </body>
    <script>
        var img = document.querySelector("img");
        img.addEventListener("mousewheel",function (e){
            if(e.wheelDelta>0){
                if(this.offsetHeight+10>500) return;
                this.style.height = (this.offsetHeight+10)+ "px";
            }else{
                if(this.offsetHeight-10<50) return;
                this.style.height = (this.offsetHeight-10)+ "px";
            }
        },{
            passive:true
        })
    </script>
    </html>
    

# - 案例-键盘事件控制人物行走

  • 效果

# - 案例-拖拽

  • 效果

  • 代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>案例-拖拽</title>
        <style>
            * {
                padding: 0;
                margin: 0;
            }
            body {
                user-select: none;
            }
            #app {
                position: relative;
                margin: 0 auto;
                width: 500px;
                height: 500px;
                border: 1px solid green;
            }
    
            #ball {
                width: 50px;
                height: 50px;
                line-height: 50px;
                font-size: 30px;
                text-align: center;
                background: gray;
                border-radius: 100%;
                position: absolute;
                color:green;
                top: 0;
                left: 0;
                cursor: move;
            }
        </style>
    </head>
    <body>
        <div id="app">
            <div id="ball"></div>
        </div>
    </body>
    <script>
        // 获得ID为ball的元素。
        var ball = document.querySelector("#ball");
        var app = document.querySelector("#app");
        var ballOffsetX = 0;// 鼠标按下时所在ball的横坐标初始值
        var ballOffsetY = 0;// 鼠标按下时所在ball的纵坐标初始值
        function move(e) {
            var leftV = e.clientX - ballOffsetX - app.offsetLeft;
            var topV = e.clientY - ballOffsetY - app.offsetTop;
            if (leftV < 0) leftV = 0;
            else if (leftV > (app.clientWidth - ball.offsetWidth))
                leftV = app.clientWidth - ball.offsetWidth;
    
            if (topV < 0) topV = 0;
            else if (topV > (app.clientHeight - ball.offsetHeight))
                topV = app.clientHeight - ball.offsetHeight;
            console.log(ballOffsetX)
            ball.style.left = leftV + "px";
            ball.style.top = topV + "px";
        }
    
        function up() {
            ball.style.background = "";
            // 移除move事件
            document.removeEventListener("mousemove", move);
            // 移除抬起事件
            document.removeEventListener("mouseup", up)
        }
    
        // 按下事件
        ball.addEventListener("mousedown", function (e) {
            this.style.background = "skyblue";
            ballOffsetX = e.offsetX;// 鼠标按下时所在ball的横坐标
            ballOffsetY = e.offsetY;// 鼠标按下时所在ball的纵坐标
            document.addEventListener("mousemove", move);
            document.addEventListener("mouseup", up)
        })
    </script>
    </html>
    

# - 案例-360度旋转汽车

  • 效果

  • 代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>案例-360度旋转汽车</title>
        <style>
            * {
                padding: 0;
                margin: 0;
            }
    
            #app {
                position: relative;
                width: 780px;
                height: 520px;
                margin: 0 auto;
                border: 1px solid green;
                cursor: move;
            }
            #loading{
                position: absolute;
                left:0;
                top:0;
                width: 100%;
                height: 100%;
                background: gray;
                color:white;
                text-align: center;
                font-size: 20px;
                line-height: 520px;
            }
            #app img {
                width: 100%;
                height: 100%;
            }
        </style>
    </head>
    <body>
    <div id="app">
        <div id="loading">已加载<span>0</span>%</div>
        <img alt="">
    </div>
    </body>
    <script>
        var span = document.querySelector("span");
        var loading = document.querySelector("#loading");
        var img = document.querySelector("img");
        // 在使用鼠标移动事件以及抬起事件,一般需要写在按下事件内。
        var app = document.querySelector("#app");
        // 记录图片的下标
        var index = 0;
        var startX = 0;// 按下鼠标的起始位置初始值
        var maxImgNum = 72;// 小汽车的数量
        var saveImgNum = 0;// 加载完成的数量
        for(var i=0;i<maxImgNum;i++){
            var nImg = new Image();
            nImg.src = "../img/golf"+i+".jpg";
            // onload是异步的。
            nImg.onload = function(){
                saveImgNum++;
                // console.log("进度:"+(saveImgNum/maxImgNum*100).toFixed(2)+"%")
                span.innerText = (saveImgNum/maxImgNum*100).toFixed(2);
                if(saveImgNum === maxImgNum){// 图片加载完毕
                    img.src = "../img/golf0.jpg"
                    // 将loading元素隐藏掉
                    loading.style.display = "none";
                    // console.log("图片加载完毕");
                    app.addEventListener("mousedown", function (e) {
                        e.preventDefault();// 移除默认事件。
                        // 记录鼠标在app的横坐标位置
                        startX = e.offsetX;
                        app.addEventListener("mousemove", move)
                        document.addEventListener("mouseup", up)
                    })
                }
            }
        }
        function move(e) {
            var endX = e.offsetX;// 移动的坐标点
            // console.log(endX-startX);// 移动的距离
            var distance = endX - startX;// 移动的距离
            // if (distance < 0) console.log("向左", distance);
            // else console.log("向右",);
            index+=distance;
            if(index<0)  index=71;
            if(index>71) index=0;
            img.src = "../img/golf"+index+".jpg"
            startX = endX;
        }
    
        function up() {
            app.removeEventListener("mousemove", move);
            document.removeEventListener("mouseup", up);
        }
    </script>
    </html>
    

# - 案例-省市区三级联动

  • 效果

  • 代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>三级联动-前端猫</title>
    </head>
    <body>
    <select name="provice">
        <option value="-1">--请选择省份--</option>
    </select>
    <select name="city">
        <option value="-1">--请选择城市--</option>
    </select>
    <select name="county">
        <option value="-1">--请选择区域--</option>
    </select>
    </body>
    <script src="js/data.js"></script>
    <script>
        var provice = document.querySelector("select[name=provice]");
        var city = document.querySelector("select[name=city]");
        var county = document.querySelector("select[name=county]");
        city.onchange = function(){
            var _this = this;
            county.querySelectorAll("option").forEach(function (item){
                // 如果value不等于-1,那么删除
                if(item.value/1 !== -1)  county.removeChild(item);
            })
            if(this.value/1 === -1) return;
            county.innerHTML += Object.keys(cityJson[this.value]).map(function (item){
                return `<option value="${item}">${cityJson[_this.value][item]}</option>`
            })
        }
        provice.onchange = function(){
            // 通过this.value 将city中增加对应城市信息
            // console.log(cityJson[this.value]);
            var _this = this;
            city.querySelectorAll("option").forEach(function (item){
                // 如果value不等于-1,那么删除
                if(item.value/1 !== -1)  city.removeChild(item);
            })
            county.querySelectorAll("option").forEach(function (item){
                // 如果value不等于-1,那么删除
                if(item.value/1 !== -1)  county.removeChild(item);
            })
            // 如果未选择省份,那么直接返回
            if(this.value/1 === -1) return;
            city.innerHTML+= Object.keys(cityJson[this.value]).map(function (item){
                return `<option value="${_this.value},${item}">${cityJson[_this.value][item]}</option>`
            })
    
        }
        provice.innerHTML += Object.keys(cityJson["0"]).map(function (item){
            return `<option value="0,${item}">${cityJson["0"][item]}</option>`
        }).join("")
    </script>
    </html>
    

# - 案例-锅打灰太郎

  • 效果

  • 代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>锅打灰太郎-前端猫</title>
        <style>
            *{
                padding:0;
                margin:0;
            }
            #app {
                position: relative;
                width: 320px;
                height: 480px;
                background:url("./img/game_bg.jpg");
                margin:0 auto;
            }
            .btn{
                position: absolute;
                top:200px;
                left:50%;
                font-size:40px;
                cursor: pointer;
                transform: translateX(-50%);
                color:white;
            }
            .btn:hover{
                color:red;
            }
            /*进度条*/
            #progress{
                position: absolute;
                left:63px;
                top:66px;
                width: 180px;
                height: 16px;
                background:url("./img/progress.png");
            }
            /*狼图片定位*/
            #wolf img{
                position: absolute;
            }
            #score{
                position: absolute;
                left:60px;
                top:10px;
                font-size: 20px;
                color:skyblue;
            }
        </style>
    </head>
    <body>
    <div id="app">
        <!--   进度条     -->
        <div id="progress"></div>
        <!--    灰太狼或小灰灰    -->
        <div id="wolf"></div>
        <div id="score">0</div>
        <!--  开始按钮 -->
        <div class="btn" id="start">开始</div>
        <!--    继续按钮    -->
        <div class="btn" id="continue">继续</div>
    </div>
    </body>
    <script>
        // 伪代码:
        // 当用户将浏览器最小化,或重新打开一个视口,游戏应该中止,
        // 当用户再次进入游戏时,需要点击继续才可以。
        // 当游戏开始后,监听用户的行为:是否离开当前页面。
        // 如果离开需要让游戏暂停
        //    1- 将倒计时与狼动画定时器移除
        //    2- 显示 继续 按钮
        //    3- 点击继续,可以继续玩。
        //       3-1: 将倒计时与狼动画的定时器添加上
        //       3-2: 继续按钮隐藏掉。
        // 当游戏结束(时间到了)之后,记得清理监听离开事件
    
        // 获得点击按钮
        var startBtn = document.querySelector("#start");
        // 分数
        var score = document.querySelector("#score");
        // 得到进度条
        var progress = document.querySelector("#progress");
        // 得到包裹狼图片的元素
        var wolfBox = document.querySelector("#wolf");
        // 继续按钮
        var continueBtn = document.querySelector("#continue");
        continueBtn.style.display = "none";// 隐藏继续按钮
        // 存储倒计时定时器返回的值
        var countTime;
        // 狼出现在洞口的定位下标
        var wolfStartArr = [
            {left: "98px", top: "115px"},
            {left: "17px", top: "160px"},
            {left: "15px", top: "220px"},
            {left: "30px", top: "293px"},
            {left: "122px", top: "273px"},
            {left: "207px", top: "295px"},
            {left: "200px", top: "211px"},
            {left: "187px", top: "141px"},
            {left: "100px", top: "191px"}
        ];
        // 创建一个图片元素,狼
        var wolfImg = document.createElement("img");
        // 为继续按钮增加事件
        continueBtn.onclick = function(){
            // 计时器
            countTime = setInterval(countDown,10);
            // 创建狼的动画定时器
            wolfImg.timer = setInterval(wolfMove,200);
            // 隐藏继续按钮
            this.style.display = "none";
        }
        // 为按钮增加事件
        startBtn.onclick = function(){
            // 将分值设置为0
            score.innerHTML = 0;
            // 将时间进度条宽度设置为180px;
            progress.style.width = "180px";
            // 隐藏开始按钮
            this.style.display = "none";
            // 通过定时器控制进度条的宽度
            countTime = setInterval(countDown,10);
            // 调用产生狼的方法
            createWolf();
    
            // 判断用户是否离开 当前页面
            document.onvisibilitychange = function(){
                // 如果离开或进入到该页面会触发该函数。
                // hidden:离开 visible进入。
                // console.log(document.visibilityState);// hidden visible
                if(document.visibilityState === "hidden"){
                    // 离开
                    //    1- 将倒计时与狼动画计时器移除
                    //    2- 显示 继续 按钮
                    clearInterval(countTime);
                    clearInterval(wolfImg.timer);
                    continueBtn.style.display = "block";
                }
            }
        }
        // 为狼增加点击事件
        wolfImg.onclick = function(){
            this.state = 3;// 将动画的状态设置为3,说明锅打
            this.index = 5;
            if(this.wolfType === "h"){
                // 灰太狼加10
                score.innerHTML = score.innerHTML/1+10;
            }else{
                // 小灰灰减10
                score.innerHTML = score.innerHTML/1-10;
            }
        }
        // 去除选中的默认行为
        document.onmousedown = function (e){
            e.preventDefault();
        }
        // 倒计时
        function countDown(){
            // 每秒钟需要将进度条的宽度进行减法运行
            progress.style.width = (progress.getBoundingClientRect().width-0.3)+"px";
            // 判断是否倒计时结束
            if(progress.getBoundingClientRect().width-0.3 <= 0){
                document.onvisibilitychange = null;
                // 清除定时器
                clearInterval(countTime);
                // 清除狼动画
                clearInterval(wolfImg.timer);
                // 清除狼图片
                wolfBox.removeChild(wolfImg);
                // 显示按钮,并将按钮的文字修改为重新开始
                startBtn.innerHTML = "重新开始";
                startBtn.style.display = "block";
                console.log("游戏结束")
            }
    
        }
        // 让狼出现
        function createWolf(){
    
            // 获得狼出现洞穴的位置
            var wolfStart = wolfStartArr[getRandom(0,8)];
            // 左
            wolfImg.style.left = wolfStart.left;
            // 上
            wolfImg.style.top = wolfStart.top;
            // 通过随机函数生成1---100 数字,如果得到的数字<=80认为是灰太狼,否则是小灰灰
            wolfImg.wolfType = getRandom(1,100)<=80?"h":"x";
            // 指定初始图片位置
            wolfImg.index = 0;
            // 指定图片的状态 1- 出洞  2-回洞  3- 锅打
            wolfImg.state = 1;
            // 指定具体图片: 狼的类型+图片下标
            wolfImg.src = "./img/"+(wolfImg.wolfType+wolfImg.index)+".png";
            // 将图片放置到指定的位置
            wolfBox.appendChild(wolfImg);
            // 播放动画
            wolfImg.timer = setInterval(wolfMove,200);
        }
        // 狼进行移动,播放动画
        function wolfMove(){
            // 出洞
            if(wolfImg.state === 1){
                wolfImg.index++;
                // 当下标如果等于5,那么应该回洞
                if(wolfImg.index === 5) wolfImg.state = 2;
            }else if(wolfImg.state === 2){
                wolfImg.index--;
            }else if(wolfImg.state === 3){
                wolfImg.index++;
            }
            // 已经回洞了
            if(wolfImg.index<0 || wolfImg.index>9){
                // 清除定时器
                clearInterval(wolfImg.timer);
                // 将狼从wolfBox中移除。(注意:只是从容器中移除了)
                wolfBox.removeChild(wolfImg);
                createWolf();
                return;
            }
            wolfImg.src = "./img/"+(wolfImg.wolfType+wolfImg.index)+".png"
        }
        // 生成两个数之间的随机数字
        function getRandom(min,max){
            return Math.floor(Math.random()*(max-min+1)+min);
        }
    
    </script>
    </html>