【前端】前端三要素之DOM

写在前面:本文仅包含DOM内容,JavaScript传送门在这里,BOM传送门在这里

本文内容是假期中刷的黑马Pink老师视频(十分感谢Pink老师),原文保存在个人的GitLab中,如果需要写的网页内容信息等可以评论联系我,若是编辑博文中出现了忘记上传的图片或者错位的图片欢迎评论区指正。写作不易,欢迎点赞、收藏+关注。

文章目录

DOM

DOM 专有名词

DOM树
  1. 文档:一个页面就是一个文档,DOM中使用Document表示
  2. 元素:页面中所有的标签都是元素,DOM中使用element表示
  3. 节点:网页中所有内容都是节点(标签、属性、文本、注释等),DOM中使用node表示

DOM 把以上内容都看做事对象。

DOM 获取元素

  1. 通过ID获取 | getElementById()
  2. 通过标签名获取
  3. 通过HTML5新增方法获取
  4. 特殊元素获取
  1. 通过ID获取 | getElementById()

返回的是一个

html 复制代码
<body>
  <div id="time">
    2019-9-9
  </div>
  <script>
    var time = document.getElementById('time');
    console.log(time);
    console.log(typeof time); // 返回的是一个元素对象
    console.log(time.id);
    console.dir(time); // div#time
  </script>
</body>
  1. 根据标签名获取 | getElementsByTagName() | 返回的是对象集合
html 复制代码
<body>
    <ul>
        <li>知否知否,应是绿肥红瘦。</li>
        <li>知否知否,应是绿肥红瘦。</li>
        <li>知否知否,应是绿肥红瘦。</li>
        <li>知否知否,应是绿肥红瘦。</li>
        <li>知否知否,应是绿肥红瘦。</li>
    </ul>
    <script>
        var eles = document.getElementsByTagName('li');
        console.log(eles); // HTMLCollections(5) [li,li,li,li,li]
        
        // 逐个输出一下
        for( var i = 0; i < eles.length ; i ++) {
            console.log(eles[i]);
        }
    </script>
</body>

得到元素对象是动态的,如果元素的内容发生了变化,JS获取到的内容跟着一起变化

注意事项

  1. 如果页面中只有一个元素,返回的也是一个伪数组
  2. 如果页面中没有元素,返回的是一个空的伪数组

通过父元素获取其全部子元素

html 复制代码
<body>
    <ul>
        <li>知否知否,应是绿肥红瘦。</li>
        <li>知否知否,应是绿肥红瘦。</li>
        <li>知否知否,应是绿肥红瘦。</li>
        <li>知否知否,应是绿肥红瘦。</li>
        <li>知否知否,应是绿肥红瘦。</li>
    </ul>
    <ol>
        <li>哈哈哈</li>
        <li>哈哈哈</li>
        <li>哈哈哈</li>
        <li>哈哈哈</li>
    </ol>
    <script>
        var ol = document.getElementsByTagName('ol');
        var lis = ol[0].getElementsByTagName('li');
        console.log(lis);
    </script>
</body>
  1. HTML5新增的方法 | 通过类名获取 | getElementsByClassName()
html 复制代码
<body>
  <div class="box">123</div>
  <div class="box">123</div>
  <div class="box">123</div>
  <script>
    var lis = document.getElementsByClassName('box'); // 长度为3的伪数组
  </script>
</body>
  1. querySelectir()
  1. querySelector():获取指定选择器的第一个元素
  2. querySelectorAll():获取页面上所有元素
html 复制代码
<body>
    <div class="box">hahaha</div>
    <div class="box">hahaha</div>
    <div class="box">hahaha</div>
    <script>
        var firstBox1 = document.querySelector('.box'); // 通过.告诉选择器这是一个class | <div class="box">hahaha</div>
        var firstBox2 = document.querySelector('#box'); // 通过#告诉选择器这是一个id | null
        console.log(firstBox1);
        console.log(firstBox2);
    </script>
</body>
  1. querySelectirAll() | 获取所有元素
html 复制代码
<body>
    <div class="box">hahaha</div>
    <div class="box">hahaha</div>
    <div class="box">hahaha</div>
    <script>
        var firstBox1 = document.querySelectorAll('.box'); // 通过.告诉选择器这是一个class | <div class="box">hahaha</div>
        var firstBox2 = document.querySelectorAll('#box'); // 通过#告诉选择器这是一个id | null
        console.log(firstBox1); // 长度为3的伪数组
        console.log(firstBox2); // 空的伪数组
    </script>
</body>
  1. 获取特殊标签 | body、htlm

一般来说只有一个body标签或者一个htlm标签

  1. 获取 BODY
html 复制代码
<body>
    <script>
        var bodyEle = document.body;
        console.log(bodyEle); // 输出Body对象
    </script>
</body>
  1. 获取 HTML
html 复制代码
<body>
    <script>
        var htmlEle = document.documentElement;
        console.log(htmlEle); // HTML 对象
    </script>
</body>

DOM | 事件

