🏆从零开始学习JS进阶2️⃣🏆

简介: 文档对象模型(Document Object Model,简称DOM),是 W3C 组织推荐的处理 可扩展标记语言(html或xml文档) 的标准 编程接口。它是一种与平台和语言无关的 API,它可以动态操作HTML文档,如 对html标签作增删改查操作。DOM 是一种基于树的 API 文档,在处理html文档的过程中,DOM以对象的形式存储在内存中。因为DOM是基于树结构存储在内存中的,所以DOM又称为文档树模型。

1.JPG


🌈前言


大家好,我是问啥啥都会,干啥啥不行的XiaoLin,后端人眼中的前端是什么模样呢?我Lin某人给你答案。



三、DOM


3.1、DOM简介


文档对象模型(Document Object Model,简称DOM),是 W3C 组织推荐的处理 可扩展标记语言(html或xml文档) 的标准 编程接口。它是一种与平台和语言无关的 API,它可以动态操作HTML文档,如 对html标签作增删改查操作。DOM 是一种基于树的 API 文档,在处理html文档的过程中,DOM以对象的形式存储在内存中。因为DOM是基于树结构存储在内存中的,所以DOM又称为文档树模型。


3.2、DOM树


2.JPG


DOM树 又称为文档树模型,把文档映射成树形结构,通过节点对象对其处理,处理的结果可以加入到当前的页面。他有三个组成部分:


  • 文档:一个页面就是一个文档,DOM中使用document表示
  • 节点:网页中的所有内容,在文档树中都是节点(标签、属性、文本、注释等),使用node表示
  • 标签节点:网页中的所有标签,通常称为元素节点,又简称为“元素”,使用element表示


3.4、获取元素


我们获取元素的目的是,比如我们想要操作页面上的某部分(显示/隐藏,动画),需要先获取到该部分对应的元素,再对其进行操作。


3.4.1、根据ID获取


  1. 语法:document.getElementById(id)
  2. 参数:id的值,是区分大小写的字符串。
  3. 返回值:元素对象或null。
  4. 作用:根据ID获取元素对象。


<body>
    <div id="name">XiaoLin</div>
    <script>
        // 因为我们文档页面从上往下加载,所以先得有标签 所以我们script写到标签的下面
        var timer = document.getElementById('name');
        console.log(timer);
        console.log(typeof timer);
        // console.dir 打印我们返回的元素对象 更好的查看里面的属性和方法
        console.dir(timer);
    </script>
</body>
复制代码


3.4.2、根据标签名获取元素


  1. 语法:document.getElementsByTagName('标签名') 或者 element.getElementsByTagName('标签名')
  2. 参数:标签名。
  3. 返回值:元素对象集合(伪数组,数组元素是元素对象)。
  4. 作用:根据标签名获取元素对象。


<body>
    <ul>
        <li>知否知否,应是等你好久11</li>
        <li>知否知否,应是等你好久22</li>
        <li>知否知否,应是等你好久33</li>
        <li>知否知否,应是等你好久44</li>
        <li>知否知否,应是等你好久55</li>
    </ul>
    <ul id="nav">
        <li>生僻字</li>
        <li>生僻字</li>
        <li>生僻字</li>
        <li>生僻字</li>
        <li>生僻字</li>
    </ul>
    <script>
        // 1.返回的是 获取过来元素对象的集合 以伪数组的形式存储的
        var lis = document.getElementsByTagName('li');
        console.log(lis);
        console.log(lis[0]);
        // 2. 我们想要依次打印里面的元素对象我们可以采取遍历的方式
        for (var i = 0; i < lis.length; i++) {
            console.log(lis[i]);
        }
        // 3. element.getElementsByTagName()  可以得到这个元素里面的某些标签
        var nav = document.getElementById('nav'); // 这个获得nav 元素
        var navLis = nav.getElementsByTagName('li');
        console.log(navLis);
    </script>
</body>
复制代码


对于根据标签名获取到的元素,我们需要注意:


  1. 因为得到的是一个对象的集合,所以我们想要操作里面的元素就需要先遍历。
  2. 得到的元素对象是动态的,即:当页面增加了标签,这个集合中也就增加了元素。


3.4.3、通过其他方式获取


H5还有其他的几种方式获取:


  1. 通过类名返回元素对象集合:document.getElementByClassName('类名')
  2. 通过指定选择器返回第一个元素对象:document.querySelector('选择器')
  3. 通过指定选择器返回:document.querySelectorAll('选择器')


