【DOM】节点操作、事件高级

该系列文章是博主学习前端入门课程的笔记,同时也为了方便查阅,有任何错漏或疑问都欢迎在评论区提出。

第一章 节点操作

1.1 为什么需要节点操作?

前面介绍了利用DOM提供的方法获取元素

  • document.getelementById()
  • document.getelementByClassName()
  • document.querySelector等

这种方法很繁琐,且不能体现页面的层级关系,使用节点操作获取元素会更简单且能体现页面的层级关系

1.2 节点概述

网页中的所有内容都是节点(标签、属性、文本、注释等),在DOM中,节点使用node来表示。

HTML DOM树中的所有节点均可通过JavaScript进行访问,所有HTML元素(节点)均可被修改,也可以创建或删除。

在这里插入图片描述

节点至少有 nodeType , nodeName , nodeValue 这三个基本属性,在实际开发中通常操作的是元素节点。

  • 元素节点 nodeType 为 1
  • 属性节点 nodeType 为 2
  • 文本节点 nodeType 为 3 (文本节点包含文字、空格、换行等)
<div class="box">
    <span class="erweima">x</span>
</div>

<script>
    var box = document.querySelector('.box');
    console.dir(box);

在这里插入图片描述

1.3 节点层级

利用DOM树可以把节点划分为不同的层级关系,常见的是父子兄层级关系。
在这里插入图片描述

1. 父节点

node.parentNode:返回距离最近的父节点

<div class="demo">
    <div class="box">
        <span class="erweima">x</span>
    </div>
</div>

<script>
    // 父节点 parentNode
    var erweima = document.querySelector('.erweima');
    // 得到的是离得最近的父级节点,如果找不到父节点就返回null
    console.log(erweima.parentNode);
	var demo = document.querySelector('.demo');
    console.log(demo.parentNode);
    console.log(document.parentNode);
</script>

输出

在这里插入图片描述

2. 子节点

  • node.childNodes(标准):返回指定节点的所有子节点(包括元素节点,文本节点等)。

代码示例

<ul>
    <li>
        <div class="test">我是li</div>
    </li>
    <li>我是li</li>
    <li>我是li</li>
    <li>我是li</li>
</ul>

<script>
    // 1.DOM提供的方法(API)获取ul中的li
    var ul = document.querySelector('ul');
    var lis = ul.querySelectorAll('li');

    // 2.子级节点 childNodes 返回所有的子节点,包含元素节点和文本节点
    console.log(ul.childNodes);
    console.log(ul.childNodes[0].nodeType);
    console.log(ul.childNodes[1].nodeType);
    
    // 如果只需要元素节点,需要特别操作,所有不提倡使用childNode
    for (var i = 0; i < ul.childNodes.length; i++) {
        // console.log(i);
        if (ul.childNodes[i].nodeType == 1) {
            console.log(ul.childNodes[i]);
        }
    }
</script>

控制台输出

在这里插入图片描述

  • node.children(非标准但得到各个浏览器的支持,更常用):返回指定节点的所有子元素节点

代码示例

<ul>
    <li>
        <div class="test">我是li</div>
    </li>
    <li>我是li</li>
    <li>我是li</li>
    <li>我是li</li>
</ul>

<script>
    // 1.DOM提供的方法(API)获取ul中的li
    var ul = document.querySelector('ul');
    var lis = ul.querySelectorAll('li');

    // 3.children 获取所有的子元素节点,开发常用
    console.log(ul.children);
</script>

控制台输出

在这里插入图片描述

  • node.firstChild:返回第一个子节点,找不到则返回null。
  • node.lastChild: 返回最后一个子节点,找不到则返回null。
  • node.firstElementChild:返回第一个子元素节点,找不到则返回null。
  • node.lastElementChild:返回最后一个子元素节点,找不到则返回null。

代码示例

<ul>
    <li>第1个li</li>
    <li>第2个li</li>
    <li>第3个li</li>
    <li>第4个li</li>
</ul>

<script>
    var ul = document.querySelector('ul');
    // 1.firstChild 返回第一个子节点,无论是元素节点还是文本节点
    console.log(ul.firstChild);
    console.log(ul.lastChild);

    // 2.firstElementChile 返回第一个子元素节点,有兼容性问题,IE9以上才支持
    console.log(ul.firstElementChild);
    console.log(ul.lastElementChild);

    // 3.实际开发写法
    console.log(ul.children[0]);
    console.log(ul.children[ul.children.length - 1]);
