js DOM

JS基础:js入门基础戳这里

三、Web APIs 简介
1、Web APIs 和 JS基础 的关联性
JS的组成

js分为ECMAScript(JavaScript语法) DOM(页面文档对象模型) BOM(浏览器对象模型)

其中,ECMAScript 是js基础

DOM 和 BOM属于Web APIs

  • JS基础阶段

    学习的是ECMAScript标准规定的基本语法,但还无法做出网页交互效果

  • Web APIs阶段

    是W3C组织的标准,是JS所独有的部分;主要学习页面交互功能

2、API和Web API
(1)API

API(Application Programming Interface,应用程序编程接口)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。

简答理解就是,API是给程序员提供的一种工具,以便能更轻松地实现想要完成的功能。

(2)Web API

Web API 是浏览器提供的一套操作浏览器功能页面元素的API(BOMDOM)

现阶段我们主要针对于浏览器常用的API,做交互效果。

3、DOM
(1)DOM简介

文档对象模型(Document Object Model,简称DOM),是W3C组织推荐的处理可拓展标记语言(html或者xml)的标准编程接口。通过这些接口可以改变网页的内容、结构、样式。

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

DOM把以上内容都看作是对象

(3)获取元素
  1. 通过ID获取

    // 1.根据ID获取
            var timer = document.getElementById('time')
            console.log(timer);
            console.log(typeof timer);  //返回object 
            // console.dir 打印返回的元素对象  更好的查看里面的属性和方法
            console.dir(timer);
    
    • 返回对象为object类型
    • 使用console.dir() 可以打印返回的元素对象 更好的查看里面的属性和方法
  2. 通过标签名获取

    // 2.根据标签名获取
            var lis = document.getElementsByTagName('li');
            // 返回的是 获取过来元素对象的集合 以伪数组形式存储
            console.log(lis);
            console.log(lis[0]);
            for (var i = 0; i < lis.length; i++) {
                console.log(lis[i]);
            }
    
    // 通过父元素获取
            // (1)
            var ol = document.getElementsByTagName('ol');
            //注意这里使用ol[0]是为了获取到ol父元素,如果只是用ol的话,表示的是包含ol的伪数组
            console.log(ol[0].getElementsByTagName('li'));
            // (2) 实际开发中,通常给ol(父元素)一个id 进行获取
            var ol = document.getElementById('ol');
            console.log(ol.getElementsByTagName('li'));
    
    • 返回的是 获取过来元素对象的集合 以伪数组形式存储

    • 如果页面中只有一个li 返回的还是伪数组形式(只有一个元素)

    • 如果页面中没有元素 返回的仍然是伪数组形式(空的)

    • 还可以获取某个元素(父元素)内部所有指定标签名的子元素

      父元素必须是单个对象(必须指明是哪一个元素对象),获取的时候不包括父元素自己

  3. H5新增

    // 3.通过HTML5 新增方法进行获取
            // (1)通过类名获取某些元素集合
            var boxs = document.getElementsByClassName('box');
            console.log(boxs);
            // (2)querySelector 返回指定选择器的第一个元素对象
            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 allNav = document.querySelectorAll('#nav');
            console.log(allNav);
            var lis = document.querySelectorAll('li');
            console.log(lis);
    
    • querySelector 返回的是指定选择器的第一个元素对象
    • 切记!!里面的选择器一定要加符号 如:.box #nav
(4)获取特殊元素
		// 获取body元素
        var bodyEle = document.body;
        console.log(bodyEle);
        console.dir(bodyEle);

        // 获取html元素
        var htmlEle = document.documentElement;
        console.log(htmlEle);
(5)事件概述

js使我们有能力创建动态页面,而事件是可以被js侦测到的行为。

简单理解:触发—响应机制

网页中每个元素都可以产生某些可以触发js的事件,例如,我们可以在用户点击某按钮时产生一个事件,然后去执行某些操作。

(6)事件基础

事件三要素: 事件源 事件类型 事件处理程序

<button id="btn">唐伯虎</button>
    <script>
        // DOM 事件
        // 事件三要素  :事件源  事件类型  事件处理程序
        // 1.获取事件源  事件被触发的对象
        var btn = document.getElementById('btn');
        // 2.事件类型  如何触发
        // 3.事件处理程序  通过一个函数赋值的方式
        btn.onclick = function () {
            alert('点秋香');
        }
    </script>

执行事件的步骤:

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

常见的鼠标事件

