JavaScript 网页编程(二)——Web API(DOM)

目录

一.Web API 介绍

二.DOM 介绍

三.获取元素

四.事件基础

五.操作元素

1.改变元素内容(获取或设置)

2.普通元素的属性操作

4.样式属性操作

5.自定义属性操作

六.排他操作

七.节点操作

八.事件高级

1.注册事件(绑定事件)

2.事件监听

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

4.DOM 事件流

5.事件对象

6.阻止默认行为

7.阻止事件冒泡

8.事件委托(代理、委派)

九.常用鼠标事件

十.常用键盘事件


一.Web API 介绍

  • API:某些工具(函数)由编程语言提供,已封装好其内部实现,学会使用这些接口,无需关注其内部实现方法。
  • Web API:浏览器提供的接口,用于操作 浏览器功能(BOM) 和 页面元素(DOM),作出浏览器交互效果。

二.DOM 介绍

  • DOM 文档对象模型(Document Object Model):​是 编程接口,可以改变网页的内容、结构和样式。
  • 获取的 DOM元素 是一个对象(object),所以称为 文档对象模型
  • DOM 树(文档树模型):把 文档 映射成树形结构,通过 节点对象 对其处理,处理的结果 可以加入当前页面。
  • 名词解释:
  1. 文档:一个页面 = 一个文档,用 document 表示。
  2. 节点:网页中的所有内容,如:标签、属性、文本、注释等,用 node 表示。
  3. 标签节点:网页中的所有标签,简称为“元素”,用 element 表示。
  • DOM 把以上内容都看做是对象。
  • 对于JavaScript,为了使 JavaScript 操作 HTML,JavaScript 就有了自己的 DOM 编程接口
  • 对于HTML,DOM 使得之形成一棵 DOM 树,包含文档、元素、节点

三.获取元素

  • 想要操作页面内容,需要先获取到对应的元素。

1.根据 ID 获取

  • 语法:document.getElementById(id)
  • 返回值:元素对象 或 null
  • console.dir() 可以打印获取的元素对象,更好的查看对象的属性和方法。
<body>
    <div id="time">2020-3-25</div>
    <script>
        // 因为文档页面从上往下加载,所以先有标签,所以 script 写到标签的下面
        var timer = document.getElementById('time');
        console.log(timer);
        console.log(typeof timer);    // 这里表明 timer 是一个元素对象
        console.dir(timer);    // console.dir 打印返回的元素对象 更好的查看里面的属性和方法
    </script>
</body>