<body>
    <div class="box">XiaoLin_Java</div>
    <div class="box">XiaoLin_Python</div>
    <div id="nav">
        <ul>
            <li>首页</li>
            <li>产品</li>
        </ul>
    </div>
    <script>
        // 1. getElementsByClassName 根据类名获得某些元素集合
        var boxs = document.getElementsByClassName('box');
        console.log(boxs);
        // 2. querySelector 返回指定选择器的第一个元素对象  切记 里面的选择器需要加符号 .box  #nav
        var firstBox = document.querySelector('.box');
        console.log(firstBox);
        var nav = document.querySelector('#nav');
        console.log(nav);
        var li = document.querySelector('li');
        console.log(li);
        // 3. querySelectorAll()返回指定选择器的所有元素对象集合
        var allBox = document.querySelectorAll('.box');
        console.log(allBox);
        var lis = document.querySelectorAll('li');
        console.log(lis);
    </script>
</body>
复制代码


3.4.4、获取特殊元素


3.4.4.1、获取Body


我们可以获取到整个Body元素。


document.body // 返回一个body元素对象
复制代码


3.4.4.2、获取Html元素


document.documentElement // 返回Html元素对象
复制代码


3.5、事件基础


3.5.1、事件概述


JavaScript 使我们有能力创建动态页面,而事件是可以被 JavaScript 侦测到的行为。网页中的每个元素都可以产生某些可以触发 JavaScript 的事件,例如,我们可以在用户点击某按钮时产生一个 事件,然后去执行某些操作。


简单来说就是点击了某个按钮,发生了某件事情。


3.5.2、事件三要素


  1. 事件源(谁):触发事件的元素。
  2. 事件类型(什么事件): 例如 click 点击事件。
  3. 事件处理程序(做了啥):事件触发后要执行的代码(函数形式),事件处理函数。


<body>
    <button id="btn">XiaoLin</button>
    <script>
        // 点击一个按钮,弹出对话框
        // 1. 事件是有三部分组成  事件源  事件类型  事件处理程序   我们也称为事件三要素
        //(1) 事件源 事件被触发的对象   谁  按钮
        var btn = document.getElementById('btn');
        //(2) 事件类型  如何触发 什么事件 比如鼠标点击(onclick) 还是鼠标经过 还是键盘按下
        //(3) 事件处理程序  通过一个函数赋值的方式 完成
        btn.onclick = function() {
            alert('点秋香');
        }
    </script>
</body>
复制代码


3.5.3、执行事件的步骤


  1. 获取事件源。
  2. 注册事件(绑定事件)。
  3. 才有函数赋值形式来添加事件处理程序。


<body>
    <div>123</div>
    <script>
        // 执行事件步骤
        // 点击div 控制台输出 我被选中了
        // 1. 获取事件源
        var div = document.querySelector('div');
        // 2.绑定事件 注册事件
        // div.onclick 
        // 3.添加事件处理程序 
        div.onclick = function() {
            console.log('我是XiaoLin,我被选中了');
        }
    </script>
</body>
复制代码


3.5.4、常见的鼠标事件


鼠标事件 触发条件
onclick 鼠标点击左键触发
onmouseover 鼠标经过触发
onmouseout 鼠标离开触发
onfocus 获得鼠标焦点时触发
onblur 失去鼠标焦点触发
onmousemove 鼠标移动触发
onmouseup 鼠标弹起触发
onmousedown 鼠标按下触发


3.5.5、事件的书写位置


3.5.5.1、行内式


所谓的行内是指的就是直接把事件写在html代码中,不建议使用这种方式,虽然写起来简单,但是这种方式仅仅适合写一些简单的事件,比如说alert之类的,如果写一些复杂的就看起来很冗余,而且后期维护起来及其困难。


<button onclick="alert('hello,我是XiaoLin')">行内式</button>
复制代码


3.5.5.2、内嵌式


内嵌式指的是在html最下面写JavaScript代码,这样不仅仅可以写一些复杂的事件,同时还便于维护,但是还是不推荐这种方式,因为JavaScript代码和html代码没有完全解耦分离


<button onclick="fun()">内嵌式</button>
<script type="text/javascript">
function fun(){
  alert("hello,我是XiaoLin");
}
复制代码


3.5.5.3、外部JavaScript文件


这种是目前最主流也是推崇的方式,他是将所有的JavaScript都抽离出去,放在了一个单独的文件里面,然后在页面里面引用即可。


3.6、操作元素


JavaScript的DOM操作可以改变网页内容、结构和样式,我们可以利用DOM操作元素来改变元素里面的内容、属性等。


3.6.1、改变元素内容


方法 描述
element.innerText 从起始位置到终止位置的内容,但他去除html标签,同时空格和换行也会被去掉。
element.innerHTML 从起始位置到终止位置的内容,包括html标签,同时空格和换行也会被去掉。
<!doctype html>
 <html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <p id="demo">my first demo</p>
</body>
<script>
    document.getElementById("demo").innerHTML="<b>hello XiaoLin_Java</b>"
</script>
</html>
复制代码

1.JPG

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
</head>
<body>
    <p id="demo">my first demo</p>
</body>
<script>
    document.getElementById("demo").innerText="<h1>My First XiaoLin_Java</h1>";