鼠标事件触发条件
onclick鼠标点击左键触发
onmouseover鼠标经过触发
onmouseout鼠标离开触发
onfocus获得鼠标焦点触发
onblur失去鼠标焦点触发
onmouseover鼠标移动触发
onmouseup鼠标弹起触发
onmousedown鼠标按下触发
(7)操作元素

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

  1. 修改元素内容

    element.innerText

    从起始位置到终止位置的内容,但它去除HTML标签,同时去掉空格和换行

    element.innerHTML

    起始位置到终止位置的全部内容,包括HTML标签,同时保留空格和换行

    		var btn = document.querySelector('button');
            var div = document.querySelector('div');
            btn.onclick = function () {
                // div.innerText = '2021-8-9';
                div.innerText = getDate();
            }
            function getDate() {
                var date = new Date();
                // 写成 年 月 日 星期的格式
                var year = date.getFullYear();
                var month = date.getMonth() + 1;
                var dates = date.getDate();
                var arr = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
                var day = date.getDay();
                return '今天是:' + year + '年' + month + '月' + dates + '日 ' + arr[day];
            }
    
            // 元素可以不添加事件
            var p = document.querySelector('p');
            p.innerText = getDate();
    

    **相同:**都可以改变元素,都可以进行读写

    不同:

    // innerText 和 innerHTML 的区别
            var div = document.querySelector('div');
            // innerText 不识别html标签 非标准  
            div.innerText = '<strong>今天是:</strong>2021';
            // innerHTML 识别html标签 W3C推荐
            div.innerHTML = '<strong>今天是:</strong>2021';
    
            // 这两个属性是可以读写的 可获取元素中的内容
            var p = document.querySelector('p');
            // innerText 会去除空格和换行
            console.log(p.innerText);
            // innerHTML 保留空格和换行
            console.log(p.innerHTML);
    
    • innerText 不识别html标签 会去除空格和换行 非标准
    • innerHTML 识别html标签 保留空格和换行 W3C推荐
  2. 修改元素属性

    	<button id="ldh">刘德华</button>
        <button id="zxy">张学友</button> <br>
        <img src="images/ldh.jpg" alt="" title="刘德华">
    	<script>
            // 修改元素属性
            var ldh = document.querySelector('#ldh');
            var zxy = document.querySelector('#zxy');
            var img = document.querySelector('img');
    
            zxy.onclick = function () {
                img.src = 'images/zxy.jpg';
                img.title = '张学友';
            }
    
            ldh.onclick = function () {
                img.src = 'images/ldh.jpg';
                img.title = '刘德华';
            }
    	</script>
    
  3. 修改表单属性

    	<button>按钮</button>
        <input type="text" value="输入内容">
        <script>
            // 修改表单属性
            var btn = document.querySelector('button');
            var input = document.querySelector('input');
            btn.onclick = function () {
                // input.innerHTML = '点击了'
                // 以上代码没有作用,因为这是普通盒子(如div标签)里面的内容
                // 表单中的值 文字内容是通过value属性来修改的
                input.value = '被点击了';
                // 如果想让某个表单禁用 使用disabled
                // btn.disabled = true;
                this.disabled = true;  //这里也可以使用this  this指向的是事件函数的调用者 btn
            }
        </script>
    
    • 修改表单中的文字,不能通过innerHTML进行操作,因为该属性是普通盒子才有的
    • 想要修改表单中的文字,只能通过value属性来修改
    • 如果想让某个表单禁用 ,可以使用disabled属性
  4. 仿京东显示隐藏密码demo

    核心思路:一个按钮两种状态,点击一次,切换为文本框;再点击一次切换为密码框
    利用一个flag变量,来判断flag的值,如果是1就切换文本框,并把flag置为0;否则相反

    <!-- 点击眼睛按钮,把密码框类型改为文本框类型,就可以看到里面的密码了 -->
        <!-- 核心思路:一个按钮两种状态,点击一次,切换为文本框;再点击一次切换为密码框 -->
        <!-- 利用一个flag变量,来判断flag的值,如果是1就切换文本框,并把flag置为0;否则相反 -->
        <div class="box">
            <label for="">
                <img src="images/close.png" alt="" id="eye">
            </label>
            <input type="password" name="" id="pwd">
        </div>
    
        <script>
            var eye = document.getElementById('eye');
            var pwd = document.getElementById('pwd');
            var flag = 0;
            eye.onclick = function () {
                if (flag == 0) {
                    eye.src = 'images/open.png';
                    pwd.type = 'text';
                    flag = 1;
                } else {
                    eye.src = 'images/close.png';
                    pwd.type = 'password';
                    flag = 0;
                }
    
            }
        </script>
    
  5. 修改样式属性

    a. 修改行内样式操作 element.style

    // 1.行内样式操作   element.style
            var div = document.querySelector('div');
            div.onclick = function () {
                // div.style.backgroundColor = 'purple';
                // 注意这里的backgroundColor 采取驼峰命名法
                this.style.backgroundColor = 'purple';
                this.style.width = '250px';
            }
    
    • js中的样式采取驼峰命名法 如:fontSize、backgroundColor
    • js修改style样式操作,产生的是行内样式,CSS权重比较高

    b. 修改元素样式 element.className

    // 2.类名样式操作   element.className
            var test = document.querySelector('div');
            test.onclick = function () {
                // 使用 element.style 来修改元素样式 如果样式较少或者功能简单的情况下使用
                /* this.style.backgroundColor = 'purple';
                this.style.color = '#fff';
                this.style.fontSize = '25px';
                this.style.marginTop = '100px'; */
                // 使用 element.className 来进行修改样式属性
                // 让当前元素的类名改为了change  注意不要加'.'
                // 相当于给div添加了change这个类名  <div class="change"></div>
                this.className = 'change';
                // 如果还想保留原来的类  多类名选择器
                this.className = 'first change';
            }
    
    • 首先在CSS中新写一个change类的样式,让当前元素的类名改为了change 注意不要加’.’

    • 相当于给div添加(或者覆盖)了change这个类名 <div class="change"></div>

    • 新的className会覆盖原来的类 如果想保留原来的类 则使用多类名选择器

      className = 'first change'

    • 适用于样式较多或者功能复杂的情况

  6. 仿淘宝关闭二维码demo

    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
        <style>
            .box {
                position: relative;
                width: 74px;
                height: 88px;
                border: 1px solid #ccc;
                margin: 100px auto;
                font-size: 12px;
                text-align: center;
                color: #f40;
            }
    
            .box img {
                width: 60px;
                margin-top: 5px;
            }
    
            .close-btn {
                position: absolute;
                left: -16px;
                top: -1px;
                width: 14px;
                height: 14px;
                line-height: 14px;
                border: 1px solid #ccc;
            }
        </style>
    </head>
    
    <body>
        <div class="box">
            淘宝二维码
            <img src="images/tao.png" alt="">
            <i class="close-btn">×</i>
        </div>
    
        <script>
            // 仿淘宝关闭二维码 demo
            // 核心思路:利用样式的显示和隐藏完成,display:none 隐藏元素  display:block 显示元素
            var btn = document.querySelector('.close-btn');
            var box = document.querySelector('.box');
            btn.onclick = function () {
                box.style.display = 'none';
            }
        </script>
    </body>
    

    **注意:这里使用display:none,当页面刷新时,二维码又会重新出现。**隐藏元素也可以使用visibility: hidden;和opacity: 0;两种方法,且这两种方法不会造成回流,性能更好。

  7. 循环精灵图

    精灵图一般排布都是有规律的。所以利用for循环就可以遍历出所有的精灵图,而不用一个一个去定位。

    // 循环精灵图
            // 核心思路:精灵图图片排列是有规律的,利用for循环,修改精灵图片的背景位置 background-position
            var lis = document.querySelectorAll('li');
            for (var i = 0; i < lis.length; i++) {
                // 因为该精灵图是竖着排列的  所以只要y坐标改变就行
                var index = i * 44;
                // 注意精灵图的坐标一般是负数,那么这里的-号一定要保留!注意写法!
                lis[i].style.backgroundPosition = '0 -' + index + 'px';
            }
    

    但要注意这里的写法,backgroundPosition有两个参数,一个是x坐标,一个是y坐标。注意带单位!注意字符串拼接。

  8. 显示隐藏文本框内容

    // 显示隐藏文本框内容
            // 首先表单需要两个新事件,获得焦点onfocus  失去焦点onblur
            var text = document.querySelector('input');
            text.onfocus = function () {
                // console.log('得到了焦点');
                // 获得焦点 需要判断表单里面内容是否为默认文字,如果为默认文字则清空表单内容
                if (this.value === '手机') this.value = '';
                // 获得焦点 需要把文本框里面的文字颜色变黑
                this.style.color = '#333';
            }
            text.onblur = function () {
                // console.log('失去焦点');
                // 失去焦点 需要判断表单内容是否为空,如果为空则恢复默认文字
                if (this.value === '') this.value = '手机';
                // 失去焦点 需要把文本框里面的文字颜色变浅
                this.style.color = '#999';
            }
    
    • 使用时间:onfocus onblur
    • 充分利用属性可读写
  9. 密码验证信息demo

    因为要修改的样式较多 所以我们使用className来进行修改

    // 密码框验证信息
            // 首先判断的事件是表单失去焦点 onblur
            // 如果输入正确则提示正确的信息 颜色为绿色 小图标变化
            // 如果输入不是6-16位 则提示错误信息 颜色为红色 小图标变化
            // 因为要修改的样式较多 所以我们使用className来进行修改
            var ipt = document.querySelector('.ipt');
            var message = document.querySelector('.message');
            ipt.onblur = function () {
                // 根据表单里面值的长度来进行判断
                var len = this.value.length;
                if (len < 6 || len > 16) {
                    message.className = 'message wrong';
                    message.innnerHTML = '您输入的位数不对,要求6-16位'
                } else {
                    message.className = 'message right';
                    message.innerHTML = '该密码符合';
                }
            }
    

    修改className时,使用的是多类名。

