preventDefault
这个用来阻止默认行为。什么是默认行为呢?在初学的时候,最常举得例子就是<a>
标签,它的点击事件出发后,默认行为就是进行跳转。
现在如果想要阻止点击<a>
标签后的跳转行为,就可以使用preventDefault
<div>
<a id='a' href="http://www.baidu.com">《《传送门》》</a>
</div>
<script>
const aElement = document.querySelector('#a');
aElement.addEventListener('click', e => e.preventDefault());
</script>
兼容性鼠标事件
<div id='outer'>
<div id='inner'>
</div>
</div>
<script>
const outer = document.querySelector('#outer');
const inner = document.querySelector('#inner');
outer.addEventListener('mousedown', () => {
console.log('outer-mousedown');
});
outer.addEventListener('mouseup', () => {
console.log('outer-mouseup');
});
inner.addEventListener('mousedown', () => {
console.log('inner-mousedown');
});
inner.addEventListener('mouseup', () => {
console.log('inner-mouseup');
});
</script>
看上面的例子,如果点击#inner
会发生什么?首先会触发mousedown
事件,由于都是在冒泡阶段进行处理,所以输出inner-mousedown
、outer-mousedown
;然后触发mouseup
同样在冒泡阶段处理,输出inner-mouseup
、outer-mouseup
。
最终会输出:
inner-mousedown
outer-mousedown
inner-mouseup
outer-mouseup
上面的结果不难看懂,接下来做一些修改:
<div id='outer'>
<div id='inner'>
</div>
</div>
<script>
const outer = document.querySelector('#outer');
const inner = document.querySelector('#inner');
outer.addEventListener('mousedown', () => {
console.log('outer-mousedown');
});
outer.addEventListener('mouseup', () => {
console.log('outer-mouseup');
});
inner.addEventListener('mousedown', () => {
console.log('inner-mousedown');
});
inner.addEventListener('mouseup', () => {
console.log('inner-mouseup');
});
inner.addEventListener('pointerdown', e => {
console.log('inner-pointerdown');
e.preventDefault();
});
</script>
这次,在inner
上绑定了一个pointerdown
事件,点击之后发现,只输出了inner-pointerdown
,也就是说,mouse
相关的事件一个都没有触发。
这就是题目说的,mousedown
是pointerdown
的默认行为,可能会有疑问:如果将outer
的mousedown
放在捕获阶段处理呢?其实这里产生了一个误区,捕获和冒泡仅仅会影响相同事件的处理顺序,也就是说,如果将outer
的mousedown
在捕获阶段处理,也仅仅只是让outer
的mousedown
先于inner
的mousedown
,和其他事件没有关系。
回到上面的问题,由于mousedown
是pointerdown
的默认行为,所以pointerdown
先于mousedown
触发,因此,先触发inner
的pointerdown
,之后,本来要触发默认行为,inner
触发一个mousedown
事件,但是被通过e.preventDefault()
阻止了,所以,并没有触发mousedown
事件。
现在,已经弄清楚两个mousedown
为什么没有触发了,还有一个问题两个mouseup
为什么没有触发?举一反三,是因为mousedown
没有触发,也就是说,mouseup
是mousedown
的默认行为,因此,必须先触发mousedown
才能触发mouseup
。也可以做个实验,在最初的例子中,阻止inner
的mousedown
的默认行为。
关于这一段的问题,MDN有详细的解释:
The browser may map generic pointer input to mouse events for compatibility with mouse-based content. This mapping of events is called compatibility mouse events. Authors can prevent the production of certain compatibility mouse events by canceling the pointerdown event but note that:
- Mouse events can only be prevented when the pointer is down.
- Hovering pointers (e.g. a mouse with no buttons pressed) cannot have their mouse events prevented.
- The
mouseover
,mouseout
,mouseenter
, andmouseleave
events are never prevented (even if the pointer is down).
阻止mousedown的默认行为,会阻止blur
假设有一个输入框,在blur
事件触发时,会执行一些操作;有另一个按钮,在按钮电击时,执行某些操作,但是,不希望触发输入框的blur
事件。
这就和上面所说的默认行为一样,如果输入框要触发blur
事件,一定会在其他元素上触发mousedown
,反过来说,一个非输入框的元素触发mousedown
,它的默认行为就是输入框触发blur
。
<input type="text" id='input'>
<div id='dom'>
</div>
<script>
const dom = document.querySelector('#dom');
const input = document.querySelector('#input');
dom.addEventListener('mousedown', e => e.preventDefault());
input.addEventListener('blur', () => console.log('input-blur'));
</script>
这时,点击#dom
时,默认情况下#input
失去焦点,触发blur
,但这里阻止了这种默认行为。
注意,如果将上面的dom结构改成:
<div id='dom'>
<input type="text" id='input'>
</div>
<script>
const dom = document.querySelector('#dom');
const input = document.querySelector('#input');
dom.addEventListener('mousedown', e => e.preventDefault());
input.addEventListener('blur', () => console.log('input-blur'));
input.addEventListener('focus', ()=>console.log('input-focus'));
</script>
这次再点击,就会发现#input
连焦点都没办法获取,这和上面的原因也一样,mousedown
的默认行为也有获取焦点focus
,如果阻止默认行为,获取焦点的事件也会被阻止。