</script>

控制台输出

在这里插入图片描述

案例:仿新浪下拉菜单

<style>
    * {
        margin: 0;
        padding: 0;
    }

    li {
        list-style-type: none;
    }

    a {
        text-decoration: none;
        font-size: 14px;
    }

    .nav {
        margin: 100px;
    }

    .nav>li {
        position: relative;
        float: left;
        width: 80px;
        height: 41px;
        text-align: center;
    }

    .nav li a {
        display: block;
        width: 100%;
        height: 100%;
        line-height: 41px;
        color: #333;
    }

    .nav>li>a:hover {
        background-color: #eee;
    }

    .nav ul {
        display: none;
        position: absolute;
        top: 41px;
        left: 0;
        width: 100%;
        border-left: 1px solid #FECC5B;
        border-right: 1px solid #FECC5B;
    }

    .nav ul li {
        border-bottom: 1px solid #FECC5B;
    }

    .nav ul li a:hover {
        background-color: #FFF5DA;
    }
</style>

<ul class="nav">
    <li>
        <a href="#">微博</a>
        <ul>
            <li>
                <a href="">私信</a>
            </li>
            <li>
                <a href="">评论</a>
            </li>
            <li>
                <a href="">@我</a>
            </li>
        </ul>
    </li>
    <li>
        <a href="#">微博</a>
        <ul>
            <li>
                <a href="">私信</a>
            </li>
            <li>
                <a href="">评论</a>
            </li>
            <li>
                <a href="">@我</a>
            </li>
        </ul>
    </li>
    <li>
        <a href="#">微博</a>
        <ul>
            <li>
                <a href="">私信</a>
            </li>
            <li>
                <a href="">评论</a>
            </li>
            <li>
                <a href="">@我</a>
            </li>
        </ul>
    </li>
    <li>
        <a href="#">微博</a>
        <ul>
            <li>
                <a href="">私信</a>
            </li>
            <li>
                <a href="">评论</a>
            </li>
            <li>
                <a href="">@我</a>
            </li>
        </ul>
    </li>
</ul>
<script>
    // 1.获取元素
    var nav = document.querySelector('.nav');
    var lis = nav.querySelectorAll('li');

    // 2.循环注册事件
    for (var i = 0; i < lis.length; i++) {
    	// 相较于之前自定义属性的方式,节点操作更简洁
        lis[i].onmouseenter = function () {
            this.children[1].style.display = 'block';
        }
        lis[i].onmouseleave = function () {
            this.children[1].style.display = 'none';
        }
    }
</script>

3. 兄弟节点

  • node.nextSibling:返回当前元素的下一个兄弟节点,找不到则返回null。
  • node.previousSibling:返回当前元素的上一个兄弟节点,找不到则返回null。
  • node.nextElementSibling:返回当前元素下一个兄弟元素节点,找不到则返回null。有兼容性问题, IE9 以上才支持。
  • node.previousElementSibling:返回当前元素上一个兄弟元素节点,找不到则返回null。有兼容性问题, IE9 以上才支持。

代码示例

<div>我是div</div>
<span>我是span</span>

<script>
    var div = document.querySelector('div');
    // 1.nextSibling 下一个兄弟节点:包含元素节点和文本节点
    console.log(div.nextSibling);
    console.log(div.previousSibling);

    // 2.nextElementSibling 下一个兄弟元素节点
    console.log(div.nextElementSibling);
    console.log(div.previousElementSibling); // 找不到时输出null
</script>

控制台输出

在这里插入图片描述

兼容性写法:既能只返回兄弟元素节点,又不产生兼容性问题。了解即可,实际开发通常不要求

function getNextElementSibling(element) {
    var el = element;
    // 如果存在兄弟节点则循环继续
    while (el = el.nextSibling) {
    	// 兄弟节点是元素类型时返回
        if (el.nodeType === 1) {
            return el;
        }
    }
    return null;
}

了解到如何获取节点后,接下来可以尝试对节点进行操作了

1.4 创建和添加节点

document.createElement(‘tagName’) :创建由 tagName 指定的 HTML 元素。因为这些元素原先不存在,是根据需求动态生成的,所以也称为动态创建元素节点

