事件
事例参考的 HTML 结构
<div class="my-select">
<button id="selectBtn">选择</button>
<ul class="select-list">
<li>
<input id="countryInput" type="text" name="country" placeholder="请输入">
<button id="addCountry">确定</button>
</li>
<li class="select-item" data-value="中国">中国</li>
<li class="select-item" data-value="美国">美国</li>
<li class="select-item" data-value="英国">英国</li>
<li class="select-item" data-value="德国">德国</li>
</ul>
</div>
绑定事件
绑定事件的三种方法DOM、DOM2、DOM3,在之后的使用中,请使用 DOM3 的方法。
DOM1
在 HTML 中绑定,DOM1的事件毁掉方法中,this 对象的指向为 window 。
<button id="selectBtn" onclick="testfunc()">选择</button>
<script>
function testfunc(){
console.log('bindDOM done',this)
}
</script>
DOM2
在元素的属性中绑定,this 对象指向绑定元素本身。
var selectBtn = document.getElementById('selectBtn');
selectBtn.onclick = function(){
console.log('bindDOM2 done',this)
}
DOM3
通过 addEventListener 将指定的监听器注册到 EventTarget 上,当该对象触发指定的事件时,指定的回调函数就会被执行。
var selectBtn = document.getElementById('selectBtn');
selectBtn.addEventListener('click',function(){
console.log('bindDOM3 done')
})
事件流程
DOM2级事件开始 规定事件的三个阶段:事件捕获阶段 -> 处于目标阶段 -> 事件冒泡阶段。
- 事件由具体元素接收,然后逐级向上传播的事件流程成为事件冒泡。
- 所有DOM触发的事件上,都会产生事件对象 event
- 使用event.stopPropagation 用于立即停止事件在DOM层次中的传播(例如冒泡)
- 使用event.preventDefault 用于阻止事件的默认行为(例如提交)
var mySelect = document.getElementsByClassName('my-select')[0];
var selectBtn = document.getElementById('selectBtn');
mySelect.addEventListener('click',function(){
console.log('my-select 被点击了');
})
selectBtn.addEventListener('click',function(){
console.log('select-btn 被点击了');
})
我们会发现当我们,点击 selectBtn 的时候,同时也触发了 my-select 的点击事件,正是因为事件的冒泡,selectBtn 的点击事件逐层的往上冒泡,直到最外层元素。怎么才能避免事件的冒泡呢,这时候可以用到 event 事件对象的 stopPropagation 方法,停止事件的传播行为。
var mySelect = document.getElementsByClassName('my-select')[0];
var selectBtn = document.getElementById('selectBtn');
mySelect.addEventListener('click',function(){
console.log('my-select 被点击了');
})
selectBtn.addEventListener('click',function(event){
event.stopPropagation()
console.log('select-btn 被点击了');
})
这样我们就会发现,点击 selectBtn 的时候,没有触发 my-select 的点击事件。
委托绑定
委托绑定使用场景是在我们动态生成元素,在元素生成之前绑定好事件。在一般情况下,我们每生成一个需要交互元素都要绑定一次事件。因为刚刚开始绑定事件的时候,我们动态生成没有生成出来,无法绑定到。通过事件处理流程,我们可以使用委托绑定的方法,把事件绑定在父级元素,然后当我们点击子元素时,事件会冒泡出发父元素的绑定事件,然后在绑定事件中,判断是否我们意向的子元素触发而来,如果是,就执行相应事件。
- 原生委托绑定事例
var selectList = document.getElementsByClassName('select-list')[0];
selectList.addEventListener('click',function(event){
var that = event.target; // 获取当前点击的元素
var selectItemsClassName = that.getAttribute('class') ? that.getAttribute('class') : '';
var hasSelectItemClass = (selectItemsClassName.indexOf('select-item') === -1 );
// 如果点击来源是 select-item 的子元素才触发事件
if(!hasSelectItemClass){
console.log('do Something')
}
})
- jQuer原生绑定事例
$('.select-list').on('click','.select-item',function(e){
console.log('do Something')
})
点击事件
- click 点击
- dbclick 双击
输入事件
focus 获得焦点
blur 失去焦点
change 元素内容发生改变
<input id="userName" type="text" name="test_name" placeholder="请输入abc">
var userName = document.getElementById('userName');
userName.addEventListener('focus',function(){
console.log('你聚焦了输入框')
})
userName.addEventListener('keyup',function(){
console.log('你正在输入',this.value)
})
userName.addEventListener('change',function(){
console.log('你输入了',this.value,',发生了改变!')
})
userName.addEventListener('blur',function(){
console.log('你离开了输入框')
})
滚动事件
scroll 在文档被滚动期间重复触发
- 原生
window.addEventListener('scroll',function(event){
console.log(window.pageYOffset)
})
- jQuery
$(window).on('scroll',function(){
var winScroll = $(window).scrollTop(); // 页面滚动的距离
console.log(winScroll)
})
鼠标事件
mousedown 用户按下鼠标任意按钮
mouseenter 鼠标光标首次从原属外部移动到元素内部范围之内触发
mouseleave 在位于元素上方的鼠标光标移动到元素范围之外时触发
mousemove 鼠标在元素内移动时重复触发
参照以下完成仿卡片鼠标点击拖拉事例。逻辑如下:
<div style="height: 400px;"></div>
<div class="card-list-contaienr">
<div class="card-item">haha</div>
</div>
<div style="height: 400px;"></div>
*{
margin: 0;
padding: 0;
}
.card-list-contaienr{
position: relative;
width: 800px;
height: 400px;
background: #f5f5f5;
margin: 0 auto;
overflow: hidden;
}
.card-item{
position: absolute;
top: 20px;
left: 20px;
width: 100px;
height: 100px;
background-color: #ccc;
border: 1px solid #e4e4e4;
}
// 使用前请确保引用了 jQuery
$(function(){
var cardPage = {
init:function(){
// 插入卡片
this.setCard();
// 绑定事件
this.bind();
},
setCard:function(){
// 定义卡片内容
let cardList = [{name:'Jax'},{name:'Joye'},{name:'Jimmy'},{name:'Jay'}];
// 拼接DOM结构
let html = '';
cardList.forEach(function(card,index){
let tmp = `<div class="card-item" style="z-index:${index + 1}">${card.name}</div>`;
html += tmp
})
// 插入
$('.card-list-contaienr').append(html);
// 存储与数量相关的层级数
cardPage.zindex = cardList.length;
},
bind:function(){
// 委托绑定 item项 鼠标按下时候触发
$('.card-list-contaienr').on('mousedown','.card-item',this.mousedownEvent)
// 绑定容器,当容器有鼠标滑过就触发
$('.card-list-contaienr').on('mousemove',this.mousemoveEvent)
// 委托绑定 item项 鼠标按起时候触发
$('.card-list-contaienr').on('mouseup','.card-item',this.lockMouseEvent)
// 绑定容器,如果鼠标离开触发
$('.card-list-contaienr').on('mouseleave',this.lockMouseEvent)
},
// 锁,用于锁住鼠标滑动事件的运行
isLock:true,
// 成叠级别数量,用于确保点击的项可以提前到最高级
zindex:0,
// 项目,用来存储点击的项目
item:null,
// 偏移查
itemDiffOffsetX:null,
itemDiffOffsetY:null,
// 鼠标点击时候
mousedownEvent:function(event){
// 开锁
cardPage.isLock = false;
// 存储偏移量的差
// 假设 容器和页面左边距离 为 a
// 项目和容器直接的距离为 b ( this.offsetX)
// 鼠标原点和项目左侧的距离为 c
// ClientX 为 a + b + c;
// 存储偏移量的差的距离 a + c;
// 当鼠标发生移动时候
// ClientX_2 为 a + b + d(移动值) + c
// 这时候项目和容器直接的距离为 b + d(移动值)
// b + d = ClienX_2 - 存储偏移量的差( a + c)
itemDiffOffsetX = event.clientX - this.offsetLeft;
itemDiffOffsetY = event.clientY - this.offsetTop;
cardPage.itemDiffOffsetX = itemDiffOffsetX;
cardPage.itemDiffOffsetY = itemDiffOffsetY;
// 设置当前元素的 z-index 值为最高,并刷新最高值
let zindex = cardPage.zindex + 1;
$(this).css({'z-index':zindex});
cardPage.zindex = zindex;
// 存储当前点击的元素
cardPage.item = $(this);
},
mousemoveEvent:function(event){
// 如果有锁,返回
if(cardPage.isLock){
return
}
// 如果没锁,获取 ClienX_2 - 存储偏移量的差 计算出偏移量
let top = event.clientY - cardPage.itemDiffOffsetY;
let left = event.clientX - cardPage.itemDiffOffsetX;
// 从存储中获取到当前的项,然后设置偏移量
cardPage.item.css({
top:top,
left:left
})
},
lockMouseEvent:function(){
// 移动结束,锁回去
cardPage.isLock = true;
}
}
cardPage.init();
})
触摸事件
触摸事件在普通显示屏上无效,在手机和iPad上面有效。
touchstart
touchmove
touchend
touchcancel
键盘事件
keydown 和 keyup 事件时候,event 对象的 keyCode 会包含一个代码于键盘上一个特定的键位对应。
keydown 用户按下任意键触发
keypress 用户按下字符键时候触发
keyup 当用户释放任意键时候触发
拖拽事件
- 源对象:
- dragstart:源对象开始拖放。
- drag:源对象拖放过程中。
- dragend:源对象拖放结束。
- 过程对象:
- dragenter:源对象开始进入过程对象范围内。
- dragover:源对象在过程对象范围内移动。
- dragleave:源对象离开过程对象的范围。
- 目标对象:
- drop:源对象被拖放到目标对象内。
浏览器事件
load 加载完毕
unload 卸载完毕
resize 视窗改变
当页面完全加载后(包括图像、JavaScript文件、CSS 文件等外部资源),就会触发 window 上的 load 事件。图片 img 和 脚本 script 也可以触发 load事件,以便开发人员确认文件是否加载完毕。
window.addEventListener('load',function(){
console.log('window load')
})
unload 事件在文档被完全卸载后触发。只要用户从一个页面切到另一个页面,就会触发unload事件。unload 是一切都被卸载后触发,那么也没加载后存在的对象就不一定存在了,此时,操作DOM节点或者元素样式就会导致错误。
window.addEventListener('unload',function(){
console.log('window unload')
})
resize 事件当浏览器窗口被调节到一个新的高度或者高度时候,就会触发 resize 事件。浏览器窗口最大化,最小化也会触发 resize 事件。 不同浏览器触发 resize 事件有不同的机制,有些重复触发,有些是操作停止后触发。
window.addEventListener('resize',function(event){
console.log(document.documentElement.getBoundingClientRect().width)
})