(8)排他思想(算法)
	<button>按钮1</button>
    <button>按钮2</button>
    <button>按钮3</button>
    <button>按钮4</button>
    <button>按钮5</button>
    <script>
        // getElementsByTagName可兼容低版本
        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) 然后才让当前点击的按钮背景颜色变成粉色
                this.style.backgroundColor = 'pink';
            }
        }
    </script>

算法思想:

  1. 首先先去除所有元素的样式
  2. 然后才设置自己的样式
百度换肤demo
// 先选出类名为baidu的ul,再选出其中的图片,这样写更严谨
        var imgs = document.querySelector('.baidu').querySelectorAll('img');
        for (var i = 0; i < imgs.length; i++) {
            imgs[i].onclick = function () {
                // console.log(this.src); 把这个路径给body就行了
                document.body.style.backgroundImage = 'url(' + this.src + ')';
            }
        }

注意url的写法:url('+ 路径 +')

表格各行变色效果demo
// 核心思路:鼠标经过tr行,当前的行变背景颜色,鼠标离开去掉背景颜色
        // 这里表格分成thead和tbody  只有经过tbody时才变色
        var trs = document.querySelector('tbody').querySelectorAll('tr');
        // console.log(trs);
        for (var i = 0; i < trs.length; i++) {
            // 鼠标经过 添加背景颜色
            trs[i].onmouseover = function () {
                this.className = 'bg';
            }
            // 鼠标离开 去掉背景颜色
            trs[i].onmouseout = function () {
                this.className = '';
            }
        }
表单全选和取消全选demo
// 1.全选和取消全选的做法:让下面所有复选框的checked属性 跟随全选按钮即可
        // 全选按钮
        var j_cbAll = document.getElementById('j_cbAll');
        // 其余复选框
        var j_tb = document.getElementById('j_tb').getElementsByTagName('input');
        j_cbAll.onclick = function () {
            // this.checked 是一个布尔值  表示当前复选框的选中状态
            // console.log(this.checked);
            for (var i = 0; i < j_tb.length; i++) {
                j_tb[i].checked = this.checked;
            }
        }


/* 2.下面复选框全部选中时 上面全选框被选中的做法:给下面所有复选框绑定点击事件,
每次点击,都要循环查看下面所有的复选框是否有没选中的;只要下面的复选框有一个没选中的,上面的全选框就不选中 */
        for (var i = 0; i < j_tb.length; i++) {
            j_tb[i].onclick = function () {
                // flag 记录全选按钮是否选中
                var flag = true;
                for (var i = 0; i < j_tb.length; i++) {
                    if (!j_tb[i].checked) {
                        flag = false;
                        break;  // 只要有一个没有选中,就标记flag为false并退出循环 提高效率
                    }
                }
                j_cbAll.checked = flag;
            }
        }
(9)自定义属性操作
	<div id="demo" index='1' class="box"></div>
    <!-- 这里index是我们自定义的属性 -->
    <script>
        var div = document.querySelector('div');
        // 1.获取元素的属性值
        // (1)element.属性  获取内置属性值(也就是本身自带的属性)
        console.log(div.id);
        // (2)element.getAttribute('属性') 主要用于获取自定义属性(程序员自定义的属性)的属性值
        // 当然也可以用于获取内置属性值
        console.log(div.getAttribute('id'));
        console.log(div.getAttribute('index'));
        console.log(div.getAttribute('class'));


        // 2.设置属性值
        // (1)element.属性 = '值'
        div.id = 'new';
        // (2)element.setAttribute('属性','值')  主要针对于自定义属性
        // 当然也可以用于修改内置属性值
        div.setAttribute('class', 'new_box');


        // 3.移除属性
        // element.removeAttribute('属性')
        div.removeAttribute('index');
    </script>
  • element.属性 用于内置属性的获取以及修改
  • element.getAttribute element.setAttribute element.removeAttribute 主要用于自定义属性
!!tab栏切换demo
// tab栏切换
        var lis = document.querySelector('.tab_list').querySelectorAll('li');
        var items = document.querySelectorAll('.item');
        for (var i = 0; i < lis.length; i++) {
            // 给li设置索引号 使用setAttribute
            lis[i].setAttribute('index', i);
            lis[i].onclick = function () {
                // 1.选项卡模块 点击某一个,当前被点击的底色为红色,其余不变  使用排他思想
                for (var i = 0; i < lis.length; i++) {
                    lis[i].className = '';
                }
                this.className = 'current';
                // 2.下面模块内容 会跟随上面选项卡变化,所有下面模块变化要写到点击事件中去
                // 规律:下面模块显示内容和上面选项卡一一对应
                // 核心思路:给上面的tab_list里面所有的li添加自定义属性,属性值从0开始编号
                var index = this.getAttribute('index');
                // console.log(index);
                // 排他思想  只显示当前选项卡对应的item  其余隐藏
                for (var i = 0; i < items.length; i++) {
                    items[i].style.display = 'none';
                }
                items[index].style.display = 'block';
            }
        }