<ul>
    <li>123</li>
</ul>

<script>
    // 1.创建元素节点
    var li = document.createElement('li');
</script>

利用createElement创建节点后发现并没有在页面中看到节点,还需要将节点添加到指定位置

在这里插入图片描述
添加节点有两种方式

  • parentNode.appendChild(child):将一个节点添加到指定节点的子节点列表末尾。类似于 CSS 里面的 after 伪元素。
  • parentNode.insertBefore(child,指定元素):将一个节点添加到父节点的指定子节点前面。类似于 CSS 里面的 before 伪元素。
<ul>
    <li>123</li>
</ul>

<script>
    // 1.创建元素节点
    var li = document.createElement('li');
	// 2.添加节点 node.appendChild(child) 在元素末尾追加元素,类似数组的push()方法
	var ul = document.querySelector('ul');
	li.innerHTML = 'appendChild';
	ul.appendChild(li);
	// 3.添加节点 node.insertBefore(child,指定元素) 添加到指定元素的前面
	var lili = document.createElement('li');
	lili.innerHTML = 'insertBefore';
	ul.insertBefore(lili, ul.children[0]);
</script>

效果图

在这里插入图片描述

1.5 删除节点

parentNode.removeChild(child):从 DOM 中删除一个子节点,返回删除的节点。

案例:点击按钮后会删除1个li,但li全被删除时按钮被禁用

<button>删除</button>

<ul>
    <li>熊大</li>
    <li>熊二</li>
    <li>光头强</li>
</ul>

<script>
    // 1.获取元素
    var ul = document.querySelector('ul');
    // 2.删除元素
    // ul.removeChild(ul.children[0]);
    // 3.点击按钮依次删除子元素
    var btn = document.querySelector('button');
    btn.onclick = function () {
        console.log(ul.removeChild(ul.children[0]));
        if (ul.children.length == 0) {
            btn.disabled = true;
        }
    }
</script>

效果图

在这里插入图片描述
案例:评论区案例

<style>
    body {
        padding: 100px;
    }

    li {
        width: 300px;
        background-color: pink;
        color: red;
        margin: 5px 0;
    }

    li a {
        float: right;
    }
</style>

<textarea name="" id="" cols="30" rows="10"></textarea>
<button>发布</button>

<ul></ul>

<script>
    var btn = document.querySelector('button');
    var text = document.querySelector('textarea');
    var ul = document.querySelector('ul');


    btn.onclick = function () {
        if (text.value == '') {
            alert('您没有输入内容');
            return false;
        } else {
            // a.创建节点
            var li = document.createElement('li');
            // 表单的值是通过value获得的
            // href="javascript:; 阻止链接跳转 javascript:void(0)也同理
            li.innerHTML = text.value + '<a href="javascript:;">删除</a>';
            // b.添加节点
            // ul.appendChild(li);
            ul.insertBefore(li, ul.children[0]);
            // c.删除元素
            var as = document.querySelectorAll('a');
            // 每次点击都要给所有评论添加函数,好像不太合理
            for (var i = 0; i < as.length; i++) {
                as[i].onclick = function () {
                    // node.removeChild(child);
                    ul.removeChild(this.parentNode);
                }
            }
        }
    }
</script>

效果图

在这里插入图片描述

1.6 复制节点

node.cloneNode(boolean):返回调用该方法的节点的一个副本。

  • 如果括号参数为空或者为 false ,则是浅拷贝,即只克隆复制节点本身,不克隆里面的子节点。
  • 如果括号参数为 true ,则是深度拷贝,会复制节点本身以及里面所有的子节点

代码示例

<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
</ul>

<script>
    var ul = document.querySelector('ul');
    // 1.node.cloneNode() 括号为空或false是浅拷贝,只复制标签不复制子节点
    var liFalse = ul.children[0].cloneNode();
    ul.appendChild(liFalse);
    // 2.node.cloneNode() 括号为true是深拷贝,会复制节点本身和里面所有的子节点
    var liTrue = ul.children[0].cloneNode(true);
    ul.appendChild(liTrue);
</script>

效果图

在这里插入图片描述

1.7 替换节点

parentNode.replaceNode(newChild,oldChild):用指定的节点替换当前节点的一个子节点,并返回被替换掉的节点

1.8 三种创建元素的方式

  1. document.write():如果页面文档流加载完毕,再调用这句话会导致页面重绘