</script>
</html>
复制代码

2.JPG


通过运行的结果,我们可以很好的看出这两者的区别:


  1. innerText会去除空格和换行,而innerHTML会保留空格和换行。
  2. innerText不会识别html,而innerHTML会识别。


3.6.2、获取属性的值


我们可以使用JavaScript来获取属性的值,他的基本语法为元素对象.属性名


<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
</head>
<body>
    <img src="imges/ldh.jpg" alt="" id="imgBtn" title="刘德华" >
</body>
</html>
<script>
        // 获取元素
        var img = document.getElementById('imgBtn');
        // 2. 注册事件  处理程序
        img.onclick = function() {
            // 获取元素的值
            alert(img.title)
        }
 </script>
复制代码


3.6.3、设置属性的值


设置属性的值的语法为:元素对象.属性名 = 值


<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
</head>
<body>
    <img src="imges/ldh.jpg" alt="" id="imgBtn" title="刘德华" >
</body>
</html>
<script>
        // 获取元素
        var img = document.getElementById('imgBtn');
        // 2. 注册事件  处理程序
        img.onclick = function() {
            // 给元素设置值
            alert("设置之前:"+img.title)
            img.title = 'XiaoLin';
             alert("设置之后:"+img.title)
        }
 </script>
复制代码


3.6.4、布尔类型的值设值


<body>
    <button>按钮</button>
    <input type="text" value="输入内容">
    <script>
        // 1. 获取元素
        var btn = document.querySelector('button');
        var input = document.querySelector('input');
        // 2. 注册事件 处理程序
        btn.onclick = function() {
            // 表单里面的值 文字内容是通过 value 来修改的
            input.value = '被点击了';
            // 如果想要某个表单被禁用 不能再点击 disabled  我们想要这个按钮 button禁用
            // btn.disabled = true;
            this.disabled = true;
            // this 指向的是事件函数的调用者 btn
        }
    </script>
</body>
复制代码


3.6.5、排他操作


如果有同一组元素,我们想要某一个元素实现某种样式, 需要用到循环的排他思想算法:


  1. 所有元素全部清除样式(干掉其他人)
  2. 给当前元素设置样式 (留下我自己)
  3. 注意顺序不能颠倒,首先干掉其他人,再设置自己,如果先设值自己再干掉其他人会导致所有都没有样式了。


<button>按钮1</button>
    <button>按钮2</button>
    <button>按钮3</button>
    <button>按钮4</button>
    <button>按钮5</button>
    <script>
        // 1. 获取所有按钮元素
        var btns = document.getElementsByTagName('button');
        // btns得到的是伪数组  里面的每一个元素 btns[i]
        for (var i = 0; i < btns.length; i++) {
            btns[i].onclick = function() {
                // (1) 我们先把所有的按钮背景颜色去掉  干掉所有人
                for (var i = 0; i < btns.length; i++) {
                    btns[i].style.backgroundColor = '';
                }
                // (2) 然后才让当前的元素背景颜色为pink 留下我自己
                this.style.backgroundColor = 'pink';
            }
        }
    </script>
复制代码


3.6.6、一键换肤


<body>
    <ul class="baidu">
        <li><img src="images/1.jpg"></li>
        <li><img src="images/2.jpg"></li>
        <li><img src="images/3.jpg"></li>
        <li><img src="images/4.jpg"></li>
    </ul>
    <script>
        // 1. 获取元素 
        var imgs = document.querySelector('.baidu').querySelectorAll('img');
        // console.log(imgs);
        // 2. 循环注册事件 
        for (var i = 0; i < imgs.length; i++) {
            imgs[i].onclick = function() {
                // this.src 就是我们点击图片的路径   images/2.jpg
                // console.log(this.src);
                // 把这个路径 this.src 给body 就可以了
                document.body.style.backgroundImage = 'url(' + this.src + ')';
            }
        }
    </script>
</body>
复制代码


3.6.7、表格隔行变色


<script>
        // 1.获取元素 获取的是 tbody 里面所有的行
        var trs = document.querySelector('tbody').querySelectorAll('tr');
        // 2. 利用循环绑定注册事件
        for (var i = 0; i < trs.length; i++) {
            // 3. 鼠标经过事件 onmouseover
            trs[i].onmouseover = function() {
                    // console.log(11);
                    this.className = 'bg';
                }
                // 4. 鼠标离开事件 onmouseout
            trs[i].onmouseout = function() {
                this.className = '';
            }
        }
    </script>
复制代码


3.6.8、全选


