事件

所谓事件,浏览器与用户操作进行交互

  • 用户点击浏览器某个位置,这个行为会产生一个点击事件.
  • 浏览器主动为用户播放轮播图,这也属于一个事件
  • 用户滑动浏览器,页面产生滚动,这是滚动事件

在JS中事件有非常多,我们再这章里学习一些主要的事件

事件模型

  • 内联模型
  • 脚本模型
  • DOM2模型
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <button onclick="test1()">内联模型</button>
    <button id="btn">脚本模型</button>
    <button id="dom2">dom2模型</button>
</body>
<script>
    function test1(){
        console.log("1");
    }
    //脚本模型
    var btn = document.getElementById("btn");
    btn.onclick = function(){
        console.log("2");
    }

    var dom2 = document.getElementById("dom2");
    dom2.addEventListener("click",function(){
        console.log("3");
    },false)
</script>
</html>

事件列表

事件名称 作用
onload 文档或者图片加载完成之后触发
onclick 点击元素的时候触发
ondbclick 双击元素的时候触发
onblur 当焦点从元素上移开时触发
onfocus 当元素获取到焦点的时候触发
onchange 改变一个元素的值的时候触发,一般都是form表单元素
ondragdrop 拖拽并放下的时候触发
onkeydown 当按键被按下的时候触发
onkeypress 当按键被按下的时候触发
onkeyup 当按键被松开时触发
onmouseout 当图标移出时触发
onmouseover 当鼠标划过的时候触发
onmouseenter 当鼠标进入元素范围时触发(优先级高)
onmouseleave 当鼠标离开元素范围时触发(优先级高)
onreset 单击表单的reset按钮
onresize 选择一个表单对象时
onselect 选中表单里的内容
onsubmit 点击form表单的

单击和双击

如果一个元素同时单击和双击事件,就有可能出现以下情况

  • 只执行单击事件,无法执行双击事件
  • 单击事件和双击事件先后执行
<!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>
        #app {
            width:100px;
            height:100px;
            background-color: cyan;
        }
    </style>
</head>
<body>
    <div id="app"></div>
</body>
<script>
  var app = document.getElementById("app");
  app.onclick = function(){
      console.log("单击");
  }
  app.ondblclick = function(){
      console.log("双击");
  }
</script>
</html>

解决单击和双击冲突

解决方案:

  • 将单击事件的功能放到一个 延时器里等待执行,当在200毫后没有双击事件,则执行单击事件的功能
  • 如果200毫内执行了双击事件,则取消单击事件里的定时器任务
<!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>
        #app {
            width:100px;
            height:100px;
            background-color: cyan;
        }
    </style>
</head>
<body>
    <div id="app"></div>
</body>
<script>
  var app = document.getElementById("app");
  var timer = null;
  app.onclick = function(){
      //由于双击事件会产生两次单击,因此需要将上一次单击事件取消
     clearTimeout(timer);
     timer = setTimeout(function(){
        console.log("单击");
     },300)
  }

  app.ondblclick = function(){
    console.log("双击");
    clearTimeout(timer);
}
</script>
</html>

mouse事件

<!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>
        #app {
            width:100px;
            height:100px;
            background-color: cyan;
        }
    </style>
</head>
<body>
    <div id="app">
    </div>
</body>
<script>
    var app = document.getElementById("app");
    //鼠标离开时 app元素背景色变为 天蓝色
   app.onmouseout = function(){
       this.style.backgroundColor = "cyan";
   }
   //当鼠标按下不放时,app元素背景色变为 绿色
   app.onmousedown = function(){
       this.style.backgroundColor = "green"
   }
   //当鼠标发开时,app元素背景色变为 粉色
   app.onmouseup = function(){
       this.style.backgroundColor = "pink"
   }
   //当鼠标进入app元素范围的时候,app元素背景色变为 蓝色
   app.onmouseenter = function(){
        this.style.backgroundColor = "blue";
   }
   //当鼠标离开app元素范围的时候,app元素背景色变为 红色
   app.onmouseover = function(){
       console.log(this);
       this.style.backgroundColor = "red";
   }
   //当鼠标离开app元素范围的时候,app元素背景色变为 黄色
   app.onmouseleave = function(){
       this.style.backgroundColor = "yellow"
   }
</script>
</html>