代码示例

<button>点击</button>
<p>123</p>
<div class="inner"></div>
<div class="create"></div>

<script>
    // 1.document.write() 创建元素 如果页面文档流加载完毕,再调用这句话会导致页面重绘
    var btn = document.querySelector('button');
    btn.onclick = function () {
        document.write('<div>123</div>');
    }
    document.write('<div>123</div>');
</script>

点击按钮后页面变为

在这里插入图片描述
2. innerHTML:将内容写入某个 DOM 节点,不会导致页面全部重绘,但是复制节点的时候,不会复制原先节点的事件,存在内存泄露问题
3. document.createAttribute():注意与innerHTML效率上的区别:innerHTML字符串拼接 < createAttribute < innerHTML数组

<button>点击</button>
<p>123</p>
<div class="inner"></div>
<div class="create"></div>

<script>
    // 2.innerHTML 创建元素
    var inner = document.querySelector('.inner');
    // 采取字符换拼接的形式效率低
    inner.innerHTML = '<a href="#">百度</a>';
    // 利用数组能极大提升效率
    var arr = [];
    for (var i = 0; i <= 100; i++) {
        arr.push('<a href="#">百度</a>');
    }
    inner.innerHTML = arr.join('');

    // 3.document.createElement() 创建元素
    var create = document.querySelector('.create');
    var a = document.createElement('a');
    create.appendChild(a);
</script>

1.9 DOM总结

文档对象模型(Document Object Model,简称 DOM),是 W3C 组织推荐的处理可扩展标记语言(HTML或者XML)的标准编程接口。

W3C 已经定义了一系列的 DOM 接口,通过这些 DOM 接口可以改变网页的内容、结构和样式。

  1. 对于JavaScript,为了能够使JavaScript操作HTML,JavaScript就有了一套自己的dom编程接口。
  2. 对于HTML,dom使得html形成一棵dom树. 包含 文档、元素、节点

获取过来的DOM元素是一个对象(object),所以称为文档对象模型

在这里插入图片描述
dom操作主要是针对于元素的操作。主要有创建、添加、删除、修改、获取、属性操作、事件操作。

1. 创建元素

  1. document.write()
  2. innerHTML
  3. createElement(‘标签名’)

2. 添加元素

  1. appendChild(chile)
  2. insertBefore(child,指定元素)

3. 删除元素

  1. parentNode.removeChild(child)

4. 修改元素(属性、内容、元素样式)

主要修改dom的元素属性,dom元素的内容、属性, 表单的值等

  1. 修改元素属性: src、href、title等
  2. 修改普通元素内容: innerHTML 、innerText
  3. 修改表单元素: value、type、disabled等
  4. 修改元素样式: style、className

5. 获取元素

主要获取查询dom的元素

  1. DOM提供的API 方法(古老用法,不推荐): getElementById、getElementsByTagName
  2. H5提供的新方法(提倡):querySelector、querySelectorAll
  3. 利用节点操作获取元素(提倡): 父(parentNode)、子(children)、兄(previousElementSibling、nextElementSibling)

6. 属性操作

  1. setAttribute:设置dom的属性值
  2. getAttribute:得到dom的属性值
  3. element.属性名:设置或获取dom的属性值
  4. removeAttribute:移除属性

7. 事件操作

给元素注册事件:事件源.事件类型 = 事件处理程序

在这里插入图片描述

第二章 事件高级

2.1 注册事件(绑定事件)

1. 注册事件概述

给元素添加事件,称为注册事件或者绑定事件

注册事件有两种方式:传统注册方式和方法监听注册方式

在这里插入图片描述

2. addEventListener 事件监听方式

eventTarget.addEventListener(type, listener[, useCapture]) :将指定的监听器注册到 eventTarget(目标对象)上,当该对象触发指定的事件时,就会执行事件处理函数。这种方法存在兼容性问题,适用于ie9+

该方法有三个参数:

  • type:事件类型字符串,比如 click 、mouseover ,注意这里不要带 on
  • listener:事件处理函数,事件发生时,会调用该监听函数
  • useCapture:可选参数,是一个布尔值,默认是 false。学完 DOM 事件流后,再进一步讲解
<button>传统注册事件</button>
<button>方法监听注册事件</button>
<button>ie9attachEvent</button>