(10)H5自定义属性

自定义属性的目的:是为了保存并使用数据,有些数据可以保存到页面中而不用保存到数据库中。

获取自定义属性 getAttribute('属性') 但是有些自定义属性很容易引起歧义,不容易判断是元素的内置属性还是自定义属性。

因此H5新增了自定义属性。

1、设置自定义属性

  1. H5规定自定义属性data-开头作为属性名并且赋值 如:<div data-index="1"></div>;
  2. 或者使用js进行设置 element.setAttribute('data-index','2');

2、获取自定义属性

  1. 兼容性获取 element.getAttribute('data-index');
  2. H5新增 element.dataset.index 或者 element.dataset['index']
<div getTime='20' data-list-name="andy"></div>
    <script>
        var div = document.querySelector('div');
        // console.log(div.getTime);  //这个会返回undefined  因为getTime是自定义属性 不能这样进行获取
        // 而应该通过getAttribute来进行获取
        console.log(div.getAttribute('getTime'));
        div.setAttribute('data-time', 2);
        div.setAttribute('data-index', 5);
        console.log(div.getAttribute('data-time'));
        // H5新增的获取自定义属性的方法  
        // 只能获取由data-开头的自定义属性  且IE11以下不兼容
        // dataset 是一个集合 里面存放了所有以data开头的自定义属性
        console.log(div.dataset);
        console.log(div.dataset.index);
        console.log(div.dataset['time']);
        // 如果想要获取自定义属性 data-list-name 要采取驼峰命名法
        console.log(div.dataset.listName);
    </script>
  • getAttribute方法兼容性更好,开发中更常用
  • H5新增的方法 只能获取data-开头的自定义属性,且IE11以下不兼容
  • 注意如果使用dataset方式进行获取,自定义属性如果是由几个短词连接而成的,如data-list-name,要采取驼峰命名法
(11)为什么学节点操作

获取元素通常使用两种方式:

  1. 利用DOM提供的方法获取元素

    • document.getElementById()

    • document.getElementsByTagName()

    • document.querySelector

      逻辑性不强、繁琐

  2. 利用节点层次关系获取元素

    • 利用父子兄节点关系获取元素
    • 逻辑性强,但兼容性稍差

这两种方式都可以获取元素节点,但节点操作更简单。

(12)节点概述

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

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

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

  • 元素节点 nodeType为1
  • 属性节点 nodeType为2
  • 文本节点 nodeType为3 (文本节点包含文字、空格、换行等)

实际开发中,节点操作主要操作的还是元素节点。