<script>
        // 1. 全选和取消全选做法:  让下面所有复选框的checked属性(选中状态) 跟随 全选按钮即可
        // 获取元素
        var j_cbAll = document.getElementById('j_cbAll'); 
        var j_tbs = document.getElementById('j_tb').getElementsByTagName('input'); 
        // 全选按钮注册事件
        j_cbAll.onclick = function() {
                // this.checked 当前复选框的选中状态
                console.log(this.checked);
                for (var i = 0; i < j_tbs.length; i++) {
                    j_tbs[i].checked = this.checked;
                }
         }
         // 给所有的子复选框注册单击事件
        for (var i = 0; i < j_tbs.length; i++) {
            j_tbs[i].onclick = function() {
                // flag 控制全选按钮是否选中
                var flag = true;
                // 每次点击下面的复选框都要循环检查者4个小按钮是否全被选中
                for (var i = 0; i < j_tbs.length; i++) {
                    if (!j_tbs[i].checked) {
                        flag = false;
                        break; 
                    }
                }
                // 设置全选按钮的状态
                j_cbAll.checked = flag;
            }
        }
    </script>
复制代码


3.7、自定义属性操作


3.7.1、设置属性值


我们有两种方式来设置属性值:


  1. element.属性 = '值';
  2. element.setAttribute ('属性','值');


他们俩的区别是:


  1. 方式一为内置属性设置值。
  2. 方式二主要是给自定义属性设置值。


3.7.2、移除属性


移除属性的语法格式为:


element.removeAttribute('属性');
div.removeAttribute('index');
复制代码


3.8、节点操作


3.8.1、节点概述


网页中的所有内容都是节点(标签、属性、文本、注释等),在DOM 中,节点使用 node 来表示。HTML DOM 树中的所有节点均可通过 JavaScript 进行访问,所有 HTML 元素(节点)均可被修改,也可以创建或删除。在日常开发中,我们最常使用的是元素节点。


3.JPG


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


  • 元素节点的节点类型为1
  • 属性节点的节点类型为2
  • 文本节点的节点类型为3


3.8.2、节点层级


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


3.8.3、获取父级节点


我们可以通过parentNode来获取某节点的父节点,他返回的是最近的一父节点,如果没有就返回null。


<div class="demo">
        <div class="box">
            <span class="erweima">×</span>
        </div>
    </div>
    <script>
        // 1. 父节点 parentNode
        var erweima = document.querySelector('.erweima');
        // 得到的是离元素最近的父级节点(亲爸爸) 如果找不到父节点就返回为 null
        console.log(erweima.parentNode);
    </script>
复制代码


3.8.4、子节点


获取子节点的语法为:


parentNode.childNodes
复制代码


这种写法是标准写法,返回包含指定节点的所有子节点,包括元素节点、文本节点,如果只想获得里面的元素节点,就需要专门处理,所以我们一般是不会用的,我们一般使用下面这个:


parentNode.childrent
复制代码


parentNode.childrent是一个只读属性,返回所有的子元素节点,他只返回子元素节点,其他节点不返回。


<ul>
        <li>我是XiaoLin</li>
        <li>我是XiaoLin</li>
        <li>我是XiaoLin</li>
        <li>我是XiaoLin</li>
    </ul>
    <script>
        // DOM 提供的方法(API)获取
        var ul = document.querySelector('ul');
        var lis = ul.querySelectorAll('li');
        // 1. 子节点  childNodes 所有的子节点 包含 元素节点 文本节点等等
        console.log(ul.childNodes);
        console.log(ul.childNodes[0].nodeType);
        console.log(ul.childNodes[1].nodeType);
        // 2. children 获取所有的子元素节点 也是我们实际开发常用的
        console.log(ul.children);
    </script>
复制代码


还有几个获取常见节点的方法。


获取第一个节点

parentNode.firestChild
复制代码


他返回的的是第一个子节点的所有节点,如果找不到就返回null。


获取第一子元素节点

parentNode.firstElementChild
复制代码


他返回的是第一个子元素节点,找不到就返回NULL。这个方法有兼容性问题,IE9以上才支持。


获取最后一个节点

parentNode.lastChild
复制代码

获取最后一个子元素节点

parentNode.lastElementChild
复制代码


他返回的是最后一个子元素节点,找不到就返回null。这个方法有兼容性问题,IE9以上才支持。


总结


在实际开发中,firstChild和lastChild会包含其他节点,对于我们的操作是十分不方便的,而firstElementChild 和 lastElementChild又有兼容性问题,如果我们想要获取第一子元素节点和最后一个子元素节点,我们可以采用的办法是:


  1. 获取第一个子元素节点:parentNode.childrent[0]
  2. 获取最后一个子元素节点:parentNode.children[parentNode.children.length-1]