<script>
    var btns = document.querySelectorAll('button');
    // 1.传统注册事件
    btns[0].onclick = function () {
        console.log('hi');
    }
    // 最后一个事件(同一个元素同一个注册事件)会覆盖前面的事件
    btns[0].onclick = function () {
        console.log('hello');
    }

    // 2.方法监听注册事件
    // a.事件类型是字符串,且不带on
    btns[1].addEventListener('click', function () {
        console.log(22);
    });
    // b.同一个元素同一个事件可以添加多个监听器,会依次执行
    btns[1].addEventListener('click', function () {
        console.log(33);
    })
</script>

控制台输出

在这里插入图片描述

3. attachEvent 事件监听方式

eventTarget.attachEvent(eventNameWithOn, callback)

ie9以前的版本进行方法监听注册的方式。作用与addEventListener相同:将指定的监听器注册到 eventTarget(目标对象) 上,当该对象触发指定的事件时,指定的回调函数就会被执行。

该方法接收两个参数:

  • eventNameWithOn:事件类型字符串,比如 onclick 、onmouseover ,注意这里要带 on
  • callback: 事件处理函数,当目标触发事件时回调函数被调用

代码示例

// 3.attachEvent ie9以前的版本支持
btns[2].attachEvent('onclick', function () {
    alert(11);
})
console.log(btns[2].attachEvent); // undefined,因为chrome浏览器不支持这种写法,所以输出是undefined

4. 注册事件兼容性解决方案(了解)

兼容性处理的原则: 首先照顾大多数浏览器,再处理特殊浏览器

<script>
function addEventListener(element, eventName, fn) {
    // 判断当前浏览器是否支持 addEventListener 方法
    if (element.addEventListener) {
        element.addEventListener(eventName, fn); // 第三个参数默认是false
    } else if (element.attachEvent) {
        element.attachEvent('on' + eventName, fn);
    } else {
        // 相当于 element.onclick = fn;
        element['on' + eventName] = fn;
    }
}
</script>

2. 删除事件(解绑事件)

1. 删除事件的方式

  • 传统注册方式:eventTarget.onclick = null;
<style>
    div {
        width: 100px;
        height: 100px;
        background-color: pink;
    }
</style>

<div>1</div>
<div>2</div>
<div>3</div>

<script>
    var divs = document.querySelectorAll('div');

    divs[0].onclick = function () {
        alert(11);
        // 1.传统方式删除事件 对方法监听注册事件不生效
        divs[0].onclick = null;
    }
</script>
  • 方法监听注册方式
    ① eventTarget.removeEventListener(type, listener[, useCapture]);
    ② eventTarget.detachEvent(eventNameWithOn, callback);
// 2.removeEventListener 删除事件
divs[1].addEventListener('click', fn) // 里面的函数不加()调用

function fn() {
    alert(22);
    divs[1].removeEventListener('click', fn);
}

// 3.detachEvent 删除事件
divs[2].attachEvent('onclick', fn1);
function fn1() {
    alert(33);
    div[2].detachEvent('click', fn1);
}

2. 删除事件兼容性解决方案(了解)

function removeEventListener(element, eventName, fn) {
    //  removeEventListener 和 detachEvent 两种方法不同时兼容
    if (element.removeEventListener) {
        element.removeEventListener(eventName, fn); // 第三个参数 默认是false
    } else if (element.detachEvent) {
        element.detachEvent('on' + eventName, fn);
    }
    element['on' + eventName] = null;
}

2.3 DOM事件流

事件流描述的是从页面中接收事件的顺序。

事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程即 DOM 事件流。

DOM 事件流分为3个阶段:

  1. 捕获阶段:网景最早提出,由 DOM 最顶层节点开始,然后逐级向下传播到到最具体的元素接收的过程。
  2. 当前目标阶段
  3. 冒泡阶段:IE 最早提出,事件开始时由最具体的元素接收,然后逐级向上传播到到 DOM 最顶层节点的过程。

在这里插入图片描述

由此可以看出:当我们某个元素触发事件时,也可能对其他节点造成影响

代码示例:捕获阶段

<div class="father">
    <div class="son">son盒子</div>
</div>

<script>
<style>
    .father {
        overflow: hidden;
        width: 300px;
        height: 300px;
        margin: 100px auto;
        text-align: center;
        background-color: pink;
    }

    .son {
        width: 100px;
        height: 100px;
        line-height: 100px;
        margin: 100px;
        background-color: skyblue;
    }
