本篇为 JavaScript 系列笔记第五篇,将陆续更新后续内容。参考:黑马程序员JavaScript核心教程,前端基础教程
系列笔记:
JavaScript(一)—— 初识JavaScript / 注释 / 输入输出语句 / 变量 / 数据类型
JavaScript(二)—— 运算符 / 流程控制 / 数组
JavaScript(三)—— 函数 / 作用域 / 预解析 / 对象
JavaScript(四)—— 内置对象 / 简单数据类型与复杂类型 ?
一、Web APIs 简介
1. Web APIs 和 JavaScript基础 关联性
主要学习 ECMAScript 标准规定的基本语法,但是无法实现常用的网页交互效果
Web APIs 是 W3C 组织的标准,主要学习 DOM 和 BOM,实现页面交互功能 ?
2. API 和 Web API
API(Application Programming Interface,应用程序接口)是一些预先定义的接口(如函数、HTTP接口),或指软件系统不同组成部分衔接的约定。 用来提供应用程序与开发人员基于某软件或硬件得以访问的一组例程,而又无需访问源码,或理解内部工作机制的细节。
API 是给程序员提供的一种工具,以便能更轻松的实现所需功能
Web API 是浏览器提供的一套操作 浏览器功能 和 页面元素 的 API ( BOM 和 DOM ) ?
二、DOM
文档对象模型 (Document Object Model,简称 DOM ),是 W3C 制定的标准接口规范,是一种处理HTML 和 XML 文件的标准 API
1. DOM 简介
DOM 提供了对整个文档的访问模型,将文档作为一个树形结构,树的每个结点表示了一个HTML标签或标签内的文本项
- 文档:一个页面就是一个文档,DOM 中使用 document 表示
- 元素:页面中的所有标签都是元素,DOM 中使用 element 表示
- 节点:网页中的所有内容都是节点(标签、属性、文本、注释等),DOM 中用 node 表示
DOM 把以上内容都看作是对象 ?
2. 获取元素
获取页面中的元素可以使用以下几种方式:
- 根据 ID 获取
- 根据标签名获取
- 通过 HTML5 新增的方法获取
- 特殊元素获取
使用 getElementById() 方法可以获取带有 ID 的元素对象
- 文档页面从上向下加载,因此
<script> 写到标签下面 - 返回一个匹配到 ID 的 DOM Element 对象
为了更好查看元素对象里面的属性和方法,可以使用 console.dir(对象)
使用 getElementsByTagName() 方法可以返回带有指定标签名的对象的 集合
- 可以通过遍历打印里面的元素对象
for (var i = 0; i < lis.length; i++) {
console.log(lis[i]);
}
- 如果页面中没有此元素,返回空的伪数组
element.getElementByTagName('标签名')
还可以获取某个元素(父元素)内部所有指定标签名的子元素
element.getElementByTagName('标签名')
注意: 父元素必须是单个对象,不能是伪数组(必须指明是哪一个元素对象)
<ol id="ol">
<li>生僻字</li>
<li>生僻字</li>
<li>生僻字</li>
<li>生僻字</li>
</ol>
<script>
var ol = document.getElementById('ol');
console.log(ol.getElementsByTagName('li'));
</script>
1. document.getElementsByClassName('类名')
2. document.querySelector('选择器')
3. document.querySelectorAll('选择器')
1. document.body
2. document.documentElement
?
3. 事件基础
JavaScript 使我们有能力创建动态页面,而事件是可以被 JavaScript 侦测到的行为。网页中的每个元素都可以产生某些可以触发 JavaScript 的事件。例如,我们可以在用户点击某按钮时产生一个事件,然后去执行某些操作。
- 事件源:事件被触发的对象
- 事件类型:触发的方式,如鼠标点击触发(onclick)
- 事件处理程序:通过函数赋值
- 获取事件源
- 注册事件(绑定事件)
- 添加事件处理程序(采取函数赋值形式)
当然,除了 onclick 还有其他鼠标事件,后面将陆续讲述
4. 操作元素
JavaScript 的 DOM 操作可以改变网页内容、结构和样式,我们可以利用 DOM 操作元素来改变元素里面的内容、属性等。
1. element.innerText
从起始位置到终止位置的内容,不能识别 html 标签,同时空格和换行也会去掉
2. element.innerHtml
从起始位置到终止位置的全部内容,可以识别 html 标签,同时保留空格和换行。实际上,element.innerHtml 用到的更多,下面对比一下两种方式的不同:
innerText、innerHTML 改变元素内容src、href id、alt、title
利用DOM可以操作如下表单元素的属性:type、value、checked、seleced、disabled
- 不能使用
input.innerHTML 修改表单元素值,innerHTML 只能修改普通盒子的值,如 div - 事件函数中
this 指向事件函数的调用者
点击按钮将密码框切换为文本框,并可以查看密码明文。
<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) {
pwd.type = 'password';
eye.src = 'images/close.png';
flag = 0;
} else {
pwd.type = 'text';
eye.src = 'images/open.png';
flag = 1;
}
}
</script>
我们可以通过 JS 修改元素的大小、颜色、位置等样式
1. element.style 行内样式操作
2. element.className 类名样式操作
- JavaScript 里面样式采取驼峰命名,比如
fontSize 、backgroundColor - JavaScript 修改 style 样式操作,产生的是行内样式,权重比重高
- 案例 1:淘宝点击关闭二维码
<div class="box">
淘宝二维码
<img src="images/tao.png" alt="">
<i class="close-btn">x</i>
</div>
<script>
var btn = document.querySelector('.close-btn');
var box = document.querySelector('.box');
btn.onclick = function () {
box.style.display = 'none';
}
</script>
????????????????
<script>
var lis = document.querySelectorAll('li');
for (var i = 0; i < lis.length; i++) {
var index = i * 44;
lis[i].style.backgroundPosition = '0 -' + index + 'px';
}
</script>
当鼠标点击文本时,里面的默认文字隐藏,当鼠标离开文本框时,里面的文字显示 两个新事件: 获得焦点 onfocus 、 失去焦点 onblur
<input type="text" placeholder="手机">
<script>
var text = document.querySelector('input');
text.onfocus = function () {
text.placeholder = '';
this.style.color = '#333';
}
text.onblur = function () {
text.placeholder = '手机';
this.style.color = '#999';
}
</script>
点击后表单边框变得很丑,可以通过 outline:none; 取消此效果
- 样式属性操作二:
element.className
前面讲述的 element.style 更适合样式简单情况,如果需要改变大量样式属性,则可以采用 element.className 方式,直接覆盖先前的类名(若想保留可以采用多类名选择器)
用户如果离开密码框,里面输入个数不是 6 ~ 16,则提示错误信息,否则提示正确
<div class="register">
<input type="password" class="ipt">
<p class="message">请输入6~16位密码</p>
</div>
<script>
var ipt = document.querySelector('.ipt');
var msg = document.querySelector('.message');
ipt.onblur = function () {
if (this.value.length < 6 || this.value.length > 16) {
msg.className = 'message wrong';
msg.innerHTML = '您所输入的密码不满足6~16位';
} else {
msg.className = 'message right';
msg.innerHTML = '您输入的密码格式正确';
}
}
</script>
每次点击后,先将所有按钮的颜色去除,最后给所点击的按钮设置背景颜色
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
<button>按钮5</button>
<script>
var btns = document.getElementsByTagName('button');
for (var i = 0; i < btns.length; i++) {
btns[i].onclick = function () {
for (var i = 0; i < btns.length; i++) {
btns[i].style.backgroundColor = '';
}
this.style.backgroundColor = 'tomato';
}
}
</script>
- 案例:百度换肤
<ul class="baidu">
<li><img src="images/1.jpg"></li>
<li><img src="images/2.jpg"></li>
<li><img src="images/3.jpg"></li>
<li><img src="images/4.jpg"></li>
</ul>
<script>
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 + ')';
}
}
</script>
this.src 就是所点击图片的路径document.body 获取 body 元素
用到两个新的鼠标事件:鼠标经过 onmouseover ;鼠标离开 onmouseout
- 案例:表单全选和反选
var j_cbAll = document.getElementById('j_cbAll');
var j_tbs = document.getElementById('j_tb').getElementsByTagName('input');
j_cbAll.onclick = function () {
for (var i = 0; i < j_tbs.length; i++) {
j_tbs[i].checked = this.checked;
}
}
for (var i = 0; i < j_tbs.length; i++) {
j_tbs[i].onclick = function () {
var flag = true;
for (var i = 0; i < j_tbs.length; i++) {
if (!j_tbs[i].checked) {
flag = false;
break;
}
}
j_cbAll.checked = flag;
}
}
1. element.属性
2. element.getAttribute('属性')
其中,element.属性 主要用于获取内置属性值(元素自带);而 element.getAttribute('属性') 主要用于获得我们自己定义的属性
<div id="demo" index="1"></div>
<script>
var div = document.querySelector('div');
console.log(div.id);
console.log(div.getAttribute('id'));
console.log(div.getAttribute('index'));
</script>
1. element.属性 = '值'
2. element.setAttribute('属性', '值')
主要用到设置自定义索引,和排他思想
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++) {
lis[i].setAttribute('index', i);
lis[i].onclick = function () {
for (var i = 0; i < lis.length; i++) {
lis[i].className = '';
}
this.className = 'current';
var index = this.getAttribute('index');
console.log(index);
for (var i = 0; i < items.length; i++) {
items[i].style.display = 'none';
}
items[index].style.display = 'block';
}
}
自定义属性的目的是为了保存并使用数据,但是有些自定义属性很容易引起歧义,不易判断是自定义属性还是内置属性。于是,H5 给出了相关定义:
H5 规定自定义属性 date- 开头做属性名,如
1. <div date-index="1"></div>
2. element.setAttribute('data-index', 2)
1. 兼容性获取 element.getAttribute('data-index')
2. H5 新增 elment.dataset.index 或 element.dataset['index']
-
dataset 是一个集合,里面存放了所有以 data- 开头的自定义属性 -
如果自定义属性采取多个 - 连接,如 data-list-name="andy" 。获取时应采用驼峰命名法 dataset.listName 或 dataset['listName'] ?
5. 节点操作
前面学习了利用 DOM 获取元素的几种方法,如
document.getElementById() document.getElementByTagName() document.querySelector - …
每次操作时需要获取很多事件源,而且相互之间逻辑性不强。于是下面给出利用节点间层次关系获取元素的方法,逻辑性强,便于操作。
一般地,节点至少拥有 nodeType(节点类型) 、nodeName(节点名称) 和 nodeValue(节点值) 三个基本属性。
- 元素节点
nodeType 为 1 - 属性节点
nodeType 为 2 - 文本节点
nodeType 为 3(包括 文字、空格、换行等)
利用 DOM 树可以把节点划分为不同的层级关系,常见的是父子兄级关系
node.parentNode
- parentNode 属性可返回某节点的父节点,注意是最近的一个父节点
- 如果指定的节点没有父节点则返回
null
1. parentNode.childNodes 标准,但不常用
注意:返回值里面包含了所有的子节点,包括元素节点、文本节点等。如果只需要获得元素节点,则需要专门处理,因此一般不提倡使用childNodes。
2. parentNode.children 非标准,比较常用
parentNode.children 是一个只读属性,返回所有的子元素节点。它只返回子元素节点,其余节点不返回。
3. parentNode.firstChild 返回第一个子节点
4. parentNode.lastChild 返回最后一个子节点
以上是针对所有节点操作的,包括元素节点、文本节点等
5. parentNode.firstElementChild 返回第一个子节点
6. parentNode.lastElementChild 返回最后一个子节点
这两种方法只针对元素节点操作,但存在兼容性问题,IE9 以上支持
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';
}
}
1. node.nextSibling 下一个兄弟节点
2. node.previousSibling 上一个兄弟节点
针对所有节点进行操作,找不到则返回 null
3. node.nextElementSibling 下一个兄弟元素节点
4. node.previousElementSibling 上一个兄弟元素节点
针对元素节点进行操作,找不到则返回 null ,但同样存在兼容性问题
document.createElement('tagName')
此方法创建由 tagName 指定的 HTML 元素。因为这些元素之前不存在,是根据需求动态生成的,因此也称之为动态创建元素节点。
1. node.appendChild(child)
此方法将一个节点添加到指定父节点的子节点列表末尾,类似 CSS 中 after 伪元素。
2. node.insertBefore(child, 指定元素)
此方法将一个节点添加到父节点的指定子节点前面,类似 CSS 中 before 伪元素。
- 案例:留言板
var ul = document.querySelector('ul');
var text = document.querySelector('textarea');
var btn = document.querySelector('button');
btn.onclick = function () {
if (!text.value) {
alert('您没有输入内容!');
return false;
}
var li = document.createElement('li');
li.innerHTML = text.value;
ul.appendChild(li);
}
node.removeChild(child)
此方法从 DOM 中删除一个子节点,并返回删除的节点
var ul = document.querySelector('ul');
var btn = document.querySelector('button');
btn.onclick = function () {
if (!ul.children.length)
this.disabled = true;
else
ul.removeChild(ul.children[0]);
}
var ul = document.querySelector('ul');
var text = document.querySelector('textarea');
var btn = document.querySelector('button');
btn.onclick = function () {
if (!text.value) {
alert('您没有输入内容!');
return false;
}
var li = document.createElement('li');
li.innerHTML = text.value + "<a href='javascript:;'>删除</a>";
ul.appendChild(li);
var a = li.querySelector('a');
a.onclick = function () {
ul.removeChild(this.parentNode);
}
}
注意:阻止链接跳转需要添加 javascript:void(0); 或者 javascript:;
1. node.cloneNode() 复制节点本身
2. node.cloneNode(true) 复制节点本身加子节点
此方法返回调用此方法的节点的一个副本,也可称为克隆/拷贝节点
注意:如果参数为空或者 false,则只复制节点本身,不复制其中子节点;如果参数为 true, 则会复制节点本身以及其中所有子节点
- 案例:动态生成表格
var datas = [{
name: '魏璎珞',
subject: 'JavaScript',
score: 100
}, {
name: '弘历',
subject: 'JavaScript',
score: 98
}, {
name: '傅恒',
subject: 'JavaScript',
score: 99
}, {
name: '明玉',
subject: 'JavaScript',
score: 88
}, {
name: '大猪蹄子',
subject: 'JavaScript',
score: 0
}];
var tbody = document.querySelector('tbody');
for (var i = 0; i < datas.length; i++) {
var tr = document.createElement('tr');
tbody.appendChild(tr);
for (var k in datas[i]) {
var td = document.createElement('td');
td.innerHTML = datas[i][k];
tr.appendChild(td);
}
var td = document.createElement('td');
td.innerHTML = "<a href='javascript:;'>删除</a>"
tr.appendChild(td);
}
var as = document.querySelectorAll('a');
for (var i = 0; i < as.length; i++) {
as[i].onclick = function () {
tbody.removeChild(this.parentNode.parentNode);
}
}
1. document.write()
2. element.innerHTML
3. document.createElement()
document.write() 是直接将内容写入页面的内容流,但是如果文档流执行完毕,则它会导致页面全部重绘innerHTML 是将内容写入某个 DOM 节点,不会导致页面全部重绘innerHTML 创建多个元素效率更高(不要拼接字符串,采取数组形式拼接),结构稍微复杂createElement() 创建多个元素效率略低,但是结构清晰
方式一:字符串拼接,耗时:768ms
var str = '';
for (var i = 0; i < 1000; i++) {
document.body.innerHTML += '<div></div>';
}
方式二:数组转字符串,耗时:3ms
var array = [];
for (var i = 0; i < 1000; i++) {
array.push('<div></div>');
}
document.body.innerHTML = array.join('');
方式三:元素创建追加,耗时:7ms
for (var i = 0; i < 1000; i++) {
var div = document.createElement('div');
document.body.appendChild(div);
}
document.body.innerHTML = array.join('');
总结:不同浏览器下,innerHTML 效率比 createElement 高 ?
三、思维导图
本篇文章主要所讲 DOM 操作思维导图如下:
|