事件-PC 与 mobile phone

526 阅读6分钟

事件

  • 注意点: 事件方法都是没有驼峰的
  • ==移动端和 PC 端各自有特有的事件==
  • css 的 pointerevents: none; 能使对应选择器选到的 dom 不响应不拦截事件 css 的 pointerevents: none; 能使对应选择器选到的 dom 不响应不拦截事件

移入, 移出, 移动 (移动端没有移入移出事件)

  • .onmouseover .onmouseenter
  • .onmouseout .onmouseleave
  • .onmousemove
  • 淦!居然没驼峰
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>13-JavaScript-移入移出事件</title>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        div{
            width: 300px;
            height: 300px;
            background: red;
        }
    </style>
</head>
<body>
<div></div>
<script>
    let oDiv = document.querySelector("div");
    // 1.移入事件
    // oDiv.onmouseover = function () {
    //     console.log("移入事件");
    // }
    // 注意点: 对于初学者来说, 为了避免未知的一些BUG, 建议使用onmouseenter
    oDiv.onmouseenter = function () {
        console.log("移入事件");
    }

    // 2.移出事件
    // oDiv.onmouseout = function () {
    //     console.log("移出事件");
    // }
    // 注意点: 对于初学者来说, 为了避免未知的一些BUG, 建议使用onmouseleave
    oDiv.onmouseleave = function () {
        console.log("移出事件");
    }
    3.移动事件
    oDiv.onmousemove = function () {
        console.log("移动事件");
    }
</script>
</body>
</html>

表单焦点事件

  • .onfocus
  • .onblur
  • .onchange (只在失焦后才能拿到修改后的数据)
  • .oninput (实时获取到修改后的数据)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>17-JavaScript-焦点事件</title>
</head>
<body>
<input type="text">
<script>
    let oInput = document.querySelector("input");
    // 1.监听input获取焦点
    oInput.onfocus = function () {
        console.log("获取到了焦点");
    }
    // 2.监听input失去焦点
    oInput.onblur = function () {
        console.log("失去了焦点");
    }
    // 3.监听input内容改变
    // 注意点: onchange事件只有表单失去焦点的时候, 才能拿到修改之后的数据
    oInput.onchange = function () {
        console.log(this.value);
    }
    // oninput事件可以时时获取到用户修改之后的数据, 只要用户修改了数据就会调用(执行)
    // 注意点: oninput事件只有在IE9以上的浏览器才能使用
    // 在IE9以下, 如果想时时的获取到用户修改之后的数据, 可以通过onpropertychange事件来实现
    oInput.oninput = function () {
        console.log(this.value);
    }
</script>
</body>
</html>

添加事件的三种方式

  1. element.onxxx
  2. element.attachEvent (上古, 了解即可)
  3. element.addEventListener (现代,可选事件冒泡 or 捕获)

方法一:onxxx

注意点:

  1. 由于是给属性赋值, 所以后赋值的会覆盖先赋值
  2. 缺点很明显: 不能通过一个事件触发多个方法, 不利于维持方法的细粒度
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>onclick</title>
</head>
<body>
<button id="btn">我是按钮</button>
<script>
    // 这点击事件只会输出 777, 原理就是给对象属性重赋值会对原有的值进行覆盖
    var oBtn = document.getElementById("btn");

    oBtn.onclick = function () {
        alert("666");
    }
    oBtn.onclick = function () {
        alert("777");
    }
</script>
</body>
</html>

方法二:attachEvent

注意点:

  1. 事件名称必须加上on
  2. 后添加的不会覆盖先添加的
  3. 只支持低版本的浏览器
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>attachEvent</title>
</head>
<body>
<button id="btn">我是按钮</button>
<script>
    // 既输出 666 也输出 777,但现代浏览器已经摒弃了这个方法
    var oBtn = document.getElementById("btn");

    oBtn.attachEvent("onclick", function () {
        alert("666");
    });
    oBtn.attachEvent("onclick", function () {
        alert("777");
    });
</script>
</body>
</html>

方法三:addEventListen

注意点:

  1. 事件名称不需要添加 on
  2. 后添加的不会覆盖先添加的
  3. 只支持现代浏览器(其实完全足够了)
  4. 这是三个里唯一可自定义事件是冒泡行为还是捕获行为的方法
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>addEventListener</title>
</head>
<body>
<button id="btn">我是按钮</button>
<script>
    // 既输出 666 也输出 777,现代浏览器通用这个方法
    var oBtn = document.getElementById("btn");

    oBtn.addEventListener("click", function () {
        alert("666");
    });
    oBtn.addEventListener("click", function () {
        alert("777");
    });

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

强行兼容

看似考虑周全,实则没有必要

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>加个判断</title>
</head>
<body>
<button id="btn">我是按钮</button>
<script>
    var oBtn = document.getElementById("btn");

    function addEvent(ele, name, fn) {
        if(ele.attachEvent){
            ele.attachEvent("on"+name, fn);
        }else{
            ele.addEventListener(name, fn);
        }
    }