表单事件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <form action="#" onsubmit="sb()" onreset="rs()">
        <input type="text" id="username">
        <select id="city">
            <option value="河池">河池</option>
            <option value="深圳">深圳</option>
        </select>
        <textarea></textarea>
        <input type="submit" value="提交"/>
        <input type="reset" value="重置">
    </form>
</body>
<script>
    var usernameInput = document.getElementById("username");
    usernameInput.onfocus = function(){
        console.log("聚焦到input元素了");
    }
    usernameInput.onblur = function(){
        console.log("input元素失焦了");
    }
    usernameInput.onchange = function(){
        console.log("输入完成,失焦后.内容发生变化");
    }
    usernameInput.oninput = function(){
        console.log("正在输入内容",this.value);
    }
    usernameInput.onselect = function(){
        console.log("选中的内容",this.value);
    }

    var city = document.getElementById("city");
    city.onchange = function(){
        console.log(this.value);
    }

    function rs(){
        alert("重置");
    }
    function sb(){
        alert("提交");
    }

</script>
</html>

事件对象

当事件被触发的时候,会自动生成一个 事件对象 作为参数传递给要触发的函数.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <button id="btn1">点击</button>
</body>
<script>
    var btn1 = document.getElementById('btn1');
    btn1.onclick = function(ev){
        console.log(ev);
    }
</script>
</html>

属性 说明
altKey alt键是否被按下
bubbles 是否是冒泡事件类型
cancelBubble 阻止事件冒泡
cancelable 返回布尔值,指示事件是否可拥可取消的默认动作。
ctrlKey 'ctrl'键是否被按下
currentTarget 返回其事件监听器触发该事件的元素。
defaultPrevented 返回一个布尔值,表明当前事件是否调用了 event.preventDefault()方法。
eventPhase 返回一个代表当前执行阶段的 整数值
shiftKey 是否同时按下shift键
target 返回触发此事件的元素
type 事件类型

target

该属性直接获取触发事件的元素对象

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <input type="text" id="username">
</body>
<script>
    var usernameInput = document.getElementById("username");
    usernameInput.onchange = function(ev){
        //兼容性
        var event = ev || window.event;
        var target = event.target;
        console.log(target.value);
        console.log(target.id);
        console.log(target.style);
    }
</script>
</html>

鼠标事件对象

当我们按下鼠标左键,右键,滚轮等行为的时候,它也会生成一个事件对象,在触发的事件函数里使用.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <button id="btn">按钮1</button>
</body>
<script>
   var btn = document.getElementById("btn");
   btn.onclick = function(ev){
        var e = ev || window.event;
        console.log(e);
   }
</script>
</html>

从上图中我们看到鼠标事件多出了很多属性

属性名 说明
altKey 是否使用了alt键
button 使用的按键
buttons 触发时哪些鼠标按键被按下,或者有多少个触控点
clientX 与浏览器可视区域左边的距离
clientY 与浏览器可视区域顶部的距离
ctrlKey 是否使用了ctrl键
layerX 与最近有定位的元素的x轴距离
layerY 与最近有定位的元素的y轴距离
metaKey 是否按下了meta键(windows或者mac command)
movementX 与上一个mousemove事件之间的水平距离
movementY 与上一个mousemove事件之间的垂直距离
offsetX 鼠标相对于事件源x轴的位置
offsetY 鼠标相对于事件源y轴的位置
pageX
pageY
screenX 鼠标相对于显示器屏幕x轴的位置
screenY 鼠标相对于显示器屏幕y轴的位置
shiftKey 是否按下了shift键
type "click"​
x 鼠标相对于可视区域左侧的距离
y 鼠标相对于可视区域顶部的距离

鼠标按钮 button

它返回一个值,代表用户按下并触发了事件的鼠标按键。

按键值 说明
0 按下主键(左键)
1 按下滚轮
2 按下右键
<!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>
        #app {
            width: 100px;
            height: 100px;
            background-color: cyan;
        }
    </style>
</head>
<body>
    <div id="app"></div>
</body>
</html>
<script>
    var app = document.getElementById("app");
    app.onmouseup = function(ev){
        var e = ev || window.event;
        alert(e.button);
    }
</script>

兼容IE

按键值 说明
0 没有按下按钮
1 主按钮 (左键)
2 次要按钮(右键)
4 中间滚轮
function checkButton(ev){
    var e = ev || window.event;
    if(ev){
        return e.button;
    }else if(window.event){
        switch(e.button){
            case 1: 
                return 0;
            case 2
                return 2;
            case 4: 
                return 1;
        }
    }
}

