目录
一.Web API 介绍
- API:某些工具(函数)由编程语言提供,已封装好其内部实现,学会使用这些接口,无需关注其内部实现方法。
- Web API:浏览器提供的接口,用于操作 浏览器功能(BOM) 和 页面元素(DOM),作出浏览器交互效果。
二.DOM 介绍
- DOM 文档对象模型(Document Object Model):是 编程接口,可以改变网页的内容、结构和样式。
- 获取的 DOM元素 是一个对象(object),所以称为 文档对象模型
- DOM 树(文档树模型):把 文档 映射成树形结构,通过 节点对象 对其处理,处理的结果 可以加入当前页面。
- 名词解释:
- 文档:一个页面 = 一个文档,用 document 表示。
- 节点:网页中的所有内容,如:标签、属性、文本、注释等,用 node 表示。
- 标签节点:网页中的所有标签,简称为“元素”,用 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 的事件。
- 事件源(谁):触发事件的元素。
- 事件类型(事件如何触发): 如 onclick 点击事件。
- 事件处理程序(做什么):事件触发后要执行的代码(事件处理函数)。
<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 的元素属性、元素内容、表单值等
- 修改元素属性: src、href、title等
- 修改普通元素内容: innerHTML 、innerText
- 修改表单元素: value、type、disabled等
- 修改元素样式: style、className
- 查:主要获取查询 DOM 的元素
- DOM 提供的API 方法: getElementById、getElementsByTagName 古老用法 不推荐
- H5提供的新方法: querySelector、querySelectorAll 提倡
- 利用节点操作获取元素: 父(parentNode)、子(children)、兄(previous/nextElementSibling) 提倡
- 属性操作:主要针对于自定义属性
- setAttribute:设置 DOM 的属性值
getAttribute:得到 DOM 的属性值 removeAttribute:移除属性
- 事件操作:给元素注册事件, 采取 事件源.事件类型 = 事件处理程序
1.改变元素内容(获取或设置)
- innerText 和 innerHTML的区别:
- 获取内容时的区别: innerText 会去除空格和换行,而 innerHTML 会保留空格和换行
- 设置内容时的区别: innerText 不会识别 html,而 innerHTML 会识别
- 这两个属性是 可读写的 ,可以获取元素内容、改写元素内容
- 语法格式: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.普通元素的属性操作
- 获取属性的值:元素对象.属性名
- 设置属性的值:元素对象.属性名 = 值
分时问候分析
- 得到当前的小时数 需要 Date 内置对象
- 判断小时数改变图片和文字信息
<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 才开始支持
六.排他操作
排他思想
- 同一组元素,想要某一个元素实现某种样式, 需要用到循环的 排他思想 算法:
- 所有元素全部清除样式(干掉其他人)
- 给当前元素设置样式 (留下我自己)
- 注意 顺序不能颠倒,先干掉其他人,再设置自己
- 被点击的按钮变色 其余按钮不变色(排他思想):
- 两层 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; }}
七.节点操作
- 获取元素的两种方式:
- 利用 DOM 提供的方法获取元素:document.getElementBy......逻辑性不强、繁琐
- 利用节点层级关系获取元素:逻辑性强, 但兼容性差
1.节点概述
- 网页中所有内容都是节点(标签、属性、文本、注释等),DOM 中,节点用 node 来表示。
- 节点均可通过 JavaScript 进行访问、修改、创建或删除。
- 节点至少拥有nodeType(节点类型)、nodeName(节点名称)和 nodeValue(节点值)三个基本属性。
- 关于 nodeType:
元素节点 nodeType 为 1 (节点操作主要操作元素节点) 属性节点 nodeType 为 2 文本节点 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、onmouseleave4.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; }})