(13)节点层次

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

  1. 父级节点

    node.parentNode

    var erweima = document.querySelector('.erweima');
    // var box = document.querySelector('.box');
    // 得到的是离元素最近的父节点  如果找不到父节点就返回null
    console.log(erweima.parentNode);
    
    • 返回的是最近的父节点
    • 如果找不到父节点就返回null
  2. 子节点

    node.childNodes(标准)

    // 子节点
            // 如果使用 DOM 提供的API获取
            var ul = document.querySelector('ul');
            // var lis = ul.querySelectorAll('li');
            // 1.使用子节点进行获取 node.childNodes
            console.log(ul.childNodes);
            //这里会返回9个节点,包含4个li元素节点和5个回车文本节点
            console.log(ul.childNodes[0].nodeType); //文本节点 nodeType为3
            console.log(ul.childNodes[1].nodeType); //元素节点 nodeType为1
            // 那么如果想只获取里面的元素节点,需要通过nodeType来进行筛选
            for (var i = 0; i < ul.childNodes.length; i++) {
                if (ul.childNodes[i].nodeType === 1) {
                    // 此时表示该结点为元素节点
                    console.log(ul.childNodes[i]);
                }
            }
    
    • childNodes 会返回所有的子节点(包括文本节点、属性节点以及元素节点等)
    • 如果想只获取其中的元素节点,需要经过特殊处理(通过nodeType来进行筛选)

    parentNode.children (非标准)

    // 2.parentNode.children 实际开发中常用
            // 是一个只读属性 返回所有的子元素节点(只返回子元素节点,其余节点不返回)
            // 虽然children是一个非标准,但是得到了各个浏览器的支持,所以可以放心使用
            console.log(ul.children);
    
    • children 是一个只读属性 返回所有的子元素节点(只返回子元素节点,其余节点不返回)
    • 虽然children是一个非标准,但是得到了各个浏览器的支持,所以可以放心使用
    • 实际开发中,我们更常使用children而不是childNodes,因为一般都是操作元素节点

    firstChild & lastChild

    第一个子节点和最后一个子节点

    firstElementChild & lastElementChild

    第一个元素子节点和最后一个元素子节点。但是有兼容性问题,IE9以上才支持!!

    children[0] children[children.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[3]);
            console.log(ol.children[ol.children.length - 1]);
    
  3. 新浪下拉菜单demo

    // 导航栏中的li都要有鼠标经过的效果,所以要循环注册事件
            // 核心原理:当鼠标经过 li里面的第二个孩子ul显示;当鼠标离开,ul隐藏
            var nav = document.querySelector('.nav');
            var lis = nav.children;
            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';
                }
            }
    

    这里采取的是节点获取元素的方法。

  4. 兄弟节点

    node.nextSibling

    返回当前元素的下一兄弟节点,找不到返回null。包含所有的节点。

    node.previousSibling

    当前元素的上一兄弟节点。

    // 兄弟节点
            var div = document.querySelector('div');
            // 1.node.nextSibling  获取下一个兄弟节点(可能是元素节点,也可能是文本节点)
            console.log(div.nextSibling);
            // 2.node.previousSibling
            console.log(div.previousSibling);
            // 3.node.nextElementSibling  获取下一兄弟元素节点
            console.log(div.nextElementSibling);
            // 4.node.previousElementSibling  如果没有,则返回null
            console.log(div.previousElementSibling);
    

    node.nextElementSibling

    node.previousElementSibling

    以上两个可以返回兄弟元素节点,找不到也是返回null。但注意这两个方法有兼容性问题,IE9+才支持

    如何解决兼容性问题?

    自己封装一个兼容性函数。

    // 封装兼容性函数
            function getNextElementSibling(element) {
                var ele = element;
                while (ele = ele.nextSibling) {
                    if (ele.nodeType === 1) return ele;
                }
                return null;
            }
            console.log(getNextElementSibling(div));
    

    通过判断节点类型返回兄弟元素节点,但其实在实际开发中,兄弟节点使用较少。

  5. 创建节点

    document.createElement('tagName')

    创建由tagName指定的HTML元素。因为这些元素原先不存在,是根据我们的需求动态生成的,所以也称为动态创建元素节点

    // 1.创建元素节点
            var li = document.createElement('li');
    
  6. 添加节点

    node.appendChild(child)

    将一个节点添加到指定父节点的子节点列表末尾。类似于CSS里的after伪元素。

    // 2.添加节点 尾部追加
            var ul = document.querySelector('ul');
            ul.appendChild(li);
    

    如果原来父节点中已经有子元素了,那么则会在后面追加新元素。

    node.insertBefore(child,指定元素)

    将一个节点添加到父元素的指定子节点前面。类似于CSS里面的before伪元素。

    // 3.添加节点 指定元素前面插入
            var lili = document.createElement('li');
            ul.insertBefore(lili, ul.children[0]);
    
  7. 简单版发布留言demo

    <!-- 简单版发布留言demo -->
        <textarea name="" id=""></textarea>
        <button>发布</button>
        <ul>
        </ul>
    
        <script>
            // 1.获取文本域和button按钮,以及ul父元素
            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 {
                    // console.log(text.value);
                    // (1)创建元素
                    var li = document.createElement('li');
                    // 将文本域中的内容赋值给li
                    li.innerHTML = text.value;
                    // (2)添加元素
                    // ul.appendChild(li);
                    // 也可以把新加入的元素放到前面
                    ul.insertBefore(li, ul.children[0])
                }
            }
        </script>
    

    核心思路:

    1. 首先要创建元素
    2. 然后将文本域中的值取过来,赋给新元素 li.innerHTML = text.value;
    3. 最后将新元素添加到父节点中,可以尾部追加,也可以放到指定元素之前
  8. 删除节点

    node.removeChild(child)

    从DOM中删除一个子节点,并返回删除的节点。

    <button>删除</button>
        <ul>
            <li>熊大</li>
            <li>熊二</li>
            <li>光头强</li>
        </ul>
    
        <script>
            var ul = document.querySelector('ul');
            var btn = document.querySelector('button');
            btn.onclick = function () {
                if (ul.children.length === 0) {
                    // alert('没有孩子可以删除了');
                    // 把按钮禁用
                    this.disabled = true;
                }
                else { ul.removeChild(ul.children[0]); }
            }
        </script>
    

    注意这里的逻辑判断:如果父节点的子节点长度为0(也就是没有子节点了),将删除按钮禁用。

  9. 删除留言demo

    核心思路:

    1. 当我们把文本域中的值赋给li时,多添加一个删除的链接。
    2. 需要把所有的链接获取过来,当点击当前链接的时候,删除当前链接所在的li
    3. 阻止链接跳转需要添加javascript:void(0);或者javascript:;

    本案例是在简单版发布留言demo的基础上进行添加的:

    1. 在把文本域的值赋给li时,多添加一个用于删除的链接

      li.innerHTML = text.value + "<a href='javascript:;'>删除</a>";
      
    2. 删除元素

      // (3)删除元素 删除的是当前链接的li 也就是它的父节点
      var as = document.querySelectorAll('a');
      for (var i = 0; i < as.length; i++) {
          as[i].onclick = function () {
              // node.removerChild(child); 删除的是li(当前a所在的li) this.parentNode
              ul.removeChild(this.parentNode);
          }
      }
      

      这里需要删除的是li,li的父亲是ul;li是通过链接a来进行获取的(a.parentNode),这里是给a绑定事件,所以this指向的就是a。

      所以删除节点的操作是:ul.removeChild(this.parentNode);

  10. 复制节点(克隆节点)

node.cloneNode()

返回调用该方法的节点的一个副本,也称为克隆节点/拷贝节点