2.根据 标签名 获取

  • 语法:document.getElementsByTagName('标签名') 
  • 返回值:动态的 元素对象集合(伪数组
  • 如果页面中 只有一个 li 返回的还是 伪数组 的形式 ;没有元素 返回的是 空的伪数组 的形式(因为无对象)
  • 因为得到的是对象集合,所以想操作里面的元素,需要遍历。
<body>
    <ul>
        <li> 知否知否,应是等你好久 </li>
        // ... 4 个 li
    </ul>
    <script>
        // 1.返回的是 元素对象 的集合 以 伪数组 的形式存储的
        var lis = document.getElementsByTagName('li');
        console.log(lis[0]);

        // 2. 依次打印里面的 元素对象 可以采取遍历的方式
        for (var i = 0; i < lis.length; i++) {
            console.log(lis[i]);
        }
        // 3. 如果页面中 只有一个li 返回的还是 伪数组 的形式 
        // 4. 如果页面中 没有 这个元素 返回的是 空的伪数组 的形式
    </script>
</body>

3.H5 新增获取方法

  • getElementsByClassName('类名'):根据 类名 获得某些元素集合。
  • querySelector('选择器'):返回指定选择器的 第 一个元素对象 ,选择器需要加符号 .box  #nav。【常用】
  • querySelectorAll():返回指定选择器的 所有元素对象
<body>
    <div class="box"> 盒子1 </div>
    <div class="box"> 盒子2 </div>
    <div id="nav">
        <ul>
            <li> 一只特特 </li>
            <li> 两只 </li>
        </ul>
    </div>
    <script>
        // 1. getElementsByClassName 根据类名获得某些元素集合
        var boxs = document.getElementsByClassName('box');

        // 2. querySelector 返回指定选择器的 第一个元素对象 里面的选择器需要加符号 .box  #nav
        var firstBox = document.querySelector('.box');    // 类
        var nav = document.querySelector('#nav');    // id
        var li = document.querySelector('li');    // 标签

        // 3. querySelectorAll()返回指定选择器的 所有元素对象
        var allBox = document.querySelectorAll('.box');
        var lis = document.querySelectorAll('li');
    </script>
</body>

4.获取特殊元素(body,html)

  • 获取 body 元素:var bodyEle = document.body;
  • 获取 html 元素:var htmlEle = document.documentElement;

四.事件基础

  • 触发-响应机制:网页中的每个元素都可以产生触发 JavaScript 的事件。
  1. 事件源(谁):触发事件的元素。
  2. 事件类型(事件如何触发): 如 onclick 点击事件。
  3. 事件处理程序(做什么):事件触发后要执行的代码(事件处理函数)。
    <div> 特特真可爱 </div>
    <script>
        // 点击div 控制台输出 茶茶也可爱
        // 1. 获取事件源
        var div = document.querySelector('div');    
        // 2.绑定事件 注册事件:div.onclick 
        // 3.添加事件处理程序 
        div.onclick = function() {
            console.log('茶茶也可爱');    
        }
    </script>

 

五.操作元素

  • DOM 操作元素 改变 元素内容、属性等。(注意:这些操作都是通过 元素对象的属性 实现的)
  • DOM 操作:主要针对于 元素 的操作,有创建、增、删、改、查、属性操作、事件操作
  • 创建:document.write、innerHTML、createElement
  • 增:appendChild、insertBefore
  • 删:removeChild
  • 改:主要修改 DOM 的元素属性、元素内容、表单值等
  1. 修改元素属性: src、href、title等
  2. 修改普通元素内容: innerHTML 、innerText
  3. 修改表单元素: value、type、disabled等
  4. 修改元素样式: style、className
  • 查:主要获取查询 DOM 的元素
  1. DOM 提供的API 方法: getElementById、getElementsByTagName 古老用法 不推荐
  2. H5提供的新方法: querySelector、querySelectorAll 提倡
  3. 利用节点操作获取元素: 父(parentNode)、子(children)、兄(previous/nextElementSibling) 提倡
  • 属性操作:主要针对于自定义属性
  1. setAttribute:设置 DOM 的属性值
  2. getAttribute:得到 DOM 的属性值
  3. removeAttribute:移除属性
  • 事件操作:给元素注册事件, 采取 事件源.事件类型 = 事件处理程序

1.改变元素内容(获取或设置)

  • innerText 和 innerHTML的区别:
  1. 获取内容时的区别:​ innerText 会去除空格和换行,而 innerHTML 会保留空格和换行
  2. 设置内容时的区别:​ innerText 不会识别 html,而 innerHTML 会识别
  3. 这两个属性是 可读写的 ,可以获取元素内容、改写元素内容
  4. 语法格式:element.innerText 、element.innerHTML (W3C标准)
<body>
    <div></div>
    <p>
        我是特特喜欢的人
        <span>喵喵喵</span>
    </p>
    <script>
        // 1. innerText 不识别html标签 去除空格和换行 忽略标签
        // 2. innerHTML 识别html标签 保留空格和换行 保留标签 W3C标准
        var div = document.querySelector('div');
        div.innerHTML = '<strong>今年是:</strong> 2020';

        // 这两个属性是可读写的  可以获取元素里面的内容 
        var p = document.querySelector('p');
        console.log(p.innerText);
        console.log(p.innerHTML);
    </script>
</body>

2.普通元素的属性操作

  • 获取属性的值:元素对象.属性名
  • 设置属性的值:元素对象.属性名 =  值

分时问候分析

  1. 得到当前的小时数 需要 Date 内置对象
  2. 判断小时数改变图片和文字信息
    <img src="images/s.gif" alt="">
    <div> 上午好,好好写代码 </div>
    <script>
        var img = document.querySelector('img');
        var div = document.querySelector('div');
        // 得到当前的小时数 需要 Date 内置对象
        var date = new Date();
        var h = date.getHours();
        // 判断小时数改变图片和文字信息
        if (h < 12) {
            img.src = 'images/s.gif';
            div.innerHTML = '上午好,好好写代码';
        } else if (h < 18) {
            img.src = 'images/x.gif';
            div.innerHTML = '下午好,好好写代码';
        } else {
            img.src = 'images/w.gif';
            div.innerHTML = '晚上好,好好写代码';
        }
    </script>

3.表单元素的属性操作

  • 表单中,这些属性的值是布尔型:disabled、checked、selected。
  • 表单里面的值、文字内容是通过 value 来修改的。
  • this 指向的是:事件函数的调用者。
  • 点击一次,改变输入框内容,并禁用按钮示例:
        var btn = document.querySelector('button');
        var input = document.querySelector('input');
        btn.onclick = function() {
            // 表单里面的值 文字内容是通过 value 来修改的
            input.value = '被点击了';
            // 如果想要某个表单被禁用 如单击一次后 这个按钮不能再点击 disabled 
            this.disabled = true;    // this 指向的是事件函数的调用者 btn
        }

仿京东显示密码分析

  • 核心思路: 点击眼睛按钮,把密码框类型改为文本框就可以看见里面的密码
  • 一个按钮两个状态,点击一次,切换为文本框,继续点击一次切换为密码框
  • flag :用于确定 现在输入的密码是否可见,每次更换完图片 和 密码框显示方式 之后,都要 记得重置 flag

    <div class="box">
        <img src="images/close.png" alt="" id="eye">
        <input type="password" name="" id="pwd">
    </div>
    <script>
        var eye = document.getElementById('eye');    // 点击眼睛按钮 决定密码是否显示
        var pwd = document.getElementById('pwd');    // 操作密码文本框显示方式是 pwd 还是 text
        var flag = 0;    // 用于判断眼睛是否睁开
        eye.onclick = function() {
            if (flag == 0) {
                pwd.type = 'text';    //眼睛是睁开的 那么密码框 变成 text 类型 使得密码显示
                eye.src = 'images/open.png';
                flag = 1;    // 改变眼睛状态
            } else {
                pwd.type = 'password';    
                eye.src = 'images/close.png';
                flag = 0;    // 改变眼睛状态
            } }

4.样式属性操作

  • element.style 行内样式操作:适合于 样式较少 或 功能简单 的情况
  • element.className 类名样式操作:适合于 样式较多 或 功能复杂 的情况

4.1 element.style 行内样式操作

  • 元素对象的 style 属性也是一个对象!
  • JS 的样式采取 驼峰命名法 比如 fontSize、 backgroundColor

4.1.1 淘宝点击关闭二维码:

  • 核心思路:通过点击 关闭按钮后,改变 装有二维码的盒子的 显示方式:box.style.display = 'none';
        var btn = document.querySelector('.close-btn');    // 关闭符号
        var box = document.querySelector('.box');    // 装二维码的盒子
        // 给按钮添加点击事件 单击后 二维码盒子 box 的显示方式为 display = 'none'
        btn.onclick = function() {
            box.style.display = 'none';
        }

4.1.2 循环精灵图背景:

  • 核心思路: 利用 for循环 修改精灵图片的 背景位置 background-position
  • 遍历获取 li 伪数组,计算每个 li 在精灵图中的 y 坐标(索引号*44)
        var lis = document.querySelectorAll('li');    // 此时 li 是 伪数组
        for (var i = 0; i < lis.length; i++) {
            // 索引号 乘以 44 是每个li 的背景 y 坐标
            var index = i * 44;
            lis[i].style.backgroundPosition = '0 -' + index + 'px';    // 记得 背景驼峰命名 负数 单位px
        }

4.1.3 显示隐藏文本框内容:        

  • 当鼠标点击文本框时,里面的默认文字隐藏,当鼠标离开文本框时,里面的文字显示。
  • 获得焦点事件 onfocus / 失去焦点事件 onblur
  • 获得焦点, 判断表单内容是否为默认文字,如果是,就清空表单内容
  • 失去焦点, 判断表单内容是否为空,如果为空,则表单内容改为默认文字
    <input type="text" value="手机">
    <script>
        var text = document.querySelector('input');
        // 获得焦点事件 onfocus
        text.onfocus = function() {
                if (this.value === '手机') {    // 如果输入框中有'手机' 那么将输入框内容清空
                    this.value = '';}
                this.style.color = '#333';    // 获得焦点需要把文本框里面的文字颜色变黑
            }
        // 失去焦点事件 onblur
        text.onblur = function() {
            if (this.value === '') {    // 失去焦点后 如果输入框内没有内容 那么补充默认输入框内容
                this.value = '手机';
            }
            this.style.color = '#999';    // 失去焦点需要把文本框里面的文字颜色变浅色
        }
</script>

4.2 element.className 类名样式操作

  • className 更改元素的类名,会覆盖原先的类名
        // 1. 使用 element.style 获得修改元素样式  如果样式比较少 或者 功能简单的情况下使用
        var test = document.querySelector('div');
        test.onclick = function() {    // 下面的代码十分繁琐
            // this.style.backgroundColor = 'purple';
            // this.style.color = '#fff';
            // this.style.fontSize = '25px';
            // this.style.marginTop = '100px';
            // 2. 通过 className 更改元素的样式 适合于 样式较多 或 功能复杂 的情况
            // 3. 就是 将 修改后的样式 写在 css 中,更改类名 即可 更改样式
            this.className = 'first change';}

4.2.1 密码框格式提示错误信息:

  • 对失去焦点事件进行注册:判断密码输入是否合法,是在 密码输入框 失去焦点之后 开始判断的 
  • 输入长度不合法时,更改样式很多,故采取 className 的形式,在 css 中写修改样式,在 js 中引用对应样式。
        var ipt = document.querySelector('.ipt');    
        var message = document.querySelector('.message');    
        ipt.onblur = function() {    // 表单失去焦点后 对输入长度进行判断
            // 根据表单里面值的长度 ipt.value.length 
            if (this.value.length < 6 || this.value.length > 16) {    // 输入不合法
                message.className = 'message wrong';
                message.innerHTML = '请输入有效长度!要求 6~16 位';
            } else {    // 输入合法
                message.className = 'message right';
                message.innerHTML = '输入合法!';
            } }

5.自定义属性操作

5.1 自定义属性简介

  • 自定义属性:程序员自己添加的属性,多用 index
  • 自定义属性目的:为了保存并使用数据 有些数据可以保存到页面中,而不用保存到数据库中
  • 自定义属性获取是通过 getAttribute(‘ 属性 ’) 获取

5.1.1 获取属性:

  • element.属性= '值';:获取内置属性值(元素本身自带的属性)
  • element.getAttribute('属性');:获取程序员自定义的属性

5.1.2 设置属性值:

  • (1) element.属性= '值';:设置内置属性值。
  • (2) element.setAttribute('属性', '值'); : 主要针对于自定义属性

5.1.3 移除属性:

  • removeAttribute(属性);  
        console.log(div.id);    // 获取属性
        console.log(div.getAttribute('index'));    // 获取属性
        div.setAttribute('index', 2);    // 设置属性
        div.setAttribute('class', 'footer');    // 设置属性 class 不是 className 
        div.removeAttribute('index');    // 移除属性

5.2 tab 栏(重点)

  • 获取元素:上方选项卡,下方每个选项卡的显示内容
  • 注册事件:给选项卡注册点击事件,当点击某个选项卡时,对应的显示内容就会显示,其他显示内容隐藏
  • 遍历选项卡元素对象,给每一个元素对象添加自定义属性 index;注册选项卡点击事件,利用排他思想,遍历清除所有选项卡样式,单独给点击的选项卡设置样式;获得之前设置的自定义属性,利用排他思想,遍历将所有 显示内容模块 隐藏起来,单独给点击的索引号和选项卡相同的 显示内容模块 设置成可以显示
  • 效果展示:
        var tab_list = document.querySelector('.tab_list');
        var lis = tab_list.querySelectorAll('li');
        var items = document.querySelectorAll('.item');
        for (var i = 0; i < lis.length; i++) {        // for循环,给 选项卡 绑定点击事件
            // 0.开始给5个小li 设置索引号 
            lis[i].setAttribute('index', i);
            lis[i].onclick = function() {
                // 1. 当前这一个选项卡 底色会是红色,其余不变(排他思想)
                // 干掉所有人 全部的li清除 class 这个类
                for (var i = 0; i < lis.length; i++) {    // 是 选项卡 的长度
                    lis[i].className = '';
                }
                // 留下我自己 给当前li增加 被选中的类的样式
                this.className = 'current';
                // 2. 下面的 item 显示内容模块
                var index = this.getAttribute('index');
                // 干掉所有人 让其余的 item 隐藏
                for (var i = 0; i < items.length; i++) {    // 是 item显示内容模块 的长度
                    items[i].style.display = 'none';
                }
                // 留下我自己 让对应的item 显示出来
                items[index].style.display = 'block';
            }}

5.3 H5 新增自定义属性

  • 有些自定义属性很容易引起歧义,不容易判断是 元素的内置属性 还是 自定义属性。
  • H5 新增的 自定义属性方法 只能 data-开头,如:element.setAttribute(‘data-index’, 2)
  • 兼容性获取 :element.getAttribute(‘data-index’);
  • dataset 是一个集合,存放了所有以 data 开头的自定义属性
  • H5新增 element.dataset .index 或者 element.dataset [‘index’]  , IE11 才开始支持

六.排他操作

排他思想

  • 同一组元素,想要某一个元素实现某种样式, 需要用到循环的 排他思想 算法:
  1. 所有元素全部清除样式(干掉其他人)
  2. 给当前元素设置样式 (留下我自己)
  3. 注意 顺序不能颠倒先干掉其他人,再设置自己
  • 被点击的按钮变色 其余按钮不变色(排他思想):
  • 两层 for 循环,第一层用来给所有元素对象注册点击事件,第二层用来"干掉其他人"
        var btns = document.getElementsByTagName('button');// 获取 按钮伪数组
        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';
            }}