//为整个document对象注册鼠标事件
document.onmouseup = function(ev){
    var button = checkButton(ev);
    if(button(ev) === 0){
        console.log("按下了左键");
    }else if(button==1){
        console.log("按下了中键");
    }else if(button==2){
        console.log("按下了右键");
    }
}

preventDefault()

阻止默认事件,例如,阻止右键行为

<!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>
        #app {
            width: 300px;
            height:300px;
            background: lightblue;
        }
    </style>
</head>
<body>
    <div id="app"></div>
</body>
</html>
<script>
  document.getElementById("app").oncontextmenu = function(ev){
    var event = ev || window.event;
    if(event.preventDefault){
        event.preventDefault();
    }else{
        event.returnValue = false;
    }
    alert("禁止使用鼠标右键");
  }
</script>

自定义右键菜单

下方例子中, 当我们右键点击app元素的时候,会弹出我们自定义的菜单.左键点击页面的时候,隐藏菜单

<!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>
        * {
            padding: 0;
            margin: 0;
            list-style: none;
        }
        #app {
            width: 300px;
            height:300px;
            background: lightblue;
        }
        #contextMenu{
            display: none;
            width: 100px;
            background-color: gray;
            padding: 10px;
            box-sizing: border-box;
        }
        #contextMenu li {
            width: 100%;
            height: 30px;
        }
        #contextMenu li:hover{
            background-color: lightgray;
        }
    </style>
</head>
<body>
    <div id="app"></div>
    <ul id="contextMenu">
        <li>html</li>
        <li>css</li>
        <li>javascript</li>
    </ul>
</body>
</html>
<script>
  document.getElementById("app").oncontextmenu = function(ev){
    var event = ev || window.event;
    if(event.preventDefault){
        event.preventDefault();
    }else{
        event.returnValue = false;
    }
    var contextMenu = document.getElementById("contextMenu");
    contextMenu.style.position = 'absolute';
    contextMenu.style.top = event.clientY+10+"px";
    contextMenu.style.left = event.clientX+"px";
    contextMenu.style.display = "block";
  }
  //取消右键
  document.onclick = function(){
      contextMenu.style.display = "none"
  }
</script>

事件传递

事件传递(事件流).它是描述页面接收事件的顺序.当一个页面有多层元素的叠加到一起时,当你点击一个元素,它并不会只触发当前元素的绑定的事件,而是将所有叠加的元素的事件都触发一遍.

事件传递的方向 有两种:

  • 事件冒泡
  • 事件捕获

事件冒泡

事件从里往外逐层触发

<!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>

        #app {
            width: 100px;
            height: 100px;
            background-color: lightblue;
        }
    </style>
</head>
<body>
    <div id="app">
        <button id="btn">按钮</button>
    </div>
</body>
</html>
<script>
    var app = document.getElementById("app");
    var btn = document.getElementById("btn");
    var body = document.body;
    app.onclick = function(){
        console.log("app");
    }
    btn.onclick = function(){
        console.log("btn");
    }
    body.onclick = function(){
        console.log("body");
    }
    document.onclick = function(){
        console.log("document");
    }
</script>

输出顺序如下

阻止事件冒泡

<!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>

        #app {
            width: 100px;
            height: 100px;
            background-color: lightblue;
        }
    </style>
</head>
<body>
    <div id="app">
        <button id="btn">按钮</button>
    </div>
</body>
</html>
<script>
    var app = document.getElementById("app");
    var btn = document.getElementById("btn");
    var body = document.body;
    app.onclick = function(){
        console.log("app");
    }
    btn.onclick = function(ev){
        console.log("btn");
        var e = ev || window.event;
        window.event?e.cancelBubble=true:e.stopPropagation();
    }
    body.onclick = function(){
        console.log("body");
    }
    document.onclick = function(){
        console.log("document");
    }
</script>

事件委托

事件委托就是将 当前元素的要触发的事件,委托给其他元素来触发.

优点:

  • 大量节省内存的占用,减少事件注册
  • 对新增元素实现动态绑定事件

大量占用内存的例子

<!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>

        #app {
            width: 100px;
            height: 100px;
            background-color: lightblue;
        }
    </style>
</head>
<body>
    <div id="app">
        <ul id="list">

        </ul>
    </div>
</body>
</html>
<script>
    var list = document.getElementById("list");
    for(var i=0;i<10000;i++){
        var li = document.createElement("li");
        li.index = i;
        li.innerHTML = i;
        /*
            注释部分
        */
        li.onclick = function(){
            console.log(this.index);
        }
        list.appendChild(li);
    }