</script>
</body>
</html>

事件对象

  • 事件对象就是一个系统自动创建的一个对象
  • 当注册的事件被触发的时候, 系统就会自动创建事件对象
  • 事件对象的属性海了去了

注意点

  1. 在高级版本的浏览器中, 会自动将事件对象传递给回到函数
  2. 在低级版本的浏览器中, 不会自动将事件对象传递给回调函数, 需要通过 window.event 来获取事件对象

关于阻止默认行为

  1. event.preventDefault(); 只支持现代浏览器
  2. event.returnValue = false; 只支持上古浏览器(了解即可, 这年头谁还用 IE8 啊)
  3. return false; 通吃
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>事件对象</title>
</head>
<body>
<button id="btn">我是按钮</button>
<a href="http://www.baidu.com">百度首页</a>
<script>
   var oBtn = document.getElementById("btn");
   oBtn.onclick = function (event) {
       // 兼容性的写法
       event = event || window.event;
       // alert("666");
       console.log(event);
       console.log(typeof event);
   }

   let oA = document.querySelector("a");
    oA.onclick = function (event) {
        // 兼容性的写法
        event = event || window.event;

        alert("666");
        // 阻止默认行为
        return false; // 企业开发推荐

        // event.preventDefault(); // 高级浏览器
        // event.returnValue = false; // IE9以下浏览器
    }
</script>
</body>
</html>

事件执行的三个阶段

三个阶段

  1. 捕获阶段(从外向内的传递事件)
  2. 当前目标阶段
  3. 冒泡的阶段(从内向外的传递事件)

注意点:

  • 三个阶段只有两个会被同时执行
  • 要么捕获和当前, 要么当前和冒泡

为什么要么只能是捕获和当前, 要么只能是当前和冒泡?

这是JS处理事件的历史问题

早期各大浏览器厂商为争夺定义权, 以及对事件的理解不同, 产生了捕获和冒泡两种流向

后续W3C为了兼容, 将两种方式都纳入标准

冒泡还是捕获

如何设置事件到底是捕获还是冒泡?

通过addEventListener方法, 这个方法接收三个参数

  • 第一个参数: 事件的名称
  • 第二个参数: 回调函数
  • 第三个参数: ==false 冒泡== / ==true 捕获==

注意点

  • onXxx的属性, 不接收任何参数, 所以默认就是冒泡
  • attachEvent方法, 只能接收两个参数, 所以默认就是冒泡
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>41-JavaScript-事件执行的三个阶段</title>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        .father{
            width: 300px;
            height: 300px;
            background: red;
        }
        .son{
            width: 150px;
            height: 150px;
            background: blue;
        }
    </style>
</head>
<body>
<div class="father">
    <div class="son"></div>
</div>
<script>
    let oFDiv = document.querySelector(".father");
    let oSDiv = document.querySelector(".son");
    oFDiv.addEventListener("click", function () {
        console.log("father");
    }, false);
    oSDiv.addEventListener("click", function () {
        console.log("son");
    }, false);
</script>
</body>
</html>

<!--
默认就是 false,也就是冒泡
设置为 true 的元素及其子元素链条都变为捕获,且仅影响自身及其子代,不影响其父元素
-->

关于冒泡的细节

IE6.0: div -> body -> html -> document

其他浏览器: div -> body -> html -> document -> window

注意: 不是所有的事件都能冒泡,有些事件不冒泡,如 blur、 focus、 load、 unload 等

阻止事件冒泡 (强行兼容)

首先要明确:阻止的前提是有事件,阻止事件冒泡的操作是写在实践方法的回调函数里的

  1. event.cancelBubble = true; (上古)
  2. event.stopProgagation(); (现代,知道这个才要紧)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>43-JavaScript-阻止事件冒泡</title>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        .father{
            width: 300px;
            height: 300px;
            background: red;
        }
        .son{
            width: 150px;
            height: 150px;
            background: blue;
        }
    </style>
</head>
<body>
<div class="father" id="father">
    <div class="son" id="son"></div>
</div>
<script>
    // 1.拿到需要操作的元素
    var oFDiv = document.getElementById("father");
    var oSDiv = document.getElementById("son");
    
    // 2.注册事件监听
    oFDiv.onclick = function () {
        console.log("father");
    }
    oSDiv.onclick = function (event) {
        event = event || window.event;
        // event.stopPropagation(); // 高级浏览器
        // event.cancelBubble = true; // 低级浏览器
        if(event.cancelBubble){
            event.cancelBubble = true;
        }else{
            event.stopPropagation();
        }
        console.log("son");
    }
</script>
</body>
</html>

不同移入移出事件辨析