</style>

    // dom事件流 三个阶段:捕获 当前目标 冒泡
    // 1.JS代码只能执行捕获或冒泡其中的一个阶段
    // 2.onclick 和 attachEvent只能得到冒泡阶段
    // 3.捕获阶段 如果addEventListener的第三个参数是true 那么处于捕获阶段 
    // document -> html -> body -> father -> son
    var son = document.querySelector('.son');
    son.addEventListener('click', function () {
        console.log('son');
    }, true);
    var father = document.querySelector('.father');
    father.addEventListener('click', function () {
        console.log('father');
    }, true);
</script>

控制台输出
在这里插入图片描述

点击子盒子:捕获阶段是自上而下传递事件的,father先接收到click,所以先输出father在输出son

冒泡阶段

 	// 4.冒泡阶段 如果addEventListener的第三个参数是false或省略(默认是false) 那么处于冒泡阶段 
 	// son -> father -> body -> html -> document
    var son = document.querySelector('.son');
    son.addEventListener('click', function () {
        alert('son');
    }, false);
    var father = document.querySelector('.father');
    father.addEventListener('click', function () {
        alert('father');
    }, false);
    var document = document.querySelector('.document');
    document.addEventListener('click', function () {
        alert('document');
    }, false);

控制台输出

在这里插入图片描述

点击子盒子:冒泡阶段是自下而上传递事件的,son -> father -> body -> html -> document依次接收click事件。实际开发对冒泡阶段的关注和使用会更多

2.4 事件对象

1. 什么是事件对象

官方解释:事件(event)对象代表事件的状态,比如键盘按键的状态、鼠标的位置、鼠标按钮的状态。

简单理解:事件发生后,跟事件相关的一系列信息数据的集合,包含这个集合的对象就是事件对象event,它有很多属性和方法。

比如:

  1. 谁绑定了这个事件。
  2. 鼠标触发事件的话,会得到鼠标的相关信息,如鼠标位置。
  3. 键盘触发事件的话,会得到键盘的相关信息,如按了哪个键。

2. 事件对象的用法

<style>
    div {
        width: 100px;
        height: 100px;
        background-color: pink;
    }
</style>

<div>123</div>
<script>
    var div = document.querySelector('div');
    div.onclick = function (event) {
        // 兼容性写法,ie6-8需要通过window.event获取
        event = event || window.event;
        console.log(event);
    }

    div.addEventListener('click', function (e) {
        console.log(e);
    })
</script>

总结

  1. event就是一个事件对象,写到监听函数的小括号里面,当形参来看
  2. 事件对象在事件发生后才会存在,它是系统自动创建,不需要传参
  3. 事件对象是事件的一系列相关数据的集合(与事件相关) 比如鼠标点击click里面包含了鼠标点击的相关信息,例如鼠标坐标,如果是键盘事件就包含键盘事件的信息,例如用户按下的键
  4. 事件对象可以自行命名,如e、evt、event等等
  5. 事件对象存在兼容性问题,ie6-8需要通过window.event获取。兼容性写法 event = event || window.event;

2.5 事件对象的常见属性和方法

在这里插入图片描述

1. 常用属性 target

作用:返回触发事件的的对象

<ul>
    <li>abc</li>
    <li>abc</li>
    <li>abc</li>
</ul>

<script>
    // 1.e.target 返回触发事件的对象 this 返回的是绑定事件的对象(元素)
    var ul = document.querySelector('ul');
    ul.addEventListener('click', function (e) {
        // ul绑定事件,this指向ul
        console.log(this);
        // 与this非常相似的属性(作用相同),但存在兼容性问题 ie6-8不可用
        console.log(e.currentTarget);
        // e.target返回的是被点击的对象,谁触发这个事件 被点击的是li,所以返回li
        console.log(e.target);
    })
</script>

在这里插入图片描述

控制台输出(点击abc后):

在这里插入图片描述
前两个分别输出的是this和currentTarget,是调用绑定事件的对象ul;第三个输出的是target,是触发事件的对象li

target的兼容性写法

div.onclick = function (e) {
    e = e || window.event;
    // srcElement是非标准写法,是为了兼容ie678
    var target = e.target || e.srcElement;
    console.log(target);
}