JavaScript 使我们有能力创建动态页面,而时间是可以被JavaScript侦测到的行为

简单理解:出发---响应机制

网页中的每个元素都可以产生某些可以出发JavaScript的事件,例如,我们可以在用户点击某个按钮的时候产生一个事件,然后去执行某些操作。
事件是由三部分组成:事件源、事件类型、事件处理程序(一般称为事件三要素)

  1. 事件源:时间的触发的对象,是由谁来触发,比如按钮
  2. 事件类型:如何出发,比如鼠标点击出发、鼠标经过出发、键盘按下触发
  3. 事件处理程序:通过一个函数赋值的方式完成

点击按钮事件示例

html 复制代码
<body>
    <button id="btn">点击一下</button>
    <script>
        var btn = document.getElementById('btn');
        btn.onclick = function () {
            alert('点击了一下~');
        }
    </script>
</body>

执行事件过程

  1. 获取事件源
  2. 注册时间(绑定事件)
  3. 添加事件处理程序(采用函数赋值形式)

鼠标到Div上跳出弹框示例

html 复制代码
<body>
    <div style="background: antiquewhite;text-align: center;width: 200px;border-radius: 5px;box-shadow: 0 0 3px gray">123</div>
    <script>
        var div = document.querySelector('div');
        div.onmouseover = function (){
            alert('Hello World~');
        }
    </script>
</body>
鼠标事件 描述
onclick 鼠标点击左键触发
onmouseover 鼠标经过触发
onmouseenter 鼠标进入触发
onmouseout 鼠标离开触发
onfocus 获取鼠标焦点触发
onblur 失去鼠标焦点触发
onmousemove 鼠标移动触发
onmousedown 鼠标按下触发
onmouseup 鼠标弹起触发
contextmenu 右键的时候触发
selectstart 选择文字的时候触发

注意:mouseover存在冒泡事件,经过自身盒子会触发事件,子盒子也会触发。mouseenter不存在冒泡事件,只有经过自身时才会触发。

操作元素

修改元素内容

  1. element.innerText | 起始位置稻种植未知的内容,但它去除Html标签,同时空格和空行也会去掉
  2. element.innerHTML | 起始位置到终止位置的全部内容,包括html,同时保留空格和换行

示例 | 鼠标移动进去显示中文,离开显示英文

html 复制代码
<body>
    <button>显示中文</button>
    <div style="background: antiquewhite;border: .1px solid gray">Hello World</div>
    <script>
        //1. 获取元素
        var btn = document.querySelector('button');
        var div = document.querySelector('div');
        btn.onmouseover = function () {
            div.innerText = '你好,世界!'
        }
        btn.onmouseleave = function () {
            div.innerText = 'Hello World'
        }
    </script>
</body>

鼠标移动上去显示中文,鼠标移出之后显示英文。

区别 | innerText 与 innerHTML 的区别

innetText:它会原封不动的将我们给它的字符串显示到页面上(无法识别HTML标签)

innerHTML:如果发现是html标签,它会解析html标签并显示

尽量使用innerHTML,这是W3C发布的标准

html 复制代码
<body>
    <button>点击加粗名字</button>
    <div>我是Jim.kk</div>
    <script>
        var btn = document.querySelector('button');
        var div = document.querySelector('div');
        btn.onclick = function () {
            div.innerHTML = '<b>我是Jim.kk</b>';
        }
    </script>
</body>

如果是使用innerText的方式的话,则会原封不动的显示双引号的内容,下面看示例:

html 复制代码
<body>
<button>点击加粗名字</button>
<div>我是Jim.kk</div>
<script>
    var btn = document.querySelector('button');
    var div = document.querySelector('div');
    btn.onclick = function () {
        div.innerText = '<b>我是Jim.kk</b>';
    }
</script>
</body>

通过innetText 和 innerHTML 获取文字内容

两者的区别在于:innerText只会获取文字

innerText
html 复制代码
<body>
    <p>
        一段文字:
        <span>我是Jim.kk</span>
    </p>
    <script>
        var p = document.querySelector('p');
        console.log(p.innerText); // 一段文字: 我是Jim.kk
    </script>
</body>
innerHTML
html 复制代码
<body>
    <p>
        一段文字:
        <span>我是Jim.kk</span>
    </p>
    <script>
        var p = document.querySelector('p');
        console.log(p.innerHTML); // 一段文字:(换行)<span>我是Jim.kk</span>
    </script>
</body>

修改元素属性

  1. src、href
  2. id、alt、title
html 复制代码
<body>
  <button id="btn1">图片1</button>
  <button id="btn2">图片2</button>
  <div style="margin-top: 20px">
    <img src="img/img1.png" alt="" style="width: 500px" title="IMG1">
  </div>
  <script>
    // 修改图片属性
    var btn1 = document.getElementById('btn1');
    var btn2 = document.getElementById('btn2');
    var img  = document.querySelector('img');
    btn1.onclick = function () {
      img.src = 'img/img1.png';
      img.title = 'IMG1';
    }

    btn2.onclick = function () {
      img.src = 'img/img2.png';
      img.title = 'IMG2';
    }
  </script>