区别在于是否触发冒泡(或捕获)

  1. onmouseover 和 onmouseenter 的区别
    • onmouseover 移入到子元素,父元素的移入事件也会被触发
    • onmouseenter 移入到子元素,父元素的移入事件不会被触发
  2. onmouseout 和 onmouseleave 的区别
    • onmouseout 移出到子元素,父元素的移入事件也会被触发
    • onmouseleave 移出到子元素,父元素的移入事件不会被触发
  3. 简单来说, enter 和 leave 比较干净不拖泥带水
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>44-JavaScript-移入移出事件区别</title>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        .father{
            width: 300px;
            height: 300px;
            background: red;
        }
        .son{
            width: 150px;
            height: 150px;
            background: blue;
        }
    </style>
</head>
<body>
<div class="father">
    <div class="son"></div>
</div>
<script>
    let oFDiv = document.querySelector(".father");
    let oSDiv = document.querySelector(".son");
    /*
    oFDiv.onmouseover = function () {
        console.log("father");
    }
    oSDiv.onmouseover = function () {
        console.log("son");
    }
     */
    /*
    oFDiv.onmouseenter = function () {
        console.log("father");
    }
    oSDiv.onmouseenter = function () {
        console.log("son");
    }
     */
    oFDiv.onmouseleave = function () {
        console.log("father");
    }
    oSDiv.onmouseleave = function () {
        console.log("son");
    }
</script>
</body>
</html>

通过事件获取元素位置

  • offsetX/offsetY: 事件触发点相对于当前元素自身以左上为原点的位置
  • clientX/clientY: 事件触发相对于浏览器可视区域的位置(上古浏览器不支持)
  • 注意点:
    • 显示器区域不包括滚动出去的部分(这个开发中用的少)
  • screenX/screenY: 事件触发相对于屏幕的位置
  • 整个网页包括滚动出去的部分 pageX/pageY: 事件触发相对于整个网页的位置(在页面不滚动时拿到的值和 clientX/clientY 是一样的)

细节们

  • 调用事件方法时 return false 可以禁用默认事件, 或者用 event.preventDefault() 也行

  • 如果想获取 query 到的 input 标签对象中输入的内容, 只能通过其 value 属性

    html 可以给标签设置自定义标签属性, 可以给 JS 用,

    甚至可以通过 JS 给指定 DOM 添加自定义属性标签

  • 在 JS 中如果 HTML 标签的属性名称和取值名称一样的时候, JS 会返回 true/false 但如果是通过代码给 input 设置的数据, 那么不会触发 oninput 事件

  • 对上古浏览器的兼容只需了解即可

移动端事件

移动端特有事件对象

  1. Touch事件对象
    • 移动端的touch事件也是一个事件, 所以被触发的时候系统也会自动传递一个事件对象给我们
  2. 移动端touch事件对象中比较重要的三个子对象
    1. touches: 当前屏幕上所有手指的列表
    2. targetTouches: 保存了当前元素上所有的手指里列表 (这个用的多)
    3. changedTouches: 当前屏幕上刚刚接触的手指或者离开的手指

移动端点透问题

  1. 移动端点透问题
    • 当一个元素放覆盖了另一个元素, 覆盖的元素监听 ==touch== 事件,而下面的元素监听 ==click== 事件
    • 并且touch事件触发后覆盖的元素就消失了, 那么就会出现点透问题
  2. 移动端点透问题出现的原因
    1. 当手指触摸到屏幕的时候,系统生成两个事件,一个是touch 一个是click
    2. touch 事件先执行,执行完后从文档上消失
    3. click 事件有 100~300ms 延迟, 所以后执行. (理由不对嗷, 我加了 content="scalable=no" 也还能点透)
    4. 但 click 事件执行的时候触发的元素已经消失了, 对应的位置现在是下面的元素, 所以就触发了下面元素的 click 事件
  3. 移动端点透问题解决方案 在touch事件中添加event.preverDefault(); 阻止事件扩散
<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>10-移动端点透问题</title>
		<style>
			* {
				margin: 0;
				padding: 0;
			}
			div {
				text-align: center;
				font-size: 40px;
			}
			.click {
				width: 300px;
				height: 300px;
				background: red;
				position: absolute;
				left: 50%;
				transform: translateX(-50%);
				top: 100px;
			}
			.tap {
				width: 200px;
				height: 200px;
				background: blue;
				position: absolute;
				left: 50%;
				transform: translateX(-50%);
				top: 150px;
			}
		</style>
	</head>
	<body>
		<div class="click">click</div>
		<div class="tap">tap</div>
        <!-- 两元素在同级, 但展现时两个块块有重叠 -->
		<script>
			let oClick = document.querySelector(".click");
			let oTap = document.querySelector(".tap");

			oTap.ontouchstart = function (event) {
				this.style.display = "none";
				event.preventDefault(); //  阻止事件扩散
			};
			oClick.onclick = function () {
				console.log("click");
			};
		</script>
	</body>
</html>