事件
- addEventListener 用来处理多个事件同时绑定
element.addEventListener(event, handler[, options]);
handler 事件处理程序, event 事件对象,handleEvent 对象处理程序
冒泡和捕获
- 冒泡
当一个事件发生在一个元素上,它会首先运行在该元素上的处理程序,然后运行其父元素上的处理程序,然后一直向上到其他祖先上的处理程序
- event.target
引发事件的那个嵌套层级最深的元素被称为目标元素,可以通过 event.target 访问
停止冒泡
event.stopPropagation()用于停止冒泡
event.stopImmediatePropagation() 用于停止冒泡,并阻止当前元素上的处理程序运行。使用该方法之后,其他处理程序就不会被执行
捕获
DOM事件标准描述了事件传播的 3 个阶段:
- 捕获阶段(Capturing phase)—— 事件(从 Window)向下走近元素。
- 目标阶段(Target phase)—— 事件到达目标元素。
- 冒泡阶段(Bubbling phase)—— 事件从元素上开始冒泡。
如果要在捕获阶段捕获事件 ,需要将处理程序的 capture选项设置为 true
ele.addEventListener(..., {capture: true})
ele.addEventListener(..., true)
事件委托
如果我们有许多以类似方式处理的元素,那么就不必为每个元素分配一个处理程序 —— 而是将单个处理程序放在它们的共同祖先上
- 行为模式
- 行为: 计数器
var btn = document.querySelector("button");
var num = 0;
btn.onclick = function (){
btn.innerHTML = ++num;
if(btn.innerText == 10){
alert("不累么??")
btn.style.background = "orange";
}
}
- 行为:切换器
<div class="wrapper">
<button data-toggle-id="changeBox">点击切换</button>
<div id="changeBox"></div>
</div>
<script>
let btn = document.querySelector('button')
btn.addEventListener('click', function(event){
let id = event.target.dataset.toggleId;
if(!id) return
let ele = document.querySelector('#'+id)
ele.style.display == 'none' ?
ele.style.display = 'block':
ele.style.display = 'none'
})
</script>
浏览器默认行为
event.preventDefault() 用于阻止默认行为,如果处理程序是使用 on<event>(而不是 addEventListener)分配的,那返回 false 也同样有效
创建自定义事件
- 事件构造器
let event = new Event(type[,options])
type —— 事件类型,可以是像这样 "click" 的字符串,或者我们自己的像这样 "my-event" 的参数
options —— 具有两个可选属性的对象:默认情况下,以上两者都为 false:{bubbles: false, cancelable: false}
- 触发
通过使用 elem.dispatchEvent(event)调用在元素上“运行” - 其他一些事件
● UIEvent
● FocusEvent
● MouseEvent
● WheelEvent
● KeyboardEvent
let event = new MouseEvent("click", {
bubbles: true,
cancelable: true,
clientX: 100,
clientY: 100
});
- 自定义事件
<h1 id="elem">Hello for John!</h1>
<script>
// 事件附带给处理程序的其他详细信息
elem.addEventListener("hello", function(event) {
alert(event.detail.name);
});
elem.dispatchEvent(new CustomEvent("hello", {
detail: { name: "John" }
}));
</script>
鼠标事件
- 鼠标事件类型
mousedown / mouseup
在元素上点击/释放鼠标按钮
mouseover / mouseout
鼠标指针从一个元素上移入/移出
mousemove
鼠标在元素上的每个移动都会触发此事件
click
果使用的是鼠标左键,则在同一个元素上的 mousedown 及 mouseup 相继触发后,触发该事件
dblclick
在短时间内双击同一元素后触发
contextmenu
在鼠标右键被按下时触发
事件顺序
在单个动作触发多个事件时, 事件的顺序是固定的,会遵循 mousedown -> mouseup -> click 的顺序调用处理程序-
鼠标按钮
使用 button 属性来区分是左键单击还是右键单击
组合键: shift、alt、ctrl、meta
事件属性:
● shiftKey:Shift
● altKey:Alt(或对于 Mac 是 Opt)
● ctrlKey:Ctrl
● metaKey:对于 Mac 是 Cmd坐标: clientX/Y, pageX/Y
- 相对于窗口的坐标:clientX 和 clientY。
- 相对于文档的坐标:pageX 和 pageY
-
防止在鼠标按下时的选择
防止浏览器对 mousedown进行操作,可以阻止鼠标按下时的选择
Before...
<b ondblclick="alert('Click!')" onmousedown="return false">
Double-click me
</b>
...After
通过使用oncopy事件, 可以防止用户复制
<div oncopy="alert('Copying forbidden!');return false">
Dear user,
The copying is forbidden for you.
If you know JS or HTML, then you can get everything from the page source though.
</div>
移动鼠标: mouseover/out , mouseenter/leave
-
事件mouseover/mouseout, relatedTarget
当鼠标指针移到某个元素上时,mouseover 事件就会发生,而当鼠标离开该元素时,mouseout 事件就会发生
对于mouseover:
● event.target —— 是鼠标移过的那个元素。
● event.relatedTarget —— 是鼠标来自的那个元素(relatedTarget → target) -
跳过元素
注意: 如果 mouseover被触发了, 则必须有 mouseout -
事件 mouseenter和mouseleave
事件 mouseenter/mouseleave 类似于 mouseover/mouseout。它们在鼠标指针进入/离开元素时触发
- 元素内部与后代之间的转换不会产生影响。
- 事件 mouseenter/mouseleave 不会冒泡。
鼠标拖放事件
- 拖放算法
- 在 mousedown 上 —— 根据需要准备要移动的元素(也许创建一个它的副本,向其中添加一个类或其他任何东西)
- 然后在 mousemove 上,通过更改 position:absolute 情况下的 left/top 来移动它
- 在 mouseup 上 —— 执行与完成的拖放相关的所有行为
ball.onmousedown = function(event) {
// (1) 准备移动:确保 absolute,并通过设置 z-index 以确保球在顶部
ball.style.position = 'absolute';
ball.style.zIndex = 1000;
// 将其从当前父元素中直接移动到 body 中
// 以使其定位是相对于 body 的
document.body.append(ball);
// 现在球的中心在 (pageX, pageY) 坐标上
function moveAt(pageX, pageY) {
ball.style.left = pageX - ball.offsetWidth / 2 + 'px';
ball.style.top = pageY - ball.offsetHeight / 2 + 'px';
}
// 将我们绝对定位的球移到指针下方
moveAt(event.pageX, event.pageY);
function onMouseMove(event) {
moveAt(event.pageX, event.pageY);
}
// (2) 在 mousemove 事件上移动球
document.addEventListener('mousemove', onMouseMove);
// (3) 放下球,并移除不需要的处理程序
ball.onmouseup = function() {
document.removeEventListener('mousemove', onMouseMove);
ball.onmouseup = null;
};
};
为防止浏览器自己的拖放处理与我们的拖放处理产生冲突,需禁用它
ball.ondragstart = function() {
return false;
};
- 修正定位
ball.onmousedown = function(event) {
let shiftX = event.clientX - ball.getBoundingClientRect().left;
let shiftY = event.clientY - ball.getBoundingClientRect().top;
ball.style.position = 'absolute';
ball.style.zIndex = 1000;
document.body.append(ball);
moveAt(event.pageX, event.pageY);
// 移动现在位于坐标 (pageX, pageY) 上的球
// 将初始的偏移考虑在内
function moveAt(pageX, pageY) {
ball.style.left = pageX - shiftX + 'px';
ball.style.top = pageY - shiftY + 'px';
}
function onMouseMove(event) {
moveAt(event.pageX, event.pageY);
}
// 在 mousemove 事件上移动球
document.addEventListener('mousemove', onMouseMove);
// 放下球,并移除不需要的处理程序
ball.onmouseup = function() {
document.removeEventListener('mousemove', onMouseMove);
ball.onmouseup = null;
};
};
ball.ondragstart = function() {
return false;
};
-
潜在的放置目标
有一个叫做 document.elementFromPoint(clientX, clientY) 的方法。它会返回在给定的窗口相对坐标处的嵌套的最深的元素(如果给定的坐标在窗口外,则返回 null)
基于 onMouseMove 扩展的代码,用于查找 “droppable” 的元素
// 我们当前正在飞过的潜在的 droppable 的元素
let currentDroppable = null;
function onMouseMove(event) {
moveAt(event.pageX, event.pageY);
ball.hidden = true;
let elemBelow = document.elementFromPoint(event.clientX, event.clientY);
ball.hidden = false;
// mousemove 事件可能会在窗口外被触发(当球被拖出屏幕时)
// 如果 clientX/clientY 在窗口外,那么 elementfromPoint 会返回 null
if (!elemBelow) return;
// 潜在的 droppable 的元素被使用 "droppable" 类进行标记(也可以是其他逻辑)
let droppableBelow = elemBelow.closest('.droppable');
if (currentDroppable != droppableBelow) {
// 我们正在飞入或飞出...
// 注意:它们两个的值都可能为 null
// currentDroppable=null —— 如果我们在此事件之前,鼠标指针不是在一个 droppable 的元素上(例如空白处)
// droppableBelow=null —— 如果现在,在当前事件中,我们的鼠标指针不是在一个 droppable 的元素上
if (currentDroppable) {
// 处理“飞出” droppable 的元素时的处理逻辑(移除高亮)
leaveDroppable(currentDroppable);
}
currentDroppable = droppableBelow;
if (currentDroppable) {
// 处理“飞入” droppable 的元素时的逻辑
enterDroppable(currentDroppable);
}
}
}
键盘: keydown 和 keyup
当一个按键被按下时,会触发keydown事件,而当按键被释放时,会触发keyup事件。
注意: event.code 和 event.key
事件对象的 key 属性允许获取字符,而事件对象的 code 属性则允许获取“物理按键代码”
滚动
-
防止滚动
只能在 pageUp和pageDown的 keydown事件上, 使用event.preventDefault() 来阻止滚动
表单属性和方法
-
导航: 表单和元素
文档中的表单是特殊集合 document.forms 的成员 -
input和textarea
通过 input.value(字符串)或 input.checked(布尔值)来访问复选框(checkbox)中的它们的 value
聚焦: focus / blur
-
focus/blur 事件
当元素聚焦时,会触发focus事件,当元素失去焦点时,会触发blur事件 -
focus/blur方法
elem.focus() 和 elem.blur() 方法可以设置和移除元素上的焦点 - 允许在任何元素上聚焦: tabindex
点击第一项,然后按 Tab 键。跟踪顺序。请注意,多按几次 Tab 键后,会将焦点移到这个通过 iframe 嵌入的示例的外面。
<ul>
<li tabindex="1">One</li>
<li tabindex="0">Zero</li>
<li tabindex="2">Two</li>
<li tabindex="-1">Minus one</li>
</ul>
<style>
li { cursor: pointer; }
:focus { outline: 1px dashed green; }
</style>
-
focus/blur 委托
focus 和 blur 事件不会向上冒泡。
使用 focusin 和 focusout 事件 —— 与 focus/blur 事件完全一样,只是它们会冒泡。
值得注意的是,必须使用 elem.addEventListener 来分配它们,而不是 on<event>
表单事件: change, input, cut, copy, paste
-
事件: change
当元素更改完成时, 将触发change事件
<input type="text" onchange="alert(this.value)">
-
事件: input
每当用户对输入值进行修改后, 就会触发input 事件 -
事件: cut、copy、paste
这些事件发生于剪切/拷贝/粘贴一个值的时候
它们属于 ClipboardEvent类, 并提供了对剪切/拷贝/粘贴的数据的访问方法
<input type="text" id="input">
<script>
input.onpaste = function(event) {
alert("paste: " + event.clipboardData.getData('text/plain'));
event.preventDefault();
};
input.oncut = input.oncopy = function(event) {
alert(event.type + '-' + document.getSelection());
event.preventDefault();
};
</script>