2. 阻止事件冒泡

事件冒泡:事件开始时由最具体的元素接收,然后逐级向上传播到到 DOM 最顶层节点( document )。事件冒泡本身的特性,会带来的坏处,也会带来好处,需要灵活掌握。

  • 标准写法:利用事件对象里面的 stopPropagation()方法(适用于IE 9+)
<style>
    .father {
        overflow: hidden;
        width: 300px;
        height: 300px;
        margin: 100px auto;
        text-align: center;
        background-color: pink;
    }

    .son {
        width: 100px;
        height: 100px;
        line-height: 100px;
        margin: 100px;
        background-color: skyblue;
    }
</style>

<div class="father">
    <div class="son">son盒子</div>
</div>

<script>
<script>
    // 阻止事件冒泡 dom标准写法 stopPropagation()
    var son = document.querySelector('.son');
    son.addEventListener('click', function (e) {
        console.log('son');
        e.stopPropagation();
        e.cancelable = true; // 非标准写法,ie678

        // 兼容性写法,了解即可。实际开发通常不要求
        /* if (e && e.stopPropagation()) {
            e.stopPropagation();
        }else {
            window.event.cancelBubble = true;
        } */
    }, false);
    var father = document.querySelector('.father');
    father.addEventListener('click', function () {
        console.log('father');
    }, false);
    document.addEventListener('click', function () {
        console.log('document');
    }, false);
</script>

点击son盒子后,控制台输出

在这里插入图片描述

  • 非标准写法: 利用事件对象的 cancelBubble 属性(适用于IE 6-8)
e.cancelBubble = true;
  • 兼容性写法(了解)
if(e && e.stopPropagation){
 e.stopPropagation();
} else{
 window.event.cancelBubble = true;
}

3. 阻止事件默认行为

<div>123</div>
<a href="http://www.baidu.com">百度</a>
<form action="http://www.baidu.com">
    <input type="submit" value="提交" name="sub">
</form>

<script>
    // 1.返回事件类型,
    var div = document.querySelector('div');
    div.addEventListener('click', fn);
    div.addEventListener('mouseover', fn);
    div.addEventListener('mouseout', fn);

    function fn(e) {
        console.log(e.type);
    }

    // 2.阻止默认行为(事件) 让链接不跳转或按钮不提交
    var a = document.querySelector('a');
    a.addEventListener('click', function (e) {
        e.preventDefault(); // dom标准写法
    });

    // 3.传统注册事件
    a.onclick = function (e) {
        // 普通浏览器
        e.preventDefault();
        // ie678
        e.returnValue;
        // return 没有兼容性问题,也能阻止默认行为,但是return后面的代码不执行了。且只在传统注册方式生效
        return false;
        alert(11);
    }
</script>

发现点击链接不跳转,点击div控制台有输出

在这里插入图片描述

2.6 事件委托

事件委托也称为事件代理, 在 jQuery 里面称为事件委派。

原理:把事件监听器设置在其父节点上,然后利用冒泡原理影响设置每个子节点。

优点:减少操作 DOM 的次数,能够提高程序的性能。

案例:点击每个 li 之后背景颜色都会改变且在控制台输出‘知否知否,点我应是弹框在手’

<ul>
    <li>知否知否,点我应是弹框在手</li>
    <li>知否知否,点我应是弹框在手</li>
    <li>知否知否,点我应是弹框在手</li>
    <li>知否知否,点我应是弹框在手</li>
    <li>知否知否,点我应是弹框在手</li>
</ul>
<script>
    // 事件委托的核心原理:给父节点添加侦听器,利用事件冒泡影响每一个子节点
    var ul = document.querySelector('ul');
    var lis = document.querySelectorAll('li');
    ul.addEventListener('click', function (e) {
        // 子节点被点击后背景颜色改变
        for (var i = 0; i < lis.length; i++) {
            lis[i].style.background = 'none';
        }
        e.target.style.background = 'pink';
        console.log('知否知否,点我应是弹框在手');
    })
</script>

控制台输出

在这里插入图片描述

2.7 常用的鼠标事件

1. 常用鼠标事件

在这里插入图片描述

当我们不希望页面的内容被用户直接获取时,可以使用以下代码

<p>我是不希望被分享的文字</p>