<ol>
        <li>我是li1</li>
        <li>我是li2</li>
        <li>我是li3</li>
        <li>我是li4</li>
        <li>我是li5</li>
    </ol>
    <script>
        var ol = document.querySelector('ol');
        // 1. firstChild 第一个子节点 不管是文本节点还是元素节点
        console.log(ol.firstChild);
        console.log(ol.lastChild);
        // 2. firstElementChild 返回第一个子元素节点 ie9才支持
        console.log(ol.firstElementChild);
        console.log(ol.lastElementChild);
        // 3. 实际开发的写法  既没有兼容性问题又返回第一个子元素
        console.log(ol.children[0]);
        console.log(ol.children[ol.children.length - 1]);
    </script>
复制代码


3.8.5、创建节点


如果元素原先不存在,我们可以使用document.createElement('标签名称'),来创建指定的标签的HTML元素,这些元素原先是不存在的,是根据我们的需求动态生成的,所以称为动态创建节点。


3.8.6、添加节点


我们有两种添加节点的方式:


  1. 尾插法
  2. 头插法


3.8.6.1、头插法


node.insertBefore(chile,指定元素)方法将以节点添加到父节点指定子节点的前面,类似于CSS里面的before伪元素。


<html>
    <body>
        <ul>
          <li>123</li>
      </ul>
    </body>
</html>   
    <script>
        // 添加节点 node.insertBefore(child, 指定元素);
        var lili = document.createElement('li');
        ul.insertBefore(lili, ul.children[0]);
        // 我们想要页面添加一个新的元素 : 1. 创建元素 2. 添加元素
    </script>
复制代码


3.8.6.2、尾插法


node.appendChind(child)方法将一个节点添加到指定的父节点的子节点列表的末尾,类似于CSS里面的after伪元素。


<html>
    <body>
        <ul>
          <li>123</li>
      </ul>
    </body>
</html>   
    <script>
        // 创建节点元素节点
        var li = document.createElement('li');
        // 添加节点 node.appendChild(child)  node 父级  child 是子级 后面追加元素
        var ul = document.querySelector('ul');
        ul.appendChild(li);
    </script>
复制代码


3.8.7、删除节点


node.removeChild() 方法从 node节点中删除一个子节点,返回删除的节点。


<html>
    <body>
        <button>删除</button>
      <ul>
          <li>熊大</li>
          <li>熊二</li>
          <li>光头强</li>
      </ul>
    <script>
        // 1.获取元素
        var ul = document.querySelector('ul');
        var btn = document.querySelector('button');
        // 2. 删除元素  node.removeChild(child)
        // ul.removeChild(ul.children[0]);
        // 3. 点击按钮依次删除里面的孩子
        btn.onclick = function() {
            if (ul.children.length == 0) {
                this.disabled = true;
            } else {
                ul.removeChild(ul.children[0]);
            }
        }
    </script>
    </body>
</html>
复制代码


3.8.8、复制节点


node.cloneNode()方法返回调用该方法的节点的一个副本,成为复制节点。他里面可以传参数:


  1. 如果参数为空或者是false的话,就只是浅拷贝,即只复制节点本身,不会复制里面的子节点。
  2. 如果参数为true的话,就是深拷贝,会复制该节点以及该节点的所有子节点。
  3. 注意和Java中的深/浅拷贝区分。


<html>
    <body>
        <ul>
          <li>1111</li>
          <li>2</li>
          <li>3</li>
      </ul>
  <script>
        var ul = document.querySelector('ul');
        // 1. node.cloneNode(); 括号为空或者里面是false 浅拷贝 只复制标签不复制里面的内容
        // 2. node.cloneNode(true); 括号为true 深拷贝 复制标签复制里面的内容
        var lili = ul.children[0].cloneNode(true);
        ul.appendChild(lili);
  </script>
    </body>
</html>
复制代码


3.9、元素操作


3.9.1、创建元素


我们创建元素主要有三种方式:


  1. document.write
  2. inner.innerHTML
  3. document.createElement


document.write


document.write是直接将内容写入页面的内容流。


document.write('<div>123</div>');
复制代码


innerHTML


innerHTML是将内容写入某个DOM节点,inner.innerHTML += '<a href="#">百度</a>'


如果我们需要创建多个元素可以使用innerHTML效率更高,但是这个时候不要去拼接字符串,可以采用数组的结构拼接。


createElement


createElement可以直接创建一个元素,虽然他在创建多个元素的效率稍微低一点点,但是他的结构更清晰明了。


var create = document.querySelector('.create');
        for (var i = 0; i <= 100; i++) {
            var a = document.createElement('a');
            create.appendChild(a);
        }
复制代码


3.9.2、效率比较


效率低:innerHTML字符串拼接方式


innerHTML的方式是效率最低的,而且他需要去拼接字符串,较为繁琐。


<script>
    function fn() {
        var d1 = +new Date();
        var str = '';
        for (var i = 0; i < 1000; i++) {
            document.body.innerHTML += '<div style="width:100px; height:2px; border:1px solid blue;"></div>';
        }
        var d2 = +new Date();
        console.log(d2 - d1);
    }
    fn();
</script>
复制代码


效率一般:createElement


