事件

事例参考的 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)
})