<script>
    // 1.禁用右键菜单 contextmenu
    document.addEventListener('contextmenu', function (e) {
        e.preventDefault();
    })

    // 2.禁止鼠标选中 selectstart
    document.addEventListener('selectstart', function (e) {
        e.preventDefault();
    })
</script>

2. 鼠标事件对象

在这里插入图片描述

代码验证

<style>
    body {
        height: 3000px;
    }
</style>

<script>
    // 鼠标对象mouseEvent
    document.addEventListener('click', function (e) {
        // 1.client返回鼠标在可视区的坐标
        console.log(e.clientX);
        console.log(e.clientY);
        console.log('--------------------');

        // 2.page返回鼠标在页面文档的坐标,支持ie9+。最常用
        console.log(e.pageX);
        console.log(e.pageY);
        console.log('--------------------');
        // 3.screen返回鼠标在电脑屏幕的坐标
        console.log(e.screenX);
        console.log(e.screenY);
        console.log('--------------------');
    })
</script>

控制台输出

在这里插入图片描述

案例:跟随鼠标的天使(图片会跟随鼠标移动)

在这里插入图片描述

<style>
    img {
        position: absolute;
    }
</style>

<img src="images/angel.gif" alt="">

<script>
    var pic = document.querySelector('img')
    // 鼠标移动1px时,函数就会执行
    document.addEventListener('mousemove', function (e) {
        var x = e.pageX;
        var y = e.pageY;
        // 数字做居中对齐的效果,是鼠标点在图片中央
        pic.style.top = y - 40 + 'px';
        pic.style.left = x - 48 + 'px';
    })
</script>

2.8 常用的键盘事件

1. 常用的键盘事件

在这里插入图片描述

注意:

  • onkeypress和其余两个的区别是:不识别功能键,如左右箭头、shift等
  • 如果键盘按下不松开,onkeydown和onkeypress会一直触发
    在这里插入图片描述

2. 返回键的ASCII值 keyCode

ASCII表(了解即可)

在这里插入图片描述
keyCode返回键的ASCII值

注意: onkeydown 和 onkeyup 不区分字母大小写,onkeypress 区分字母大小写。

<script>
    // 键盘事件对象的keyCode属性可以得到按键的ASCII值
    // 1.keyup和keydown不区分大小写 a和A得到的都是65
    // 2.keypress区分大小写
    document.addEventListener('keyup', function (e) {
        console.log('up:' + e.keyCode);
        var a = e.keyCode
        /* if (a === 65) {
            console.log('你按下了a键');
        } else {
            console.log('你没有按下a键');
        } */
    })

    document.addEventListener('keypress', function (e) {
        // 可以利用keyCode判断用户按下哪个键
        console.log('press:' + e.keyCode);
    })
</script>

控制台输出

在这里插入图片描述
案例:仿京东快递查询
在这里插入图片描述
代码

<style>
    * {
        margin: 0;
        padding: 0;
    }

    .search {
        position: relative;
        width: 178px;
        margin: 100px;
    }

    .con {
        display: none;
        position: absolute;
        top: -40px;
        width: 171px;
        border: 1px solid rgba(0, 0, 0, .2);
        box-shadow: 0 2px 4px rgba(0, 0, 0, .2);
        padding: 5px 0;
        font-size: 18px;
        line-height: 20px;
        color: #333;
    }

    .con::before {
        content: '';
        width: 0;
        height: 0;
        position: absolute;
        top: 28px;
        left: 18px;
        border: 8px solid #000;
        border-style: solid dashed dashed;
        border-color: #fff transparent transparent;
    }
</style>

<div class="search">
    <div class="con">123</div>
    <input type="text" placeholder="请输入您的快递单号" class="jd">
</div>
<script>
    var con = document.querySelector('.con');
    var jd_input = document.querySelector('.jd');
	
    jd_input.addEventListener('keyup', function (e) {
        if (this.value == '') {
            con.style.display = 'none';
        } else {
            con.style.display = 'block';
            con.innerHTML = this.value;
        }
    })

    // 失去焦点时隐藏盒子
    jd_input.addEventListener('blur', function () {
        con.style.display = 'none';
    });
    // 获得焦点且内容不为空时显示盒子
    jd_input.addEventListener('focus', function () {
        if (this.value != '') {
            con.style.display = 'block';
        }
    });
</script>
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

君和-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值