</body>

以上代码点击按钮2显示一张图片,点击按钮1又会变回来

表单属性操作

修改表单文字

type、value、checked、selected、disabled

html 复制代码
<body>
  <input type="text" value="请输入内容">
  <button id="btn1">按钮</button>
  <button id="btn2">禁用</button>
  <script>
    // 1. 获取元素
    var btn1 = document.getElementById('btn1');
    var btn2 = document.getElementById('btn2');
    var input = document.querySelector('input');
    btn1.onclick = function () {
      input.value = '123';
    }
    btn2.onclick = function () {
      input.disabled = true;
      this.disabled = true;  // this指向的是btn,谁调用函数就指向谁,这里是btn2
    }
  </script>
</body>
示例 | 输入密码

点击小眼睛显示密码,同时小眼睛睁开

再次点击隐藏密码,同时小眼睛关闭

html 复制代码
<body>
  <div class="box">
    <label for="">
      <img src="img/icons/close.png" alt="" id="eye">
    </label>
    <input type="password" name="" id="pwd">
  </div>
  <script>
    var btn = document.getElementById('eye');
    var pwd = document.getElementById('pwd');
    var flag = 0;
    btn.onclick = function () {
      if(flag === 0) {
        this.src = 'img/icons/open.png'
        pwd.type='text';
        flag = 1;
      } else {
        this.src = 'img/icons/close.png'
        pwd.type='password';
        flag = 0;
      }
    }
  </script>
</body>

表单样式修改

可以用以下两种方式修改:

  1. element.style='background: red';
  2. element.style.background='red';

区别在于,第一种方式可以在引号内写入很多样式,第二种需要一个样式定义一行

示例 | 修改背景颜色

设置一个DIV,鼠标移入之后改变背景颜色,示例如下:

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>样式操作1</title>
    <style>
        div {
            width: 200px;
            height: 200px;
            background: antiquewhite;
        }
    </style>
</head>
<body>
<div>

</div>
<script>
    var div = document.querySelector('div');
    div.onmouseover = function () {
        div.style = 'background: red';
    }
    div.onmouseleave = function () {
        div.style = 'background: antiquewhite';
    }
</script>
</body>
</html>

或者像下面这么写:

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>样式操作2</title>
    <style>
      div {
        width: 200px;
        height: 200px;
        background: antiquewhite;
      }
    </style>
</head>
<body>
  <div></div>
  <script>
    var div = document.querySelector('div');
    div.onmouseover = function () {
      div.style.background = 'red';
    }
    div.onmouseleave = function () {
      div.style.background = 'antiquewhite';
    }
  </script>
</body>
</html>
示例 | 淘宝精灵图
html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>淘宝精灵图案例</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }
        li {
            list-style-type: none;
        }
        .box {;
            width: 250px;
            margin: 100px auto;
        }
        .box li {
            float: left;
            width: 24px;
            height: 24px;
            background: pink;
            margin: 15px;
            background: url(img/icons/sprite.png) no-repeat;
        }
    </style>
</head>
<body>
    <div class="box">
        <ul>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
        </ul>
    </div>
    <script>
        // 1. 获取所有li
        var lis = document.querySelectorAll('li');
        for (var i = 0; i < lis.length ; i++){
            // 让索引号*44获取每个li的背景y坐标
            var index = i * 44;
            lis[i].style.backgroundPosition = '0 -' + index + 'px'
        }
    </script>
</body>
</html> 
示例 | 得到焦点与失去焦点

以下示例有一个磨人的输入框,若是输入框的默认值是'手机',则在得到焦点的时候清空输入框,若是失去焦点的时候输入框内容是空的,则显示手机。

html 复制代码
<body>
    <input type="text" value="手机"></input>
    <script>
        var input = document.querySelector('input');
        input.onfocus = function () {
            // console.log('得到焦点');
            if ( input.value === '手机' ) {
                this.value = '';
            }
        }
        input.onblur = function () {
            if ( input.value === '' ) {
                input.value = '手机';
            }
        }
    </script>
</body>

使用className修改样式

上述方法是一条一条的style属性,除了以上方法外,我们还可以同时定义多个div,然后通过element.className='divName'的方式修改它的样式
如果想要即保留原来的类名,有来个新的类名,只需要element.className='oldDivName newDivName'

下面源码是个很好的示例:

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>className</title>
    <style>
        .div1{
            height: 50px;
            background: antiquewhite;
            box-shadow: 0 0 5px gray;
            border: 3px solid gray;
            border-radius: 5px;
        }
        .div2{
            height: 100px;
            background: red;
            box-shadow: 0 0 20px gray inset;
            border: .1px solid gray;
            border-radius: 50px;
        }
    </style>
</head>
<body>
    <div class="div1"></div>
    <script>
        var div = document.querySelector('div');
        div.onmouseenter = function () {
            div.className = 'div2';
        }
        div.onmouseleave = function () {
            div.className = 'div1';
        }
    </script>