<script>
    function fn() {
        var d1 = +new Date();
        for (var i = 0; i < 1000; i++) {
            var div = document.createElement('div');
            div.style.width = '100px';
            div.style.height = '2px';
            div.style.border = '1px solid red';
            document.body.appendChild(div);
        }
        var d2 = +new Date();
        console.log(d2 - d1);
    }
    fn();
</script>
复制代码


效率高:innerHTML数组方式


<script>
    function fn() {
        var d1 = +new Date();
        var array = [];
        for (var i = 0; i < 1000; i++) {
            array.push('<div style="width:100px; height:2px; border:1px solid blue;"></div>');
        }
        document.body.innerHTML = array.join('');
        var d2 = +new Date();
        console.log(d2 - d1);
    }
    fn();
</script>
复制代码


3.10、事件高级


3.10.1、addEventListener()


addEventListener()方法将指定的监听器注册到目标对象上,一般是目标对象直接调用该方法,当该对象触发指定的事件时,就会执行事件处理函数。该方法有三个参数:


  1. type:事件的类型,他是一个字符串,常见的有click、mouseover
  2. listener:事件处理函数,事件发生时,会调用该监听函数。
  3. useCapture:是一个可选参数,类型为布尔值,默认为false。


<html>
    <body>
        <button>addEventListener方法测试</button>
    </body>
    <script>
       var btns = document.querySelectorAll('button');
         btns[0].addEventListener('click', function() {
        alert(22);
      })
    </script>
</html>
复制代码


3.10.2、attacheEvent()


attachEvent()方法将指定的监听器注册到目标对象上,由目标对象调用该方法,当该对象触发指定的事件时,指定的回调函数就会被执行。该方法有两个参数:


  1. eventNameWithOn:事件类型的类型,他是一个字符串,常见的有onclick、onmouseover
  2. callback:事件处理函数,当目标事件触发时,回调函数被调用。


<html>
    <body>
        <button>attacheEvent方法测试</button>
    </body>
    <script>
       var btns = document.querySelectorAll('button');
      btns[0].attachEvent('onclick', function() {
        alert(11);
      })
    </script>
</html>
复制代码


3.10.3、DOM事件流


事件流描述的是从页面接受事件的顺序。当事件发生时,会在元素节点之间按照特定的顺序传播,这个传播过程就叫做DOM事件流。


那么事件流的顺序是怎么样的呢?互联网两大巨头公司有了不同的纷争:


  1. 事件冒泡:由IE提出,事件开始时是由最具体的元素接受,然后逐渐向上传播到DOM最顶层节点的过程。
  2. 事件捕获:由网景公司提出,他表示的是由DOM最顶层节点开始,然后逐渐向下传播到最具体的元素接受的过程。


我们可以发现,这两家公司好像是天生的仇人一样,他们连提出的标准的方向都不同。IE 提出从目标元素开始,然后一层一层向外接收事件并响应,也就是冒泡型事件流。而Netscape(网景公司)提出从最外层开始,然后一层一层向内接收事件并响应,也就是捕获型事件流。他们俩谁也不服谁,为了一统江湖,W3C采用了这种的方式,制定了一个新的标准:先捕获再冒泡。当事件发生的时候,会经历3个阶段:


  1. 捕获阶段
  2. 当前目标阶段
  3. 冒泡阶段


他有点类似于我们往水里抛一个石头:


  1. 首先它会有一个下降的过程,这个过程就可以理解为从最顶层向事件发生的最具体元素(目标点)的捕获过程。
  2. 之后会产生泡泡,会在最低点( 最具体元素),有点类似于处于当前目标阶段。
  3. 最后会漂浮到水面上,这个过程相当于事件冒泡。


4.JPG

他有几个注意的点:


  1. JavaScript代码只能执行捕获或者冒泡其中的一个阶段。
  2. onclick和attachEven只能到冒泡阶段。
  3. addEventListener(type、listener、useCapture)中的第三个参数如果是true,则表示在事件捕获阶段调用事件处理程序,如果使用默认的false,表示事件在冒泡阶段调用事件处理函数。
  4. 实际的开发中我们很少使用事件捕获,更多的是关注事件冒泡。
  5. 有一些事件是没有冒泡的,比如onblur、onfocus、onmouseenter、onmouseleave。


3.10.4、事件对象


3.10.4.1、概述


事件发生后,跟事件相关的一系列信息数据的集合都放到这个对象里面,这个对象就是事件对象。这一系列信息的数据集合包括:


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


3.10.4.2、属性和方法