// 克隆节点
        var ul = document.querySelector('ul');
        // 1.node.cloneNode();
        var liClone = ul.children[0].cloneNode();
        // 如果括号参数为空或者false,则是浅拷贝,只复制标签,并不克隆里面的内容
        ul.appendChild(liClone);
        console.log(liClone);

        // 如果括号参数为true,则是深拷贝,复制标签,并克隆里面的内容
        var liDeep = ul.children[0].cloneNode(true);
        ul.appendChild(liDeep);
        console.log(liDeep);
  • 如果括号参数为空或者为false,则为浅拷贝,只复制节点本身,不克隆里面的子节点
  • 如果括号参数为true,则为深拷贝,会复制节点本身以及里面的所有子节点。
  1. 动态生成表格demo

    <!-- 动态生成表格 -->
        <table cellspacing="0">
            <thead>
                <tr>
                    <th>姓名</th>
                    <th>科目</th>
                    <th>成绩</th>
                    <th>操作</th>
                </tr>
            </thead>
            <tbody>
            </tbody>
        </table>
    
    
        <script>
            // 1.创建学生数据
            // 里面的学生数据都是动态的,需要js动态生成。数据采取对象形式存储
            // 数组存储数据,每个元素是一个学生对象
            var datas = [{
                name: '张三',
                subject: 'JavaScript',
                score: 100
            }, {
                name: '张三疯',
                subject: 'JavaScript',
                score: 90
            }, {
                name: '李四',
                subject: 'JavaScript',
                score: 95
            }, {
                name: '王五',
                subject: 'JavaScript',
                score: 98
            }]
    
            // 2.创建单元格
            // 所有的数据都是放到tbody里面的行中的
            // (1) 循环创建行 行的数量取决于数据对象的数量
            var tbody = document.querySelector('tbody');
            for (var i = 0; i < datas.length; i++) {
                // 创建tr行
                var tr = document.createElement('tr');
                // 插入行到tbody中
                tbody.appendChild(tr);
    
                // (2) 在行中创建单元格 td 单元格的数量取决于每个对象的属性个数
                for (var k in datas[i]) {  //遍历对象中的每个属性
                    var td = document.createElement('td');
                    // 填充数据
                    td.innerHTML = datas[i][k];
                    tr.append(td);
                }
    
                // (3) 创建 删除列 单元格
                var td = document.createElement('td');
                td.innerHTML = '<a href="javascript:;">删除</a>';
                tr.appendChild(td);
            }
    
            // 3.添加删除操作
            var as = document.querySelectorAll('a');
            for (var i = 0; i < as.length; i++) {
                as[i].onclick = function () {
                    // 删除掉表格中的这一行 tbody中的孩子tr 这里的tr又是as[i]父亲(td)的父亲
                    tbody.removeChild(this.parentNode.parentNode);
                }
            }
        </script>
    
  2. 三种动态创建元素的区别

    • document.write()
    • element.innerHTML
    • document.createElement()

    区别:

    1. document.write()是直接将内容写入页面的内容流;但是文档流执行完毕,再调用这句话会导致页面全部重绘

      window.onload = function () {}效果等同。

    2. innerHTML和createElement就没有页面重绘的问题

    3. 但innerHTML和createElement在创建多个元素时效率上会有不同

    4. innerHTML创建多个元素时效率更高(但前提是使用数组形式进行拼接,而不是使用字符串方式进行拼接),结构稍微复杂

    5. createElement()创建多个元素效率稍微低一点,但结构更清晰

    // 三种创建元素方式的区别
            // 1.document.write()  如果页面文档流加载完毕,再调用这句话会导致页面重绘
            var btn = document.querySelector('button');
            btn.onclick = function () {
                document.write('<div>123</div>');
            }
    
            // 2.innerHTML 创建元素
    		// 采用字符串拼接方式
            var inner = document.querySelector('.inner');
            for (var i = 0; i < 100; i++) {
                inner.innerHTML += '<a href="#">百度</a>';
            }
            // 字符串拼接效率比较低,以下进行改造
    		// 采用数组拼接方式
            var arr = [];
            for (var i = 0; i < 100; i++) {
                arr.push('<a href="#">百度</a>');
            }
            inner.innerHTML = arr.join('');
    
            // 3.document.createElement() 创建元素
            var create = document.querySelector('.create');
            for (var i = 0; i < 100; i++) {
                var a = document.createElement('a');
                create.appendChild(a);
            }
    
(14)DOM总结

文档对象模型(Document Object Model) DOM 是W3C组织推荐的处理可扩展标记语言的标准编程接口。通过这些DOM接口可以改变网页的内容、结构和样式。

获取的DOM元素都是对象。

关于DOM操作,主要针对于于元素的操作。主要有创建、增、删、改、查、属性操作、事件操作。

  1. 创建

    • document.write
    • innerHTML
    • createElement
    • appendChild
    • insertBefore
  2. removeChild

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

    • 修改元素属性:src,href,title等
    • 修改普通元素内容:innerHTML,innerText
    • 修改表单元素:value,type,disabled等
    • 修改元素样式:style,className
  4. 主要获取查询dom的元素

    • DOM提供的API方法:getElementById,getElementByTagName 古老方法 不太推荐
    • H5新增方法:querySelector,querySelectorAll 提倡
    • 利用节点操作获取元素:父(parentNode),子(children),兄(previousElementSibling、nextElementSibling) 提倡
  5. 属性操作

    主要针对自定义属性

    • setAttribute:设置dom属性值
    • getAttribute:获取dom属性值
    • removeAttribute:移除属性
  6. 事件操作

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

四、事件高级导读
(1)注册事件

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

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

1.传统注册方式
  • 利用on开头的事件 onclick
  • <button onclick = "alert('hi~~')"></button>
  • btn.onclick = function() {}
  • 特点:注册事件的唯一性
  • 同一个元素同一个事件只能设置一个处理函数,最后注册的处理函数将会覆盖前面注册的处理函数
		btns[0].onclick = function () {
            alert('hi~~');
        }
        // 1.传统注册事件 唯一性 后注册的处理函数会覆盖掉前面的处理函数
        btns[0].onclick = function () {
            alert('how are you~~');
        }
2.方法监听注册方式
  • W3C标准 推荐方式
  • addEventListener() 它是一个方法
  • IE9 以前的IE浏览器不支持此方法,可以使用attachEvent()代替
  • 特点:同一个元素同一个事件可以注册多个监听器,按照注册顺序依次执行

eventTarget.addEventListener(type, listener[, useCapture])

三个参数分别为:

  • type:事件类型字符串,如click mouseover 注意这里不要加on
  • listener:事件处理函数,事件发生时,会调用该监听函数
  • useCapture:可选参数,是一个布尔值,默认值是false
	// 2.方法监听注册事件 同一元素同一事件可以添多个侦听器(事件处理程序)
        btns[1].addEventListener('click', function () { alert('hello~~') });
        btns[1].addEventListener('click', function () { alert('hello again~~') });
3.IE 低版本

attachEvent(eventNameWithOn, callback) IE9以下兼容

  • eventNameWithOn:事件类型字符串,如onclick onmouseover 这里要加on
  • callback:事件处理函数,当目标触发事件时回调函数被调用
// 3.attachEvent(eventNameWithOn, callback)
        btns[2].attachEvent('onclick', function () { alert('我是ie9前支持的') });
4.封装兼容函数
// 兼容封装注册事件
        // 兼容处理原则:首先照顾大多数浏览器,再处理特殊浏览器
        function addEventListener(element, eventName, fn) {
            // 判断当前浏览器是否支持addEventListener方法
            if (element.addEventListener) {
                element.addEventListener(eventName, fn);
            } else if (element.attachEvent) {
                element.attachEvent('on' + eventName, fn);
            } else {
                // 相当于 element.onclick = fn;
                element['on' + eventName] = fn;
            }
        }

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

(2)删除事件(解绑事件)
1.传统删除方式

eventTarget.onclick = null;

divs[0].onclick = function () {
            alert('hi');
            // 1.传统方式删除事件
            divs[0].onclick = null;
        }
2.方法监听移除方式

eventTarget.removeEventListener(type, listener[, useCapture]);

// 2.removeEventListener 删除事件
        divs[1].addEventListener('click', fn)
        function fn() {
            alert(22);
            divs[1].removeEventListener('click', fn);
        }