</body>
</html>

自定义属性值

  1. 通过element.getAttribute('')可以获取属性
  2. 这种方式可以获取自定义属性
  3. 通过element.setAttribute('属性名','属性值'')可以为元素自定义属性
  1. 通过getAttribute获取自定义属性值

div中本来是不存在index这个内置属性的,但是我们设置之后可以通过getAttribute来获取:

html 复制代码
<body>
    <div index="1" id="div1"></div>
    <script>
        var div = document.querySelector('div');
        console.log(div.getAttribute('id')); // div1
        console.log(div.getAttribute('index')); // 1
    </script>
</body>
  1. 通过setAttribute设置自定义属性

除了直接将自定义属性写在标签里面以外,还可以通过setAttribute的方式设置自定义属性

html 复制代码
<body>
    <div id="div1"></div>
    <script>
        var div = document.querySelector('div');
        div.setAttribute('xx','123');
        console.log(div.getAttribute('xx')); // 123
        
        // 修改内置属性值
        div.setAttribute('id','divx');
        console.log(div.getAttribute('id')); // divx
    </script>
</body>
  1. H5 新标准

H5新标准中,规定所有的自定义属性都得以data-开头,所以又新增了dataset方法让我们获取自定义属性

这种方法只能获取data-开头的自定义属性

html 复制代码
<div data-index="1" data-list-name="Jim.kk"></div>
<script>
    var div = document.querySelector('div');
    console.log(div.dataset.index); // 1
    console.log(div.dataset['index']) // 1
    console.log(div.dataset.listName); // Jim.kk
    console.log(div.dataset['listName']) // Jim.kk
</script>

节点操作

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

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

节点一般由:nodeType(节点类型)、nodeName(节点名称)和nodeValue(节点值)这三个基本属性。

以上截图对应以下代码(注意,由于没有规定编码,在浏览器中打开可能存在乱码):

html 复制代码
<html lang="en">
    <head>
        <title>节点操作</title>
    </head>
    <body>
        <a href="www.baidu.com">我的链接</a>
        <h1>我的标题</h1>
    </body>
</html>

利用DOM树可以把节点划分为不同的层级关系,常见的是父子兄层级关系。

node.parentNode | 获取父节点

html 复制代码
<body>
    <div>
        <span>新年快乐!龙年行大运!!!</span>
    </div>
    <script>
        var span = document.querySelector('span');
        var div = span.parentNode;
        console.log(div);
    </script>
</body>

node.childNodes | 获取子节点

childNode默认会获得所有的子节点,包括文字和标签,所以以下代码输出的node长度为9(有五个换行)

如果不想要换行(毕竟也没什么用),可以使用children来代替childNode

children并不是官方的,但是获得了各浏览器的支持,所以放心使用。

html 复制代码
<body>
    <ul>
        <li>新年快乐!龙年行大运!!!</li>
        <li>新年快乐!龙年行大运!!!</li>
        <li>新年快乐!龙年行大运!!!</li>
        <li>新年快乐!龙年行大运!!!</li>
    </ul>
    <ul>
        <li>我是Jim.kk!!!</li>
        <li>我是Jim.kk!!!</li>
        <li>我是Jim.kk!!!</li>
        <li>我是Jim.kk!!!</li>
    </ul>
    <script>
        var ul = document.querySelector('ul');
        console.log(ul.childNodes); // NodeList(9) [text, li, text, li, text, li, text, li, text]
        console.log(ul.children); // HTMLCollection(4)
    </script>
</body>

获取特定子节点

可以使用firstChild来获取第一个节点,但是这种获取方式仍然会获取换行等文本节点

方法 说明 备注
element.firstChild 获取第一个元素,但是会包含文本信息,也就是会获取你的换行等信息
element.lastChild 获取最后一个元素,但是会包含文本信息,也就是会获取你的换行等信息
element.firstElementChild 获取第一个元素,不包含文本信息,也就是只获取标签元素 IE9以上才支持
element.lastElementChild 获取最后一个元素,不包含文本信息,也就是只获取标签元素 IE9以上才支持
element.children[0] 用下标的方式获取某个指定的元素 实际开发中的用法

获取兄弟节点

获取下一个兄弟节点
方法 说明 备注 注意
nextSibling 获取下一个兄弟节点 也会获取text节点
nextElementSibling 获取下一个兄弟节点 只会获取标签节点 IE9以上才支持
html 复制代码
<body>
    <div>新年快乐!龙年行大运</div>
    <span>我是Jim.kk</span>
    <script>
        // 下一个兄弟节点存在
        var div = document.querySelector('div');
        console.log(div.nextSibling) // #text
        console.log(div.nextElementSibling) // span
    
        // 下一个兄弟节点是Script
        var span = document.querySelector('span');
        console.log(span.nextSibling) // #text
        console.log(span.nextElementSibling) // <script>
    
        // 下一个兄弟节点不存在
        var script = document.querySelector('script');
        console.log(script.nextSibling) // <link type="text/css" rel="stylesheet" id="dark-mode-custom-link">
        console.log(script.nextElementSibling) // <link type="text/css" rel="stylesheet" id="dark-mode-custom-link">
    </script>
</body>
获取上一个兄弟节点
方法 说明 备注 注意
previousSibling 获取上一个兄弟节点 也会获取text节点
previousElementSibling 获取上一个兄弟节点 只会获取标签节点,如果不存在则会返回null IE9以上才支持
html 复制代码
<body>
  <div>新年快乐!龙年行大运</div>
  <span>我是Jim.kk</span>
  <script>

    var span = document.querySelector('span');
    console.log(span.previousSibling); // #text
    console.log(span.previousElementSibling); // <div>新年快乐!龙年行大运</div>

    // 当前节点是第一个节点,会获取一个空
    var div = document.querySelector('div');
    console.log(div.previousSibling) // #text
    console.log(div.previousElementSibling) // null
  </script>
</body>

创建节点

  1. 创建一个节点 | document.createElement();
  2. 添加到某个地方去
    2.1 fatherElement.appendChild(childElement); | 在父节点内部追加节点
    2.2 fatherElement.insertBefore(childElement,fatherElement.children[0]); | 在父节点内部的某个子节点前面插入这个元素
html 复制代码
<body>
    <ul></ul>
    <script>
        var li1 = document.createElement('li');
        var ul = document.querySelector('ul');
        ul.appendChild(li1); // 在ul的子节点中追加这个元素
    
        var li2 = document.createElement('li');
        ul.insertBefore(li2,ul.children[0]); // 在ul的0号元素前面插入一个li2
    </script>
</body>

删除节点

  1. node.removeChild() | 删除父亲中的某个孩子
html 复制代码
<body>
    <ul>
        <li>兔年</li> <!-- 打错了,删除掉 -->
        <li>龙年</li>
        <li>行大运</li>
    </ul>
    <script>
        // 1. 获取元素
        var ul = document.querySelector('ul');
        // 2. 删除元素
        ul.removeChild(ul.children[0]);
    </script>
</body>

拷贝节点

我们可以使用node.cloneNode()来拷贝节点,但是如果括号中为空或者是false,则只会克隆节点本身,而不会克隆里面的子节点,如果想要克隆里面的子节点,我们在括号内写入true即可。

html 复制代码
<body>
    <ul>
        <li>1</li>
        <li>2</li>
        <li>3</li>
    </ul>
    <script>
        var ul = document.querySelector('ul');
        // 1. node.cloneNode();
        var li1 = ul.children[0].cloneNode();
        ul.appendChild(li1); // 节点为空 | 因为上面的括号参数为空或者false,是浅拷贝,则只克隆节点本身,不克隆里面的子节点
        var li2 = ul.children[0].cloneNode(true);
        ul.appendChild(li2); // 节点为空 | 因为上面的括号参数为空或者false,是浅拷贝,则只克隆节点本身,不克隆里面的子节点
    </script>
</body>

document.write() | 不推荐

document.write是直接将内容写入页面的内容流,但是文档流执行完毕,会导致页面全部重绘。

简单解释下:若是我们在页面加载的时候就直接在script中写入document.write,那么里面的元素会追加在页面的最下面,但是若是通过页面中一个按钮点击后再加载的话,那么整个页面会只剩下这一个元素。

示例如下:

  1. 直接加载元素
html 复制代码
<body>
    <p>abc</p>
    <script>
        var btn = document.querySelector('button');
        document.write('<div>我是Jim.kk</div>');
    </script>
</body>

可以看到,由于页面加载的时候就已经执行了这个方法,所以该方法中的内容是被追加到页面最下面的。

html 复制代码
<body>
    <button>点击</button>
    <p>abc</p>
    <script>
        var btn = document.querySelector('button');
        btn.onclick = function () {
            document.write('<div>我是Jim.kk</div>')
        }
    </script>
</body>

由于页面已经渲染完毕,这时候我们点击按钮写入这个元素,页面中的内容直接被覆盖了,只留下了我们写入的元素(建议自己试验一下试试效果)。

监听事件

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

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

方式比较:

传统方式
  1. 传统方式利用on开头的事件,比如onclickonmouseenter
  2. 特点:注册事件的唯一性
  3. 同一个元素同一个事件只能设置一个处理函数,最后注册的处理函数会覆盖前面注册的处理函数。

下面代码中,由于我们给btn先后注册了两个点击事件,所以后面的点击事件会覆盖掉前面的点击事件,点击之后会跳出弹框'Hello~'

html 复制代码
<body>
    <button>点击</button>
    <script>
        var btn = document.querySelector('button');
        btn.onclick = function () {
            alert('Hi~')
        }
        btn.onclick = function () {
            alert('Hello~')
        }
    </script>
</body>
监听事件
  1. W3C 标准 推荐方式
  2. addEventListener() 是一个方法
  3. IE9之前的IE浏览器不支持此方法,可以使用attachEvent()代替
  4. 同一个元素,同一个事件可以添加多个

添加监听事件

书写方式: eventTarget.addEventListener(type,listener[,useCapture])
eventTarget.addEventListener() 方法将制定的监听器注册到eventTarget上(目标对象),当该对象出发指定的事件时,就会执行事件处理函数。

  1. type:事件类型字符串,比如clickmouseover,注意这里不带on
  2. listener:事件处理函数,事件发生时,会调用该监听函数
  3. useCapture:可选参数,是一个布尔值,默认是false。

添加监听事件的两个方法:

方法 说明 备注
addEventListener IE9以后才支持 推荐
attachEvent IE9以前才支持
html 复制代码
<body>
    <button>监听事件</button>
    <script>
        // 2. 监听事件
        var btn = document.querySelector('button');
        btn.addEventListener('click',function () {
            alert('Hello World');
        })
        btn.addEventListener('click',function () {
            alert('我是Jim.kk');
        })
    </script>
</body>

以上代码点击按钮之后,会连续跳出两次弹框,第一个弹框显示Hello World,当你关闭该弹框之后,会再次跳出一个弹框,显示我是Jim.kk

解绑事件

  1. 传统方式中,我们可以使用element.onclick = null的方式解绑事件
  2. 监听事件中,我们要移除事件,就不能再用匿名函数了,而是要给函数一个名字,然后removeEventListener这个事件
html 复制代码
<body>
    <!-- 传统方式 -->
    <button id="btn1">传统事件</button>
    <script>
        var btn = document.querySelector('#btn1');
        btn.onclick = function () {
            alert('我是Jim.kk');
            btn.onclick = null; // 解绑事件,再次点击就没用了
        }
    </script>
    <!-- 监听方式 -->
    <button id="btn2">监听方式</button>
    <script>
        var btn = document.querySelector('#btn2');
        function fun() {
            alert('Jim.kk祝大家新年快乐!!');
            btn.removeEventListener('click',fun)
        }
        btn.addEventListener('click',fun);
    </script>
</body>

在IE9之前的浏览器中,我们只能使用attachEvent添加监听函数,在这种情况下我们要使用detachEvent的方式来移除事件,用法与removeEventListener无异,这里不做演示。

事件流

事件执行流程

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

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

比如我们给DIV注册了点击事件

DOM事件流分为三个阶段:

  1. 补货阶段
  2. 当前目标节点
  3. 冒泡阶段
捕获阶段
  1. 虽然我是给Div添加的点击事件,但是点击事件的接收者是Document
  2. Document并没有绑定这个点击事件,接下来向下找,找到html
  3. html也没有绑定,继续向下找找到body
  4. body也没有绑定,接下来找到div,这时候发现div绑定了这个事件
目标节点
  1. 找到div之后,进入到当前目标阶段,开始执行事件
冒泡阶段
  1. div执行事件结束,将事件向上传播,依次给body-html-Document,这里就是冒泡阶段

事件冒泡:IE最早提出,事件开始时由最具体的元素接收,然后主机箱上传播到DOM最顶层节点的过程

事件捕获:网景公司最早提出,有DOM最顶层节点开始,然后逐级向下传播到最具体的元素接收过程

验证

如果addEventListener第三个参数是true,那么则处于捕获阶段,执行顺序是由外向内,也就是document-html-body-target

如果addEventListener第三个参数是false或者空,那么则处于冒泡阶段,执行顺序是由内到外,也就是target-body-html-document
以下代码点击之后先弹出father框,关闭后再弹出son

html 复制代码
<head>
    <meta charset="UTF-8">
    <title>事件流</title>
    <style>
        .father {
            width: 400px;
            height: 400px;
            margin: 200px auto;
            padding: 50px;
            background: antiquewhite;
        }
        .son {
            background: gray;
            width: 100%;
            height: 100%;
        }
    </style>
</head>
<body>
    <div class="father">
        <div class="son">son盒子</div>
    </div>
    <script>
        // dom 事件流三阶段:
        // 1. JS 代码中只能执行捕获或者冒泡其中一个阶段
        // 2. onclick 和 attachEvent(ie)只能得到冒泡阶段
        // 3. 捕获阶段,如果addEventListener第三个参数是true,那么则处于捕获阶段 document-html-body-father-son
        var son = document.querySelector('.son');
        son.addEventListener('click',function () {
            alert('son');
        }, true);

        var father = document.querySelector('.father');
        father.addEventListener('click',function () {
            alert('father');
        }, true)
    </script>
</body>

以下代码点击后先弹出son框,再弹出father框,最后弹出document框。

html 复制代码
<head>
    <meta charset="UTF-8">
    <title>事件流</title>
    <style>
        .father {
            width: 400px;
            height: 400px;
            margin: 200px auto;
            padding: 50px;
            background: antiquewhite;
        }
        .son {
            background: gray;
            width: 100%;
            height: 100%;
        }
    </style>
</head>
<body>
    <div class="father">
        <div class="son">son盒子</div>
    </div>
    <script>
        // 4. 冒泡阶段:如果addEventListener第三个参数是false,或者省略,那么就是冒泡阶段,执行顺序 son-father-body-html-document
        var son = document.querySelector('.son');
        son.addEventListener('click',function () {
            alert('son');
        });

        var father = document.querySelector('.father');
        father.addEventListener('click',function () {
            alert('father');
        });
        document.addEventListener('click',function () {
            alert('document');
        })
    </script>
</body>

事件对象

  1. event 就是一个事件对象,写到侦听函数的小括号中
  2. 事件对象只有有了事件才会存在,它是系统自动创建的,无需传递参数
  3. 事件对象是事件相关的一系列相关数据的集合,跟事件相关,比如鼠标点击里面就包含了鼠标的相关信息,如果是键盘事件里面就包含了键盘的信息,比如:判断用户按下了哪个键
  4. 事件对象不一定非要写成event,名字可以随便写
  5. 事件对象也有兼容性问题,ie678通过window.event获取事件对象。

获取事件对象

传统方式获取事件对象
html 复制代码
<body>
    <div>一个盒子</div>
    <script>
        var div = document.querySelector('div');
        div.onclick = function (event) {
            // 1. event 就是一个事件对象,写到侦听函数的小括号中
            // 2. 事件对象只有有了事件才会存在,它是系统自动创建的,无需传递参数
            // 3. 事件对象是事件相关的一系列相关数据的集合,跟时间相关,比如鼠标点击里面就包含了鼠标的相关信息,如果是键盘事件里面就包含了键盘的信息,比如:判断用户按下了哪个键
            console.log(event);
        }
    </script>
</body>

以上代码点击之后会在控制台输出鼠标的相关信息,如截图中所示:

监听方式获取事件对象

以下代码的执行顺序与上面代码无异,不做演示

html 复制代码
<body>
    <div>一个盒子</div>
    <script>
        var div = document.querySelector('div');
        div.addEventListener('click',function (event) {
            console.log(event);
        })
    </script>
</body>
在ie678中使用监听对象
html 复制代码
<body>
    <div>一个盒子</div>
    <script>
        var div = document.querySelector('div');
        div.addEventListener('click',function (event) {
            console.log(event); // undefined
            console.log(window.event); // 事件对象
        })
    </script>
</body>

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

事件对象属性方法 说明 备注
e.target 返回触发事件的对象 标准
e.srcElement 返回触发事件的对象 非标准 ie678使用
e.type 返回时间的类型,比如'click'、'mouseover' 不带on
e.cancelbubble 该属性组织冒泡 非标准 ie678使用
e.returnValue 该属性组织默认时间(默认行为) 非标准 ie678使用,比如不让连接跳转
e.preventDefault() 该方法阻止默认事件(默认行为) 标准,比如不让链接跳转
e.stopPropagation() 阻止冒泡 标准
  1. this:我们给谁绑定了事件,那么this就指向谁
  2. event.target:指向我们点击的那个对象
  3. event.currentEvent类似于this,但是ie678不支持

阻止默认行为

什么是默认行为?比如a标签是跳转。

html 复制代码
<body>
    <a href="https://baidu.com">百度</a>
    <script>
        var a = document.querySelector('a');
        // 1. 监听方式
        a.addEventListener('click',function (e) {
            e.preventDefault(); // DOM 标准写法
        })
        // 2. 传统方式
        a.onclick = function (e) {
            // 普通浏览器 e.preventDefault(); 方法
            e.preventDefault();
            // 低版本浏览器 ie678 returnValue 属性
            e.returnValue;
            // 也可以使用return false 阻止默认方法,没有兼容问题
            return false;
        }
    </script>
</body>

其中监听浏览器只能使用e.preventDefault()的写法

传统方式可以采用三种方式

  1. e.preventDefault();
  2. e.returnValue; // 仅支持 ie678
  3. return false; 最推荐的写法,没有兼容问题

阻止冒泡事件

冒泡事件:开始时由最具体的元素接收,然后主机箱上传播到DOM最顶层节点。

事件冒泡本身的特定会带来一定的好处,也会带来一定的坏处,需要我们灵活掌握。

阻止冒泡排序

  1. stopPropagation()方法

在事件方法中对事件对象使用stopPropagation()即可

在ie678中使用window.event.cancelBubble = true;的写法

事件委托

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

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

示例

ul注册点击事件,然后利用时间对象的target来找到当前点击的li,因为点击li,事件会冒泡到ul上,ul有注册事件,就会触发事件监听器。

一起看一下以下代码:

  1. 我们给父元素ul添加点击事件,这样当我们点击子元素li之后,就会通过冒泡的方式传递到ul
  2. 我们在点击事件中通过target获取我们点击的元素,然后给它一个背景颜色
  3. 完美
html 复制代码
<body>
    <ul>
        <li>弹框咯</li>
        <li>弹框咯</li>
        <li>弹框咯</li>
        <li>弹框咯</li>
        <li>弹框咯</li>
        <li>弹框咯</li>
        <li>弹框咯</li>
    </ul>
    <script>
        var ul = document.querySelector('ul');
        ul.addEventListener('click',function (e) {
            e.target.style.backgroundColor = 'pink';
        })
    </script>
</body>

禁用鼠标右键

html 复制代码
<script>
    document.addEventListener('contextmenu',function (e) {
        e.preventDefault();
    })
</script>

^注意:由于是监听事件,不支持return false;的写法,必须要用e.preventDefault();^

禁用文字选择

html 复制代码
<body>
    <span>我是Jim.kk</span>
    <script>
        document.addEventListener('selectstart',function (e) {
            e.preventDefault();
        })
    </script>
</body>

获取鼠标坐标信息

鼠标事件对象 说明 备注
e.clientX 返回鼠标相对于浏览器窗口可视区的X坐标
e.clientY 返回鼠标相对于浏览器窗口可视区的Y坐标
e.pageX 返回鼠标相对于文档页面的X坐标 IE9+ 支持
e.pageY 返回鼠标相对于文档页面的Y坐标 IE9+ 支持
e.screenX 返回鼠标相对于电脑屏幕的X坐标
e.screenY 返回鼠标相对于电脑屏幕的Y坐标
html 复制代码
<head>
    <meta charset="UTF-8">
    <title>获取鼠标坐标 </title>
    <style>
        body {
            height: 3000px;
        }
    </style>
</head>
<body>
<script>
    document.addEventListener('click',function (e) {
        console.log(e.clientX); // 可视窗口的X坐标(纯网页部分)
        console.log(e.clientY); // 可视窗口的Y坐标(纯网页部分)
        console.log(e.pageX);   // 页面上的X坐标(若是页面发生了滚动,则该值也会增大缩小,clientX/Y不会)
        console.log(e.pageY);   // 页面上的X坐标(若是页面发生了滚动,则该值也会增大缩小,clientX/Y不会)
        console.log(e.screenX); // 当前屏幕的坐标,包含浏览器上册的操作栏部分
        console.log(e.screenY); // 当前屏幕的坐标,包含浏览器上册的操作栏部分
    })
</script>
</body>

以上代码点击后输出内容如下所示(页面已经滚动):

常用键盘事件

键盘事件 触发条件 备注
onkeyup 某个按键松开时触发
onkeydown 某个按键按下时触发
onkeypress 某个按键按下时被触发 可以区分字母大小写,但是不能识别功能键,比如:ctrl、shift、箭头等
keydown | 键盘被按下
html 复制代码
<body>
<script>
    document.addEventListener('keydown',function (e) {
        console.log(e.key+"被按下了");
    })
</script>
</body>

以上代码,当我在页面上按住"s"键不放,效果如下:

keyup | 键盘弹起事件

与键盘按下不太一样,当键盘持续被按着时,键盘按下事件会一直被触发,但是键盘弹起事件只有在弹起的时候会被触发一次,见以下代码。

html 复制代码
<body>
    <script>
        document.addEventListener('keyup',function (e) {
            console.log(e.key+"弹起了");
        })
    </script>
</body>

以上代码在我点击键盘后(长按弹起后才会生效),控制台输出如下:

keypress | 键盘按下事件

与上面两个方法不同的是,keypress按钮对功能键不生效。

html 复制代码
<body>
    <script>
        document.addEventListener('keypress',function (e) {
            console.log(e.key+"被按下了");
        })
    </script>
</body>

以上代码当我在键盘上分别点击asdfctrl键后,控制台输出如下:

可以看到,ctrl键并没有生效。

我们可以通过event.key来获取键盘的值,或者event.keyCode来获取ASCII码值。

按下s键输入框获取焦点案例

html 复制代码
<body>
    <input type="text">
    <script>
        var search = document.querySelector('input');
        document.addEventListener('keyup',function (e) {
            if ( e.key === 's') {
                search.focus();
            }
        })
    </script>
</body>

以上代码用户在页面中点击s键,输入框就会获取焦点,用户就可以在输入框中输入内容了。

相关推荐
吕彬-前端37 分钟前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱39 分钟前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store
guai_guai_guai1 小时前
uniapp
前端·javascript·vue.js·uni-app
bysking2 小时前
【前端-组件】定义行分组的表格表单实现-bysking
前端·react.js
王哲晓2 小时前
第三十章 章节练习商品列表组件封装
前端·javascript·vue.js
fg_4112 小时前
无网络安装ionic和运行
前端·npm
理想不理想v2 小时前
‌Vue 3相比Vue 2的主要改进‌?
前端·javascript·vue.js·面试
酷酷的阿云2 小时前
不用ECharts!从0到1徒手撸一个Vue3柱状图
前端·javascript·vue.js
微信:137971205872 小时前
web端手机录音
前端
齐 飞2 小时前
MongoDB笔记01-概念与安装
前端·数据库·笔记·后端·mongodb