事件对象
e.target 返回触发事件的对象(标准
e.srcElement 返回触发事件的对象(非标准
e.type 返回事件的类型,例如click、mouseover
e.cancelBubble 阻止冒泡(非标准
e.stopPropagation() 阻止冒泡(标准
e.returnValue 阻止默认事件(非标准
e.preventDefault 阻止默认事件(标准


3.10.5、阻止默认行为


html中一些标签有默认行为,例如a标签被单击后,默认会进行页面跳转。我们如果不想让a标签跳转的话可以阻止他的默认行为。


<html>
    <body>
            <a href="http://www.baidu.com">百度</a>
    </body>
     <script>
        // 2. 阻止默认行为 让链接不跳转 
        var a = document.querySelector('a');
        a.addEventListener('click', function(e) {
             e.preventDefault(); //  dom 标准写法
        });
        // 3. 传统的注册方式
        a.onclick = function(e) {
            // 普通浏览器 e.preventDefault();  方法
            e.preventDefault();
            // 低版本浏览器 ie678  returnValue  属性
            e.returnValue = false;
            // 我们可以利用return false 也能阻止默认行为 没有兼容性问题
            return false;
        }
    </script>
</html>
复制代码


3.10.6、阻止事件冒泡


事件冒泡可能会带来好处也有可能会带来坏处,如果我们想阻止事件冒泡的话有两种写法:


  1. 标准写法:e.stopPropagation()
  2. 非标准写法:e.cancelBubble = true;


3.10.7、事件委托


事件委托也成为了事件代理,把事情委托给别人,代为处理。简单来说就是不给子元素注册事件,给父元素注册事件,把处理代码在父元素的事件中执行。


举个简单的例子,有10个班,1000名学生,每个学生要领一个swtich手柄,如果一个个由配送员来送的话,不仅仅花费的时间很长,配送员会累死,学生还要大排场龙,如果使用了代理的话,配送员只需要将每一个switch分给每个班的班主任,同学找班主任领取即可。


我们来看下面这个需求,我们点击每一个li的时候都会有一个弹窗,按照普通的思想,我们需要给每一个li单独注册事件,是非常麻烦的,不仅麻烦,而且页面加载的时候,由于访问DOM的次数变多了,会延长整个页面的响应时间。


<html>
    <body>
       <ul>
      <li>switch1</li>
      <li>switch2</li>
      <li>switch3</li>
      <li>switch4</li>
    </body>
</html>
复制代码


这个时候我们就需要采用事件委托,我们将这个事件委托给他的父元素。利用事件泡沫,当子元素的事件触发时,会冒泡到父元素,然后再去控制响应的子元素。


采用了事件委托的优点很明显:


  1. 只操作了一次DOM,显著提升了性能。
  2. 如果我们新加或者动态创建一个子元素,新加的子元素也拥有这个事件。


<html>
    <body>
       <ul>
      <li>switch1</li>
      <li>switch2</li>
      <li>switch3</li>
      <li>switch4</li>
    </body>
    <script>
        // 事件委托的核心原理:给父节点添加侦听器, 利用事件冒泡影响每一个子节点
        var ul = document.querySelector('ul');
        ul.addEventListener('click', function(e) {
            // e.target 这个可以得到我们点击的对象
            e.target.style.backgroundColor = 'pink';
        })
    </script>
</html>
复制代码


3.10.8、常见鼠标事件


鼠标事件 触发条件
onclick 鼠标点击左侧触发
onmouseover 鼠标经过触发
onmouseout 鼠标离开触发
onfoucs 获得鼠标焦点时触发
onblur 失去鼠标焦点时触发
onmousemove 鼠标移动触发
onmouseup 鼠标弹起触发
onmousedown 鼠标按下触发


我们来做一个禁止选中文字和禁止右键菜单的联系。


<body>
    我是一段不愿意分享的文字
    <script>
        // 1. contextmenu 我们可以禁用右键菜单
        document.addEventListener('contextmenu', function(e) {
                e.preventDefault();
        })
        // 2. 禁止选中文字 selectstart
        document.addEventListener('selectstart', function(e) {
            e.preventDefault();
        })
    </script>
</body>
复制代码


3.10.9、鼠标事件对象


event事件对象是事件相关的一系列信息的集合,我们常用的是鼠标事件对象

MouseEvent和键盘事件对象KeyboardEvent。


鼠标事件对象 作用
e.clientX 返回鼠标相对于浏览器可视区的X坐标
e.clientY 返回鼠标相对于浏览器可视区的Y坐标
e.pageX 返回鼠标相对于文档页面的X坐标
e.pageY 返回鼠标相对于文档页面的Y坐标
e.screenX 返回鼠标相对于电脑屏幕的X坐标
e.screenY 返回鼠标相对于电脑屏幕的Y坐标
<script>
        // 鼠标事件对象 MouseEvent
        document.addEventListener('click', function(e) {
            // 1. client 鼠标在可视区的x和y坐标
            console.log(e.clientX);
            console.log(e.clientY);
            console.log('---------------------');
            // 2. page 鼠标在页面文档的x和y坐标
            console.log(e.pageX);
            console.log(e.pageY);
            console.log('---------------------');
            // 3. screen 鼠标在电脑屏幕的x和y坐标
            console.log(e.screenX);
            console.log(e.screenY);
        })
    </script>
复制代码


我们来做一个练手的案例:跟随鼠标的猫。大致的思路是这样:


  1. 鼠标不断地移动,我们可以使用鼠标移动事件:mousemove。
  2. 鼠标在页面中移动的时候,给document注册事件。
  3. 图片要移动距离。而且不能占位置,所以我们使用绝对定位。
  4. 每次鼠标移动,我们都获得一个新的鼠标坐标,然后将这个x、y值作为图片的top和left偏移就可以移动图片了。


<html>
    <body>
            <img src="images/cat.gif" alt="">
    <script>
        var pic = document.querySelector('img');
        document.addEventListener('mousemove', function(e) {
          // 1. mousemove只要我们鼠标移动1px 就会触发这个事件
          // 2.核心原理: 每次鼠标移动,我们都会获得最新的鼠标坐标, 
            // 把这个x和y坐标做为图片的top和left 值就可以移动图片
          var x = e.pageX;
          var y = e.pageY;
          console.log('x坐标是' + x, 'y坐标是' + y);
          //3 . 千万不要忘记给left 和top 添加px 单位
          pic.style.left = x - 50 + 'px';
          pic.style.top = y - 40 + 'px';
      });
    </script>
    </body>
</html>
复制代码


3.10.11、常见的键盘事件


3.10.11.1、键盘事件


键盘事件 触发条件
onkeyup 某个键盘按键被松开时触发
onkeydown 某个键盘按键被按下时触发
onkeypress 某个键盘被按下时触发,但是他不能识别功能键,如ctrl、shift等


需要注意的是如果使用addEventListener方法时,前面不需要加on。


<script>
        // 常用的键盘事件
        //1. keyup 按键弹起的时候触发 
        document.addEventListener('keyup', function() {
            console.log('我弹起了');
        })
        //3. keypress 按键按下的时候触发  不能识别功能键 比如 ctrl shift 左右箭头啊
        document.addEventListener('keypress', function() {
                console.log('我按下了press');
        })
        //2. keydown 按键按下的时候触发  能识别功能键 比如 ctrl shift 左右箭头啊
        document.addEventListener('keydown', function() {
                console.log('我按下了down');
        })
    </script>
复制代码


3.10.11.1、键盘事件对象


键盘事件对象属性 说明
keyCode 返回该对象的ASCII码


需要注意的是:


  1. onkeydown和onkeyup不区分字母大小写,但是onkeypress区分字母大小写。
  2. 在实际开发中,用的更多的还是keydown和keyup,因为他可以识别键盘上所有的按键,包含了功能键。
  3. keypress虽然不能识别功能键,但是keyCode属性可以区分大小写,返回不能的ASCII码。


比如我们写一个小demo,判断用户按下的是哪一个按键?


<script>
        // 键盘事件对象中的keyCode属性可以得到相应键的ASCII码值
        document.addEventListener('keyup', function(e) {
            console.log('up:' + e.keyCode);
            // 我们可以利用keycode返回的ASCII码值来判断用户按下了那个键
            if (e.keyCode === 65) {
                alert('您按下的a键');
            } else {
                alert('您没有按下a键')
            }
        })
        document.addEventListener('keypress', function(e) {
            // console.log(e);
            console.log('press:' + e.keyCode);
        })
    </script>
相关文章
|
1月前
|
JavaScript
webpack学习五:webpack的配置文件webpack.config.js分离,分离成开发环境配置文件和生产环境配置文件
这篇文章介绍了如何将webpack的配置文件分离成开发环境和生产环境的配置文件,以提高打包效率。
46 1
webpack学习五:webpack的配置文件webpack.config.js分离,分离成开发环境配置文件和生产环境配置文件
|
1月前
|
JavaScript
js学习--商品列表商品详情
js学习--商品列表商品详情
19 2
|
1月前
|
JavaScript
js学习--九宫格抽奖
js学习--九宫格抽奖
17 2
|
1月前
|
JavaScript
js学习--开屏弹窗
js学习--开屏弹窗
35 1
|
1月前
|
JavaScript
js学习--抽奖
js学习--抽奖
15 1
|
1月前
|
JavaScript
js学习--隔行换色
js学习--隔行换色
30 1
|
1月前
|
JavaScript 前端开发 容器
js之dom学习
js之dom学习
35 0
|
4月前
|
存储 JavaScript 前端开发
|
6月前
|
JavaScript 前端开发
JavaScript学习 -- 内置函数(Math和Date)
JavaScript学习 -- 内置函数(Math和Date)
38 0
|
JavaScript 前端开发
JavaScript学习 -- 内置函数(Math和Date)
JavaScript学习 -- 内置函数(Math和Date)
43 0