</script>

上面的代码中,我们一次性给ul元素插入了1万个li元素,并且为每个li元素对象增加了onclick事件. 为了测试对内存的影响,我们对上面的代码中 li.onclick进行注释和不注释的代码测试.然后在火狐浏览器中, 通过内存分析工具,得出下面两张图

  • 注释了代码测试

  • 取消注释代码测试

从上面两张图中,我们可以看出如果我们为每一个li元素对象都注册一个事件,就会增加内存的消耗.降低页面的性能

为了降低内存的占用,我们可以将li的事件,注册给它的父级元素ul,这样就可以只用注册一个事件,就可以对每个li元素进行处理.大大地提供性能.

<!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>

        #app {
            width: 100px;
            height: 100px;
            background-color: lightblue;
        }
    </style>
</head>
<body>
    <div id="app">
        <ul id="list">

        </ul>
    </div>
</body>
</html>
<script>
    var list = document.getElementById("list");
    for(var i=0;i<10000;i++){
        var li = document.createElement("li");
        li.index = i;
        li.innerHTML = i;
        list.appendChild(li);
    }

    list.onclick = function(ev){
        var e = ev || window.event;
        //获取点击ul元素时,鼠标实际点中的元素
        var target = e.target;
        //判断target元素是否是li
        switch(target.tagName){
            case "LI":
                //在这里做你想做的事
                console.log(target.index);
                break;
            default:
                break;
        }
    }
</script>

DOM2 事件监听

在前面学习的事件绑定中,我们都是将函数赋值给一个事件来处理.但这种赋值方式的绑定,决定了一个元素只能绑定同类型的事件一次.如果多次绑定,则会被最后一次绑定覆盖.如下

<!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>
        #app {
            width: 100px;
            height: 100px;
            background-color: cadetblue;
        }
    </style>
    <script>
        window.onload = function(){
            console.log("onload第一次");
        }
        window.onload = function(){
            console.log("onload第二次");
        }
    </script>
</head>
<body>
    <div id="app"></div>
</body>
</html>
<script>
    var app = document.getElementById("app");
    app.onclick = function(){
        console.log("第一次绑定");
    }
    app.onclick = function(){
        console.log("第二次绑定");
    }

</script>

执行结果

onload第二次
第二次绑定

从上面的例子中,可以看出.如果同一个对象注册了相同的事件,只会执行最后一次的事件绑定.如果我们是处于多人开发的,而另外一个同事给同一个对象注册相同的事件,最终结果就只有一种 产生冲突,只执行最有一次注册的事件.

addEventListener

每一个元素对象有这个方法,该函数接受三个参数

  • 事件名称
  • 函数名
  • 冒泡或捕获(true表示捕获,false表示冒泡)

将上面的例子修改如下

<!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>
        #app {
            width: 100px;
            height: 100px;
            background-color: cadetblue;
        }
    </style>
    <script>
        window.addEventListener("load",function(){
            console.log("onload第一次");
        },false)
        window.addEventListener("load",function(){
            console.log("onload第二次");
        },false)
    </script>
</head>
<body>
    <div id="app"></div>
</body>
</html>
<script>
    var app = document.getElementById("app");
    app.addEventListener("click",function(){
        console.log("第一次绑定");
    },false)
    app.addEventListener("click",function(){
        console.log("第二次绑定");
    },false)

</script>

输出结果

 onload第一次
 onload第二次
 第一次绑定
 第二次绑定

removeEventListener

移除事件监听: 可以对注册的事件进行移除,不再触发.但是需要注意的是,注册的函数必须是 单独声明的函数, 否则无法移除

<!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>
        #app {
            width: 100px;
            height: 100px;
            background-color: cadetblue;
        }
    </style>
</head>
<body>
    <div id="app"></div>
    <button id="btn">取消绑定</button>
</body>
</html>
<script>
    var app = document.getElementById("app");
    var btn = document.getElementById("btn");
    var func = function(){
        console.log("被绑定的事件");
    }
    app.addEventListener("click",func,false)
    app.addEventListener("click",function(){
        console.log("绑定时间2");
    },false)
    btn.addEventListener("click",function(){
        //取消app绑定的click事件的func方法
        app.removeEventListener("click",func);
    },false)

</script>

results matching ""

    No results matching ""