百度换肤:

  • 通过两步 查询到需要的元素:document.querySelector('.baidu').querySelectorAll('img')
  • 循环遍历 图片列表,注册点击事件,将页面背景 更改为 遍历的图片地址 this.src
    <ul class="baidu">
        <li><img src="images/1.jpg"></li>
        // ...四张不同的背景图片
    </ul>
    <script>
        var imgs = document.querySelector('.baidu').querySelectorAll('img');
        for (var i = 0; i < imgs.length; i++) {    // 循环注册点击事件
            imgs[i].onclick = function() { 
                document.body.style.backgroundImage = 'url(' + this.src + ')';    // 把 this.src 给 body
            }
        }
    </script>

表格隔行变色:

  • 用到新的鼠标事件 鼠标经过 onmouseover 鼠标离开 onmouseout
  • 核心思路:鼠标经过 tr 行,当前的行变背景颜色, 鼠标离开去掉当前的背景颜色
  • 注意: 第一行(thead里面的行)不需要变换颜色,因此我们获取的是 tbody 里面的行
        // 1.获取元素 获取的是 tbody 里面所有的行
        var trs = document.querySelector('tbody').querySelectorAll('tr');
        // 2. 利用循环绑定注册事件
        for (var i = 0; i < trs.length; i++) {
            trs[i].onmouseover = function() {    // 3. 鼠标经过事件 onmouseover
                    this.className = 'bg';}
            trs[i].onmouseout = function() {    // 4. 鼠标离开事件 onmouseout
                this.className = '';
            }}