注意:由于这里需要指定具体要移除的处理函数,所以注册事件时,不能使用匿名函数。

3.IE 低版本
// 3.detachEvent(eventNameWithOn, callback)
        divs[2].attachEvent('onclick', fn1);
        function fn1() {
            alert(33);
            divs[2].detachEvent('onclick', fn1);
        }
(3)DOM事件流

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

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

DOM事件流的三个阶段:

  1. 捕获阶段(即自顶向下:Document -> Element html -> Element body -> Element div)
  2. 当前目标阶段
  3. 冒泡阶段(即自底向上:Element div -> Element body -> Element html -> Document)
  • 事件冒泡:IE最早提出,事件开始时由最具体的元素接收,然后逐级向上传播到DOM最顶层节点的过程
  • 事件捕获:网景最早提出,由DOM最顶层节点开始,然后逐级向下传播到最具体的元素接收的过程

注意

  1. JS代码中只能执行捕获或者冒泡中的一个阶段
  2. onclick和attachEvent只能得到冒泡阶段
  3. addEventListener(type, listener[, useCapture]) 其中第三个参数如果为true则表示在事件捕获阶段调用事件处理程序;如果是false则表示在事件冒泡阶段调用事件处理程序。参数默认值为false。
// 1.捕获阶段  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);
// 2.冒泡阶段  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.addEventListener('click', function () {
            alert('document');
        })

实际开发中很少使用事件捕获,更关注事件冒泡

有些事件没有冒泡,比如:onblur, onfocus, onmouseenter, onmouseleave

(4)事件对象
1.event对象

event对象代表事件的状态,比如键盘按键的状态、鼠标的位置、鼠标按钮的状态。

事件发生后,跟事件相关的一系列信息数据的集合都放到这个对象里面,这个对象就是事件对象event,有很多属性和方法。

// 事件对象
        var div = document.querySelector('div');
        div.onclick = function (event) {
            console.log(event);
            // 兼容性写法
            event = event || window.event;
            console.log(event);
        }

        div.addEventListener('click', function (e) {
            console.log(e);
        })
        // 1.event就是一个事件对象,写到侦听函数的小括号里,当形参来看
        // 2.事件对象只有有了事件才会存在,是系统自动创建的,不需要我们传递参数
        // 3.事件对象 是事件一系列相关数据的集合 跟事件相关的 比如鼠标点击里面就包含了鼠标的相关信息,鼠标坐标等
        // 如果是键盘事件 里面就包含的是键盘事件的信息 比如判断用户按下了哪个键
        // 4.这个事件对象可以自己命名 比如 event、evt、e
        // 5.事件对象也有兼容性问题 ie678 通过window.event 兼容性写法:e = e || window.event
2.常见事件对象属性和方法
 // 1.e.target 返回的是触发事件的对象(元素)
        // this 返回的是绑定事件的对象(元素)
        var div = document.querySelector('div');
        div.addEventListener('click', function (e) {
            console.log(e.target);
            // e.target有兼容性问题 ie678使用e.srcElement
            console.log(e.srcElement);
            console.log(this);
        })

        var ul = document.querySelector('ul');
        ul.addEventListener('click', function (e) {
            // 我们给ul绑定了事件 那么this指向的是ul
            console.log(this);
            // currentTarget 与this非常相似 但有兼容性问题 ie678不识别
            console.log(e.currentTarget);
            // e.target 指向点击的对象 谁触发事件 就指向谁 这里指向其中一个li
            console.log(e.target);
        })

e.target 和 this 的区别

  • e.target 点击了哪个元素 就返回哪个元素 有兼容性问题,ie678使用e.srcElement
  • this 绑定了哪个元素 就返回哪个元素

还有一个属性e.currentTarget 与this非常相似 但有兼容性问题 ie678不识别

事件对象属性方法说明
e.target返回触发事件的对象 标准
e.srcElement返回触发事件的对象 非标准 ie6-8使用
e.type返回事件的类型 比如 click mouseover 不带on
e.cancelBubble该属性阻止冒泡 非标准 ie6-8使用
e.returnValue该属性阻止默认事件(默认行为) 非标准 ie6-8使用 比如不让链接跳转
e.preventDefault()该方法阻止默认事件(默认行为) 标准 比如不让链接跳转
e.stopPropagation()该方法阻止冒泡 标准

返回事件类型和阻止默认事件

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

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

// 3.事件对象阻止默认行为 让链接不跳转 或者让提交按钮不提交
        var a = document.querySelector('a');
        a.addEventListener('click', function (e) {
            e.preventDefault();  //dom 标准写法
        })
        // 传统注册方式
        a.onclick = function (e) {
            // (1)普通浏览器  方法
            e.preventDefault();
            // (2)低版本浏览器 ie678  属性
            e.returnValue;
            // 还可以利用return false 来阻止默认行为 无兼容性问题 
            return false;  //但是return后面的代码就不会执行了,且该方法仅限于传统注册方式
        }

阻止事件冒泡

  • stopPropagation和cancelBubble
// 4.阻止事件冒泡
        var son = document.querySelector('.son');
        son.addEventListener('click', function (e) {
            alert('son');
            // 有兼容性问题  ie678不支持
            e.stopPropagation();  //propogation 传播
            e.cancelBubble = true;  //非标准
        }, false);
        var father = document.querySelector('.father');
        father.addEventListener('click', function () {
            alert('father');
        }, false);
        document.addEventListener('click', function () {
            alert('document');
        })

事件委托(代理、委派)

事件冒泡本身的特性,会带来坏处,但有时也会带来好处。

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

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

事件委托的作用:只操作一次dom,提高了程序的性能。

3.常见鼠标事件
  1. 禁止鼠标右键菜单

    contextmenu主要控制应该何时显示上下文菜单,主要用于程序员取消默认的上下文菜单

    // 1.contextmenu 禁用右键菜单
            document.addEventListener('contextmenu', function (e) {
                e.preventDefault();
            })
    
  2. 禁止鼠标选中

    // 2.selectstart 禁止鼠标选中
            document.addEventListener('selectstart', function (e) {
                e.preventDefault();
            })
    
4.鼠标事件对象

event对象代表事件的状态,跟事件相关的一系列信息的集合。现阶段主要是用鼠标事件对象MouseEvent和键盘事件对象KeyboardEvent。