全选反选:

  • 思路:让所有复选框的 checked 属性(选中状态)跟随 全选按钮 选中 或 取消
  • 全选按钮注册事件:遍历其他按钮,使其他按钮的 checked 状态 = 全选按钮的 checked 状态
  • 其他复选注册事件:循环给其他复选框注册点击事件,设置flag(t / f)用于指定全选按钮是否选中,循环判断每一个复选框是否被选中,若存在未被选中的复选框,则更改flag,并跳出当前循环(不需要判断剩下的),让全选按钮状态跟随 flag
        var j_cbAll = document.getElementById('j_cbAll'); // 全选按钮
        var j_tbs = document.getElementById('j_tb').getElementsByTagName('input'); // 下面所有的复选框
        j_cbAll.onclick = function() {
                // this.checked 它可以得到当前复选框的选中状态如果是true 就是选中,如果是false 就是未选中
                console.log(this.checked);
                for (var i = 0; i < j_tbs.length; i++) {
                    j_tbs[i].checked = this.checked;
                }}
            // 2. 下面复选框需要全部选中, 上面全选才能选中做法: 给下面所有复选框绑定点击事件,每次点击,都要循环查看下面所有的复选框是否有没选中的,如果有一个没选中的, 上面全选就不选中。
        for (var i = 0; i < j_tbs.length; i++) {
            j_tbs[i].onclick = function() { 
                var flag = true;    // flag 控制全选按钮是否选中
                // 每次点击下面的复选框都要循环检查者4个小按钮是否全被选中
                for (var i = 0; i < j_tbs.length; i++) {
                    if (!j_tbs[i].checked) {
                        flag = false;
                        break; // 退出for循环 这样可以提高执行效率 因为只要有一个没有选中,剩下的就无需循环判断了
                    }}
                j_cbAll.checked = flag;
            }}

七.节点操作

  • 获取元素的两种方式:
  1. 利用 DOM 提供的方法获取元素:document.getElementBy......逻辑性不强、繁琐
  2. 利用节点层级关系获取元素:逻辑性强, 但兼容性差

1.节点概述

  • ​网页中所有内容都是节点(标签、属性、文本、注释等),DOM 中,节点用 node 来表示。
  • 节点均可通过 JavaScript 进行访问、修改、创建或删除。
  • 节点至少拥有nodeType(节点类型)、nodeName(节点名称)和 nodeValue(节点值)三个基本属性。
  • 关于 nodeType:
  1. 元素节点 nodeType 为 1 (节点操作主要操作元素节点)
  2. 属性节点 nodeType 为 2
  3. 文本节点 nodeType 为 3 (文本节点包含文字、空格、换行等)

2.节点层级

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

2.1 父级节点

  • 格式:node.parentNode
  • 得到的是 离元素 最近 的父级节点(亲爸爸)
  • 如果找不到父节点,就返回为 null
    <div class="demo">
        <div class="box">
            <span class="erweima">×</span>
        </div>
    </div>
        // 得到的是 离元素最近的父级节点(亲爸爸) 如果找不到父节点就返回为 null
        var erweima = document.querySelector('.erweima'); 
        console.log(erweima.parentNode);    // box