鼠标事件对象说明
e.clientX返回鼠标相对于浏览器窗口可视区的X坐标
e.clientY返回鼠标相对于浏览器窗口可视区的Y坐标
e.pageX返回鼠标相对于文档页面的X坐标 IE9+支持
e.pageY返回鼠标相对于文档页面的Y坐标 IE9+支持
e.screenX返回鼠标相对于电脑屏幕的X坐标
e.screenY返回鼠标相对于电脑屏幕的Y坐标
// 鼠标事件对象
        document.addEventListener('click', function (e) {
            // 1. client 是相对于可视区上沿和左侧的距离
            console.log(e.clientX);
            console.log(e.clientY);
            // 2. page 是相对于页面文档的x和y坐标
            console.log(e.pageX);
            console.log(e.pageY);
            // 3. screen 是相对于电脑屏幕的x和y坐标
            console.log(e.screenX);
            console.log(e.screenY);
跟随鼠标的天使demo
  • 鼠标不断地移动,使用鼠标移动事件:mousemove
  • 在页面中移动,给document注册事件
  • 图片要移动距离,而且不占位置,使用绝对定位即可
  • **核心原理:**每次鼠标移动,都会获得最新的鼠标坐标,把这个x和y坐标作为图片的top和left值就可以移动图片了
var img = document.querySelector('img');
        console.log(img);
        document.addEventListener('mousemove', function (e) {
            var x = e.clientX, y = e.clientY;
            console.log(e.clientX);
            console.log(e.clientY);
            // 注意:千万不要忘记加单位!!  
            // x-50是为了让鼠标在图片中间  y-40同理 
            img.style.left = x - 50 + 'px';
            img.style.top = y - 40 + 'px';
        })
5.常用键盘事件

事件除了使用鼠标触发,还可以使用键盘触发。

键盘事件触发条件
onkeyup某个键盘按键被松开时触发
onkeydown某个键盘按键被按下时触发
onkeypress某个键盘按键被松开时触发 但无法识别功能键 如ctrl、shift、箭头等
// 常用键盘事件
        // 1.keyup
        /* document.onkeyup = function () {
            console.log('我弹起了');
        } */
        document.addEventListener('keyup', function () {
            console.log('我弹起了');
        })

        // 2.keydown
        document.addEventListener('keydown', function () {
            console.log('我被按下了keydown');
        })
        // 3.keypress  但无法识别功能键
        document.addEventListener('keypress', function () {
            console.log('我按下了keypress');
        })

三个事件的执行顺序:keydown -> keypress -> keyup

6.键盘事件对象

键盘事件对象之keyCode属性可以得到相应键的ASCII码值

// 键盘事件对象之keyCode属性可以得到相应键的ASCII码值
        document.addEventListener('keyup', function (e) {
            // 1.keyup 和 keydown事件不区分字母大小写 a和A得到的都是65
            console.log('up' + e.keyCode);
            // 可以利用keyCode返回的ASCII码值来判断用户按下了哪个键
            if (e.keyCode === 65) {
                alert('您按下了a键');
            } else {
                alert('您没有按下a键');
            }
        })
        document.addEventListener('keypress', function (e) {
            // 2.keypress事件区分字母大小写 A得到的是65 a得到的是97
            // console.log('press' + e.keyCode);
        })

keydown 和 keypress的区别:

  • keypress无法识别功能键
  • keydown无法识别大小写
模拟京东按s键自动将光标定位到搜索框demo
  • **核心思路:**检测用户是否按下s键,如果按下,则将光标定位到搜索框里
  • 使用键盘事件对象中的keyCode判断用户按下的是否为s键
  • 搜索框获得焦点:使用js里面的focus()方法
var search = document.querySelector('input');
        // 注意这里使用到的键盘事件是keyup,而不是keydown
        // 因为keydown会将s写到搜索框里 keyup等键弹起后再做判断 则不会写进搜索框
        document.addEventListener('keyup', function (e) {
            //通过该方法测试s的ASCII码值
            // console.log(e.keyCode); 
            if (e.keyCode === 83) {
                search.focus();  //获得焦点
            }
        })

这个案例几个注意点:

  1. 这里使用到的键盘事件是keyup,而不是keydown。因为keydown会将s写到搜索框里,keyup等键弹起后再做判断,不会写进搜索框。
  2. 可以通过console.log(e.keyCode)的方式测试想要的按键对应的ASCII码
  3. 使用element.focus()方法获得焦点
模拟京东快递单号查询效果demo
  • 快递单号输入内容时,上面的大号字体盒子(con)显示(字号更大)
  • 表单检测用户输入:给表单添加键盘事件
  • 同时把快递单号里面的值(value)获取过来赋值给con盒子(innerText)作为内容
  • 如果快递单号里面内容为空,则隐藏大号字体盒子(con)
  • 当失去焦点,则隐藏盒子
  • 当获得焦点,且文本框不为空,则显示con盒子
	var con = document.querySelector('.con');
        var jd_input = document.querySelector('.jd');
        // 这里使用的键盘事件是keyup
        // 因为keydown和keypress在文本框中的特点是:他们两个事件触发时,文字还没有落入文本框
        // 也就是当键盘按下的时候,会立马触发事件,
        // 此时当前正在按下的键还未存入文本框中,也就没办法通过this.value获取到con.innerHTML中
        // 而且keypress还无法识别功能键,删除效果无法实现
        // 而使用keyup的话,当按键弹起之后才会触发事件,此时文字已经存入文本框中
        jd_input.addEventListener('keyup', function () {
            // console.log('输入内容了');
            if (this.value == '') {
                con.style.display = 'none';
            } else {
                con.style.display = 'block';
                con.innerHTML = this.value;
            }
        })
        jd_input.addEventListener('blur', function () {
            con.style.display = 'none';
        })
        jd_input.addEventListener('focus', function () {
            if (this.value != '') {
                con.style.display = 'block';
            }
        })

这里使用的键盘事件是keyup,是因为keydown和keypress在文本框中的特点是:他们两个事件触发时,文字还没有落入文本框。也就是当键盘按下的时候,会立马触发事件,此时当前正在按下的键还未存入文本框中,也就没办法通过this.value获取到con.innerHTML中。
而且keypress还无法识别功能键,删除效果无法实现。
而使用keyup的话,当按键弹起之后才会触发事件,此时文字已经存入文本框中。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值