2.2 子节点

  • parentNode.childNodes(标准):获取的子节点集合 包含文本节点 + 元素节点
  • parentNode.children(非标准 但开发最常用):获取的是子元素节点(子节点除掉文本节点)
    <ul>
        <li> 我是li </li>    // 4 
    </ul>

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

        // 1. 子节点  childNodes 所有的子节点 包含 元素节点 文本节点等等
        console.log(ul.childNodes);    // 会有 9 个孩子节点 因为文本节点(回车)+元素节点(li)
        console.log(ul.childNodes[0].nodeType);    // 第一个子节点 是 文本节点(回车)
        console.log(ul.childNodes[1].nodeType);    // 第二个子节点 是 元素节点(li)

        // 2. children 获取所有的子元素节点(就是自动去除回车之类的子节点) 实际开发常用的
        console.log(ul.children);
  • parentNode.firstElementChild / parentNode.lastElementChild  :不管是 文本节点 还是 元素节点
  • parentNode.firstElementChild / parentNode.lastElementChild 有兼容性问题,IE9才支持
  • parentNode.chilren[0] :实际开发的写法  既没有兼容性问题 又返回第一个子元素
  • 返回最后一个子元素:parentNode.chilren[parentNode.chilren.length - 1]
       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]);

新浪下拉菜单:

  • 核心原理: 当鼠标经过 li 里面的 第二个孩子 ul 显示, 当鼠标离开,则 ul 隐藏
        var nav = document.querySelector('.nav');
        var lis = nav.children;     // 得到 4 个小 li
        for (var i = 0; i < lis.length; i++) {
            lis[i].onmouseover = function() {    
                this.children[1].style.display = 'block';
                }
            lis[i].onmouseout = function() {
                this.children[1].style.display = 'none';
                }}

2.3 兄弟节点

  • node.nextSibling / node.previousSibling:下一个兄弟节点 包含元素节点或者 文本节点等等
  • node.nextElementSibling / node.previousElementSibling :下一个兄弟元素节点,兼容性问题, IE9 以上支持
  • 解决兼容性问题:
 function getNextElementSibling(element) {
             var el = element;
             while (el = el.nextSibling) {
                         if (el.nodeType === 1) {
                                 return el;    }}
                             return null; }

3.创建 / 添加节点

  • 想要页面添加一个新的元素 : 1. 创建元素 2. 添加元素
  • 创建元素节点:document.createElement('tagName')
  • 添加节点方法1:node.appendChild(child),添加到末尾
  • 添加节点方法2:node.insertBefore(child, 指定元素位置),添加到前面
    <ul>
        <li> 特特喜欢茶茶 </li>
    </ul>
    <script>
        // 0. 想要页面添加一个新的元素 : 1. 创建元素 2. 添加元素
        // 1. 创建元素节点
        var li = document.createElement('li');
        var lili = document.createElement('li');
        var ul = document.querySelector('ul');
        // 2. 添加节点方法1 node.appendChild(child)  node 是父级  child 是子级 后面追加元素
        ul.appendChild(li);
        // 3. 添加节点方法2 node.insertBefore(child, 指定元素的前面);
        ul.insertBefore(lili, ul.children[0]);
    </script>

4.删除节点

  • 删除元素节点 node.removeChild(child):DOM 中删除一个子节点,返回删除的节点
       // 点击按钮 依次删除 父节点 里面的孩子
        btn.onclick = function() {
            if (ul.children.length == 0) {    // 如果孩子已经被删完(长度为 0)
                this.disabled = true;    // 那么按钮禁用
            } else {
                ul.removeChild(ul.children[0]);    // 删除第一个孩子
            }}

发布留言板信息(创建、添加、删除节点)

  • 点击按钮后,将留言发布到最前面,因此给 发布按钮 添加点击事件
  • 添加点击事件时,先判断文本框是否为空,若为空,则弹出提示框
  • 若文本框不是空,创建节点 li 并给他赋值(输入的文本+删除链接a),在父元素前面添加节点 li
  • 获取在前面添加的删除链接 a,遍历 a 并给他注册点击删除事件
  • 点击 a 删除的是 li ,是 ul 删除 li,也就是 ul 删除 a 的父节点:ul.removeChild(this.parentNode);(必须这么写)
  • a href='javascript:;':表示 a 被点击之后 不会跳转到其他页面,网页地址栏不会改变
    <textarea name="" id=""> 请输入留言内容 </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 {
                // (1) 创建元素 给 li 赋值 添加输入的文本内容+删除链接
                var li = document.createElement('li');    
                li.innerHTML = text.value + "<a href='javascript:;'> 删除 </a>";
                // (2) 添加元素 li 在创建并赋值之后 才会追加给 ul 最新留言显示在最前面
                ul.insertBefore(li, ul.children[0]);
                // (3) 删除元素 删除的是当前链接的li  a的父亲
                var as = document.querySelectorAll('a');    // 点击发布按钮 才会创建 a
                for (var i = 0; i < as.length; i++) {    // 不确定有几个 a 遍历 a 添加点击事件
                    as[i].onclick = function() {
                        // ul 删除 li,而 li 指的是 当前 a 所在的li  this.parentNode;
                        ul.removeChild(this.parentNode);
                    }}}}
    </script>

5.复制节点

  • node.cloneNode(); 括号为空或者里面是 false:浅拷贝 只复制标签不复制里面的内容
  • node.cloneNode(true); 括号里面是 true:深拷贝 复制标签复制里面的内容

动态生成表格

  • 将学生数据存储在 对象数组 
  • 获取 tbody 元素,循环遍历对象个数,向其中创建并添加对应数目的 tr 行元素
  • 在 tr 中遍历对象属性,创建 td 并向 td 中赋值 对象数组的属性 data[i][k],将 td 追加到 tr 中
  • 创建 删除链接 单元格,添加删除链接,并将这个 td 追加到 tr 后
  • 获取 刚刚的 删除链接,循环遍历添加 点击事件:表格删除行,也就是 a 的父亲 td 的父亲 tr(tbody.removeChild(this.parentNode.parentNode)

        // 1.准备好学生的数据 对象数组
        var datas = [{
            name: '魏璎珞',
            subject: 'JavaScript',
            score: 100
        }, // ...
            {
            name: '弘历',
            subject: 'JavaScript',
            score: 98
        }];
        // 2. 往 tbody 里面创建行: 有几个人(通过数组的长度)就创建几行
        var tbody = document.querySelector('tbody');
        for (var i = 0; i < datas.length; i++) { 
            // 1. 创建行 tr 
            var tr = document.createElement('tr');
            tbody.appendChild(tr);
            // 2. 行里面创建单元格 td 单元格的数量取决于每个对象里面的属性个数  
            for (var k in datas[i]) {    // 使用for in遍历学生对象 k 得属性名    obj[k] 得属性值  
                // 创建单元格 td
                var td = document.createElement('td');
                // 给单元格赋值 把对象里面的属性值 datas[i][k] 给 td  
                td.innerHTML = datas[i][k];
                tr.appendChild(td);    // 给行里追加单元格
            }
            // 3. 创建有删除2个字的单元格 
            var td = document.createElement('td');
            td.innerHTML = '<a href="javascript:;"> 删除 </a>';
            tr.appendChild(td);
        }
        // 4. 删除操作 
        var as = document.querySelectorAll('a');    // 获取前面的删除链接 a
        for (var i = 0; i < as.length; i++) {    // 循环遍历 a 给他注册点击事件
            as[i].onclick = function() {
                // 点击 a 删除 当前 a 所在的行(链接a的爸爸td的爸爸tr)  
                tbody.removeChild(this.parentNode.parentNode)
            }}

6.创建元素的三种方式

  • document.write() 创建元素 :将内容写入页面的内容流,但是文档流执行完毕,会导致 页面重绘
  • element.innerHTML 创建元素:将内容写入某个 DOM 节点,不会导致 页面重绘,创建多个元素效率更高(不要拼接字符串,采取数组形式拼接),结构复杂
  • document.createElement() 创建元素:创建多个元素效率稍低一点,但是结构更清晰
  • 总结:不同浏览器下,innerHTML 效率要比 creatElement 高
        // 1. document.write() 创建元素  如果页面文档流加载完毕,再调用这句话会导致页面重绘
         var btn = document.querySelector('button');
         btn.onclick = function() {
             document.write('<div>123</div>');}
        // 2. innerHTML 创建元素
        var inner = document.querySelector('.inner');
        // innerHTML 拼接字符串方式
        // for (var i = 0; i <= 100; i++) {
        //     inner.innerHTML += '<a href="#">百度</a>'
        // }
        // innerHTML 数组追加方式
        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');
        for (var i = 0; i <= 100; i++) {
            var a = document.createElement('a');
            create.appendChild(a);     }

 

八.事件高级

1.注册事件(绑定事件)

  • 传统注册事件:同一个元素同一个事件 只可以写一个处理函数 否则 后面注册的处理函数 覆盖 前面的
  • 方法监听注册事件:同一个元素同一个事件 可以添加多个侦听器(事件处理程序)
  • 传统注册方式 利用 on 开头的事件 onclick
  • 方法监听注册方式:addEventListener() ,w3c 标准,IE9 之前不支持

2.事件监听

2.1 addEventListener('事件类型',事件处理函数,useCapture)

  • type:事件类型字符串,比如 click 、mouseover ,这里不要带 on
  • listener:事件处理函数,事件发生时,会调用该监听函数
  • useCapture:可选参数,是一个布尔值,默认是 false,在 事件冒泡时 调用时间处理函数。

2.2 attacheEvent(eventNameWithOn, callback)(IE678 支持)

  • eventNameWithOn:事件类型字符串,比如 onclick 、onmouseover ,这里要带 on
  • callback: 事件处理函数,当目标触发事件时回调函数被调用
<button> 传统注册事件 onclick </button>
<button> 方法监听注册事件 addEventListener </button>
<button> ie678 attachEvent </button>
<script>
    var btns = document.querySelectorAll('button');
    // 1. 传统方式注册事件 同一个元素 只可以写一个事件 否则 后面的事件覆盖前面的事件
    btns[0].onclick = function() {
        alert('hi');
    }
   // 2. 事件侦听注册事件 addEventListener 
   // (1) 里面的 事件类型 是字符串 必加引号 而且不带on
   // (2) 同一个元素 同一个事件可以添加多个侦听器(事件处理程序)
    btns[1].addEventListener('click', function() {
        alert('特特');
    })
    btns[1].addEventListener('click', function() {
            alert('我和 特特 可以同时出现哦');
    })
    // 3. attachEvent ie9 以前的版本支持 带 on
    btns[2].attachEvent('onclick', function() {
        alert(11);
    })
</script>

2.3 事件监听兼容性解决方案

  • 兼容性处理的原则: 首先照顾大多数浏览器,再处理特殊浏览器
function addEventListener(element, eventName, fn) {
         if (element.addEventListener) { // 判断当前浏览器是否支持 addEventListener 方法
                 element.addEventListener(eventName, fn); // 第三个参数 默认是false
         } else if (element.attachEvent) {
                 element.attachEvent('on' + eventName, fn);
         } else {
                 element['on' + eventName] = fn; // 相当于 element.onclick = fn;    }

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

3.1 传统删除方式

  • eventTarget.onclick = null;

3.2 方法监听删除方式

  • eventTarget.removeEventListener(推荐)
  • eventTarget.detachEvent();(IE9 前使用)
    <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;    // 执行过一遍 对话框弹出之后 将不会再触发点击事件
        }
        // 2. removeEventListener 删除事件 IE9以后
        divs[1].addEventListener('click', fn) // 里面的fn 不需要调用加小括号
        function fn() {
            alert(22);
            divs[1].removeEventListener('click', fn);
        }
        // 3. detachEvent ie678
        divs[2].attachEvent('onclick', fn1);
        function fn1() {
            alert(33);
            divs[2].detachEvent('onclick', fn1);
        }
    </script>

3.3 删除事件兼容性解决方案 

function removeEventListener(element, eventName, fn) {
        if (element.removeEventListener) { // 判断当前浏览器是否支持 removeEventListener 方法
             element.removeEventListener(eventName, fn); // 第三个参数 默认是false
         } else if (element.detachEvent) {
             element.detachEvent('on' + eventName, fn);
         } else {
             element['on' + eventName] = null;}

4.DOM 事件流

4.1 DOM 事件流 定义

  • DOM 事件流 :事件发生时会在 元素节点之间 按照 特定顺序 传播。 
  • 事件冒泡: IE 最早提出,事件开始由最具体的元素接收,然后逐级 向上传播 到 DOM 最顶层节点的过程。
  • 事件捕获: 网景最早提出,由 DOM 最顶层节点开始,然后逐级 向下传播 到最具体的元素接收的过程。
  • html 中的标签是相互嵌套的,将元素想象成一个盒子装一个盒子,document 是最外面的大盒子。
  • 给页面中的一个 div 注册单击事件,单击 div 时,也单击了 body,单击了 html,单击了 document。
  • JS 代码中只能执行 捕获或者冒泡 其中的一个阶段。
  • onclick 和 attachEvent 只能得到 冒泡阶段。
  • addEventListener()第三个参数: true 事件捕获阶段调用事件处理程序; false(默认) 事件冒泡阶段...
  • 实际开发中很少使用事件捕获,更关注事件冒泡。
  • 有些事件是没有冒泡的,比如 onblur、onfocus、onmouseenter、onmouseleave

4.2 DOM 事件流经历3个阶段

  • 捕获阶段
  • 当前目标阶段
  • 冒泡阶段

4.3 事件冒泡

  • 冒泡阶段:addEventListener('事件类型',调用函数,false/省略) 第三个参数是 false 或者 省略 
  • 对话框弹出顺序:son -> father ->body -> html -> document
    <div class="father">
        <div class="son"> son盒子 </div>
    </div>
        // ...
        // onclick 和 attachEvent(ie) 在冒泡阶段触发
        // 冒泡阶段 如果 addEventListener 第三个参数是 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);
		// 给document注册单击事件,省略第3个参数
        document.addEventListener('click', function() {
            alert('document');
        })

4.4 事件捕获

  • 捕获阶段:addEventListener('事件类型',调用函数,true) 第三个参数是 true
  • 对话框弹出顺序:document -> html -> body -> father -> son
   <div class="father">
        <div class="son">son盒子</div>
    </div>
    // ...
        // 如果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);
        document.addEventListener('click', function() {
            alert('document');
        }, true)

5.事件对象

5.1 事件对象定义

  • 事件发生后,跟事件相关的 信息数据集合 都放到 一个对象 里面,这个对象就是事件对象 event。
  • 比如:鼠标触发事件的话,会得到鼠标的相关信息,如鼠标位置;
  • 比如:键盘触发事件的话,会得到键盘的相关信息,如按了哪个键。

5.2 事件对象的使用

  • 事件触发时 就会产生事件对象,系统会将事件对象以 实参 的形式传给 事件处理函数中的 形参
  • 所以,在 事件处理函数中 声明一个 形参(event,也可以是其他名字) 来接收 实参事件对象。
  • 注册事件时, event 对象就会被系统自动创建,并依次传递给事件监听器(事件处理函数)。
 eventTarget.onclick = function(event) {
 eventTarget.addEventListener('click', function(event) {}

5.3 事件对象的兼容性处理

  • IE6~8 中,浏览器不会给方法传递参数,如果需要的话,需要到 window.event 中获取查找。
  • 兼容性写法:e = e || window.event;
        var div = document.querySelector('div');
        div.onclick = function(e) {
                e = e || window.event;    // 事件对象
                console.log(e);    }

5.4 事件对象的属性和方法 

5.4.1 e.target 和 this 的区别:

  • this 是事件绑定的元素(绑定事件处理函数的元素) 。
  • e.target 是事件触发的元素(点击的那个元素)。
  • 一般情况下 target 和 this是一致的,但有一种情况不同:
  • 事件冒泡时(父、子元素有相同事件,单击子元素,父元素的事件处理函数也会被触发执行),
  • 这时候 this 指向的是父元素,因为它是绑定事件的元素对象,
  • 这时候 target 指向的是子元素,因为它是被点击的元素对象。
        var ul = document.querySelector('ul');
        ul.addEventListener('click', function(e) {    
              console.log(this); // ul 给 ul 绑定了事件 this 就指向 ul  
              console.log(e.target); // li  点击的是 li e.target 指向的就是 li    });

5.4.2 e.target 兼容性写法:e.target || e.srcElement;

        div.onclick = function(e) {
               e = e || window.event;
               var target = e.target || e.srcElement;
               console.log(target);    }

6.阻止默认行为

  • html 中一些标签有默认行为,例如 a 标签被单击后,默认会进行页面跳转,若想阻止默认行为,则
  • return false; //  阻止默认行为 没有兼容性问题,但是后面代码全部不执行
  •  e.preventDefault(); DOM 标准写法(推荐)
<a href="http://www.baidu.com"> 我会自动跳转哦 </a> // 阻止默认行为 让链接不跳转 提交按钮不提交
        //  DOM 标准写法
        var a = document.querySelector('a');
        a.addEventListener('click', function(e) {
             e.preventDefault(); });
        // 传统方式
        a.onclick = function(e) {          
            e.preventDefault();  // 普通浏览器 e.preventDefault();  方法
            e.returnValue = false;    // 低版本浏览器 IE678  returnValue  属性
            // 利用 return false 也能阻止默认行为 没有兼容性问题 但后面的代码不执行了 且只限于传统方式
            return false;
        }

7.阻止事件冒泡

  • 事件冒泡本身的特性,会带来坏处,参考下面,好处参考 事件委托 部分。
  • 标准写法: e.stopPropagation() 
  • 非标准写法:IE 6-8 e.cancelBubble = true;
   <div class="father">
        <div class="son" >son盒子 </div>
    </div>
    //...
        var son = document.querySelector('.son');
        son.addEventListener('click', function(e) {
            alert('son');
            e.stopPropagation(); // stop 停止  Propagation 传播
            window.event.cancelBubble = true; // 非标准 cancel 取消 bubble 泡泡
        }, false);

 7.1 阻止事件冒泡的兼容性处理

    if(e && e.stopPropagation){
             e.stopPropagation();
    }else{
             window.event.cancelBubble = true;    }

8.事件委托(代理、委派)

  • 事件委托也称为事件代理, 在 jQuery 里面称为事件委派。
  • ​事件委托原理:事件监听器设置在父节点上,利用冒泡原理影响设置每个子节点。
  • 事件委托作用:只操作一次 DOM ,提高程序的性能。
  • e.target :得到点击的对象
  <ul>
        <li> 特特喜欢茶茶 </li>    // ...五个 li
  </ul>
        // 事件委托原理:给父节点添加侦听器 利用 事件冒泡 影响每一个子节点
        var ul = document.querySelector('ul');
        ul.addEventListener('click', function(e) {
            // e.target 这个可以得到我们点击的对象
            e.target.style.backgroundColor = 'pink';        })

九.常用鼠标事件

1.禁止选中文字和禁止右键菜单

  • contextmenu 右键菜单事件
  • selectstart 选中文字 
        // 1. contextmenu 右键菜单事件
        document.addEventListener('contextmenu', function(e) {    // 是给文档添加的侦听器
                e.preventDefault();    // 禁用 默认右键菜单效果
            })
        // 2. selectstart 选中文字 
        document.addEventListener('selectstart', function(e) {    // 是给文档添加的侦听器
            e.preventDefault();    // 禁用 默认选中文字效果
        })

2.获取鼠标在页面的坐标

  • event 对象 代表跟事件相关的一系列信息的集合
  • 鼠标事件对象 MouseEvent ,键盘事件对象 KeyboardEvent
  • e.clientX/Y 鼠标在 浏览器窗口可视区的 x 和 y 坐标
  • e.pageX/Y 鼠标在 页面文档的 x 和 y 坐标(IE9+支持)
  • e.screenX/Y 鼠标在 电脑屏幕的 x 和 y 坐标

跟随鼠标的天使

  • mousemove 鼠标移动 1px 就会触发事件
  • 核心原理: 鼠标移动获得最新的坐标, 把 x 和 y 坐标做为图片的 top 和 left 值 就可以移动图片
  • 千万不要忘记给 left 和 top 添加 px 单位
    <img src="images/angel.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>

十.常用键盘事件

1.键盘事件

  • keyup 按键 弹起的时候 触发 
  • keydown 按键 按下的时候 触发  能识别功能键 比如 ctrl shift 左右箭头
  • keypress 按键 按下的时候 触发  不能识别功能键 比如 ctrl shift 左右箭头
  • 三个事件的执行顺序:  keydown(能识别功能键) -- keypress(不能识别功能键) -- keyup(弹起)
<body>
    <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>

2.键盘事件对象

  • 键盘事件对象中的 keyCode属性 得到相应键的 ASCII码值 进而判断按键
  • keyup 和 keydown 不区分字母大小写  a 和 A 得到的都是65
  • keypress 区分字母大小写  a 97  A 65
       document.addEventListener('keyup', function(e) {    // 鼠标弹起后才能判断ASCII码值
            console.log('up:' + e.keyCode);
            if (e.keyCode === 65) {    // 用 keycode 返回 ASCII码值 来判断用户按键
                alert('您按下的a键');
            } else {
                alert('您没有按下a键')     }})

模拟京东按键输入内容

  • 核心思路: 检测用户是否按下了 s 键,如果按下 s 键,就把光标定位到搜索框里面
  • 用键盘事件对象里面的 keyCode 判断用户按下的是否是 s 键
  • 搜索框获得焦点:focus() 方法
  • keydown 和 keypress 这两个事件触发时,文字还没落入文本框中,keyup 事件触发时, 文字已经落入文本框
<body>
    <input type="text">
    <script>
        var search = document.querySelector('input');
        document.addEventListener('keyup', function(e) {
            if (e.keyCode === 83) {
                search.focus();    // 如果按下了 s 就让光标定到搜索框
            }
        })
    </script>
</body>

模拟京东快递单号查询

  • 输入框输入内容时, 上面的大号字体盒子(con)显示的字号更大
  • 表单检测输入: 给表单添加 键盘keyup 事件,若输入内容,大字体盒子显示,同时把输入框的值赋值给 con 盒子,如果输入框内容为空,则隐藏大号字体盒子
  • 若输入框失去焦点,则大字体盒子不会显示,若输入框获得焦点且内容不为空,则大字体盒子显示
    <div class="search">
        <div class="con"> 这里显示更大的字号 </div>
        <input type="text" placeholder="请输入您的快递单号" class="jd">
    </div>
        //...
        var con = document.querySelector('.con');
        var jd_input = document.querySelector('.jd'); 
        jd_input.addEventListener('blur', function() {    // 失去焦点,就隐藏 con 盒子
                con.style.display = 'none';    })

        jd_input.addEventListener('focus', function() {
            if (this.value !== '') {    // 获得焦点,且输入框内容不为空时,就显示 con 盒子
                con.style.display = 'block';     }})

        jd_input.addEventListener('keyup', function() {    // 当键盘弹起 表示输入完成 
                if (this.value == '') {
                    con.style.display = 'none';    // 如果没有输入内容 大盒子就不会出现
                } else {
                    con.style.display = 'block';    // 如果输入了内容 大盒子出现 并被赋值
                    con.innerText = this.value;     }})
发布了29 篇原创文章 · 获赞 21 · 访问量 1651

猜你喜欢

转载自blog.csdn.net/Lyrelion/article/details/105064005