本文最初发表于 博客园 , 并在 GitHub 上持续更新前端的系列文章. 欢迎在 GitHub 上关注我, 一起入门和进阶前端.
以下是正文.
前言
JavaScript 的组成
JavaScript 基础分为三个部分:
ECMAScript:JavaScript 的语法标准. 包括变量, 表达式, 运算符, 函数, if 语句, for 语句等.
DOM: 文档对象模型, 操作网页上的元素的 API. 比如让盒子移动, 变色, 轮播图等.
BOM: 浏览器对象模型, 操作浏览器部分功能的 API. 比如让浏览器自动滚动.
事件
JS 是以事件驱动为核心的一门语言.
事件的三要素
事件的三要素: 事件源, 事件, 事件驱动程序.
比如, 我用手去按开关, 灯亮了. 这件事情里, 事件源是: 手. 事件是: 按开关. 事件驱动程序是: 灯的开和关.
再比如, 网页上弹出一个广告, 我点击右上角的 X, 广告就关闭了. 这件事情里, 事件源是: X. 事件是: onclick. 事件驱动程序是: 广告关闭了.
于是我们可以总结出: 谁引发的后续事件, 谁就是事件源.
总结如下:
事件源: 引发后续事件的 html 标签.
事件: js 已经定义好了(见下图).
事件驱动程序: 对样式和 html 的操作. 也就是 DOM.
代码书写步骤如下:(重要)
(1)获取事件源: document.getElementById("box"); // 类似于 Android 里面的 findViewById
(2)绑定事件: 事件源 box. 事件 onclick = function(){ 事件驱动程序};
(3)书写事件驱动程序: 关于 DOM 的操作.
最简单的代码举例:(点击 box1, 然后弹框)
<body>
<div id="box1"></div>
<script type="text/javascript">
// 1, 获取事件源
var div = document.getElementById("box1");
// 2, 绑定事件
div.onclick = function () {
// 3, 书写事件驱动程序
alert("我是弹出的内容");
}
</script>
</body>
常见的事件如下:
下面针对这事件的三要素, 进行分别介绍.
1, 获取事件源的方式(DOM 节点的获取)
获取事件源的常见方式如下:
var div1 = document.getElementById("box1"); // 方式一: 通过 id 获取单个标签
var arr1 = document.getElementsByTagName("div1"); // 方式二: 通过 标签名 获得 标签数组, 所以有 s
var arr2 = document.getElementsByClassName("hehe"); // 方式三: 通过 类名 获得 标签数组, 所以有 s
2, 绑定事件的方式
方式一: 直接绑定匿名函数
<div id="box1" ></div>
<script type="text/javascript">
var div1 = document.getElementById("box1");
// 绑定事件的第一种方式
div1.onclick = function () {
alert("我是弹出的内容");
}
</script>
方式二: 先单独定义函数, 再绑定
<div id="box1" ></div>
<script type="text/javascript">
var div1 = document.getElementById("box1");
// 绑定事件的第二种方式
div1.onclick = fn; // 注意, 这里是 fn, 不是 fn().fn() 指的是返回值.
// 单独定义函数
function fn() {
alert("我是弹出的内容");
}
</script>
注意上方代码的注释. 绑定的时候, 是写 fn, 不是写 fn().fn 代表的是整个函数, 而 fn() 代表的是返回值.
方式三: 行内绑定
<!-- 行内绑定 -->
<div id="box1" onclick="fn()"></div>
<script type="text/javascript">
function fn() {
alert("我是弹出的内容");
}
</script>
注意第一行代码, 绑定时, 是写的 "fn()", 不是写的 "fn". 因为绑定的这段代码不是写在 js 代码里的, 而是被识别成了字符串.
3, 事件驱动程序
我们在上面是拿 alert 举例, 不仅如此, 我们还可以操作标签的属性和样式. 举例如下:
点击鼠标时, 原本粉色的 div 变大了, 背景变红:
<style>
#box1 {
width: 100px;
height: 100px;
background-color: pink;
cursor: pointer;
}
</style>
</head>
<body>
<div id="box1" ></div>
<script type="text/javascript">
var div1 = document.getElementById("box1");
// 点击鼠标时, 原本粉色的 div 变大了, 背景变红了
div1.onclick = function () {
div1.style.width = "200px"; // 属性值要写引号
div1.style.height = "200px";
div1.style.backgroundColor = "red"; // 属性名是 backgroundColor, 不是 background-Color
}
</script>
上方代码的注意事项:
在 js 里写属性值时, 要用引号
在 js 里写属性名时, 是 backgroundColor, 不是 CSS 里面的 background-Color.
实现效果如下:
onload 事件
onload 事件比较特殊, 这里单独讲一下.
当页面加载 (文本和图片) 完毕的时候, 触发 onload 事件.
举例:
<script type="text/javascript">
window.onload = function() {
console.log("smyhvae"); // 等页面加载完毕时, 打印字符串
}
</script>
有一点我们要知道: js 的加载是和 html 同步加载的. 因此, 如果使用元素在定义元素之前, 容易报错. 这个时候, onload 事件就能派上用场了, 我们可以把使用元素的代码放在 onload 里, 就能保证这段代码是最后执行.
建议是: 整个页面上所有元素加载完毕在执行 js 内容. 所以, window.onload 可以预防使用标签在定义标签之前.
事件举例: 京东顶部广告栏
比如上面这张图, 当鼠标点击右上角的 X 时, 关掉整个广告栏, 这就要用到事件.
代码实现如下:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<style>
* {
padding: 0;
margin: 0;
}
.top-banner {
background-color: pink;
height: 80px;
}
.w {
width: 1210px;
margin: 10px auto;
position: relative;
}
img {
width: 1210px;
height: 80px;
background-color: blue;
}
a {
position: absolute;
top: 5px;
right: 5px;
color: #fff;
background-color: #000;
text-decoration: none;
width: 20px;
height: 20px;
font: 700 14px/20px "simsum";
text-align: center;
}
.hide {
display: none!important;
}
</style>
</head>
<body>
<div class="top-banner" id="topBanner">
<div class="w">
<img src=""alt=""/>
<a href="#" id="closeBanner">×</a>
</div>
</div>
<script>
// 需求: 点击案例, 隐藏盒子.
// 思路: 点击 a 链接, 让 top-banner 这个盒子隐藏起来(加隐藏类名).
//1. 获取事件源和相关元素
var closeBanner = document.getElementById("closeBanner");
var topBanner = document.getElementById("topBanner");
//2. 绑定事件
closeBanner.onclick = function () {
//3. 书写事件驱动程序
// 类控制
// topBanner.className += "hide"; // 保留原类名, 添加新类名
topBanner.className = "hide";// 替换旧类名
// topBanner.style.display = "none";
}
</script>
</body>
</html>
注意最后一行代码, 这种方式会替换旧类名, 意思是, 不管之前的类名叫什么, 都会被修改.
事件举例:
要求实现效果: 当鼠标悬停在 img 上时, 更换为另外一张图片; 鼠标离开时, 还原为本来的图片.
代码实现:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<script>
//window.onload 页面加载完毕以后再执行此代码
window.onload = function () {
// 需求: 鼠标放到 img 上, 更换为另一张图片, 也就是修改路径(src 的值).
// 步骤:
//1. 获取事件源
//2. 绑定事件
//3. 书写事件驱动程序
//1. 获取事件源
var img = document.getElementById("box");
//2. 绑定事件 (悬停事件: 鼠标进入到事件源中立即出发事件)
img.onmouseover = function () {
//3. 书写事件驱动程序 (修改 src)
img.src = "image/jd2.png";
// this.src = "image/jd2.png";
}
//1. 获取事件源
var img = document.getElementById("box");
//2. 绑定事件 (悬停事件: 鼠标进入到事件源中立即出发事件)
img.onmouseout = function () {
//3. 书写事件驱动程序 (修改 src)
img.src = "image/jd1.png";
}
}
</script>
</head>
<body>
<img id="box" src="image/jd1.png" />
</body>
</html>
DOM 的介绍
什么是 DOM
DOM: 文档对象模型. DOM 为文档提供了结构化表示, 并定义了如何通过脚本来访问文档结构. 目的其实就是为了能让 js 操作 html 元素而制定的一个规范.
DOM 就是由节点组成的.
解析过程
HTML 加载完毕, 渲染引擎会在内存中把 HTML 文档, 生成一个 DOM 树, getElementById 是获取内中 DOM 上的元素节点. 然后操作的时候修改的是该元素的属性.
DOM 树(一切都是节点)
DOM 的数据结构如下:
上图可知, 在 HTML 当中, 一切都是节点:(非常重要)
元素节点: HMTL 标签.
文本节点: 标签中的文字(比如标签之间的空格, 换行)
属性节点:: 标签的属性.
整个 html 文档就是一个文档节点. 所有的节点都是 Object.
DOM 可以做什么
找对象(元素节点)
设置元素的属性值
设置元素的样式
动态创建和删除元素
事件的触发响应: 事件源, 事件, 事件的驱动程序
DOM 节点的获取
DOM 节点的获取方式其实就是获取事件源的方式, 在上一段已经讲到. 这里再重复一下.
操作元素节点, 必须首先找到该节点. 有三种方式可以获取 DOM 节点:
var div1 = document.getElementById("box1"); // 方式一: 通过 id 获取单个标签
var arr1 = document.getElementsByTagName("div1"); // 方式二: 通过 标签名 获得 标签数组, 所以有 s
var arr2 = document.getElementsByClassName("hehe"); // 方式三: 通过 类名 获得 标签数组, 所以有 s
既然方式二, 方式三获取的是标签数组, 那么习惯性是先遍历之后再使用.
特殊情况: 数组中的值只有 1 个. 即便如此, 这一个值也是包在数组里的. 这个值的获取方式如下:
document.getElementsByTagName("div1")[0]; // 取数组中的第一个元素
document.getElementsByClassName("hehe")[0]; // 取数组中的第一个元素
DOM 访问关系的获取
DOM 的节点并不是孤立的, 因此可以通过 DOM 节点之间的相对关系对它们进行访问. 如下:
节点的访问关系, 是以属性的方式存在的.
JS 中的父子兄访问关系:
这里我们要重点知道 parentNode 和 children 这两个属性的用法. 下面分别介绍.
获取父节点
调用者就是节点. 一个节点只有一个父节点, 调用方式就是
节点. parentNode
获取兄弟节点
1, 下一个节点 | 下一个元素节点:
Sibling 的中文是兄弟.
(1)nextSibling:
火狐, 谷歌, IE9 + 版本: 都指的是下一个节点(包括标签, 空文档和换行节点).
IE678 版本: 指下一个元素节点(标签).
(2)nextElementSibling:
火狐, 谷歌, IE9 + 版本: 都指的是下一个元素节点(标签).
总结: 为了获取下一个元素节点, 我们可以这样做: 在 IE678 中用 nextSibling, 在火狐谷歌 IE9 + 以后用 nextElementSibling, 于是, 综合这两个属性, 可以这样写:
下一个兄弟节点 = 节点. nextElementSibling || 节点. nextSibling
2, 前一个节点 | 前一个元素节点:
previous 的中文是: 前一个.
(1)previousSibling:
火狐, 谷歌, IE9 + 版本: 都指的是前一个节点(包括标签, 空文档和换行节点).
IE678 版本: 指前一个元素节点(标签).
(2)previousElementSibling:
火狐, 谷歌, IE9 + 版本: 都指的是前一个元素节点(标签).
总结: 为了获取前一个元素节点, 我们可以这样做: 在 IE678 中用 previousSibling, 在火狐谷歌 IE9 + 以后用 previousElementSibling, 于是, 综合这两个属性, 可以这样写:
前一个兄弟节点 = 节点. previousElementSibling || 节点. previousSibling
3, 补充: 获得任意一个兄弟节点:
节点自己. parentNode.children[index]; // 随意得到兄弟节点
获取单个的子节点
1, 第一个子节点 | 第一个子元素节点:
(1)firstChild:
火狐, 谷歌, IE9 + 版本: 都指的是第一个子节点(包括标签, 空文档和换行节点).
IE678 版本: 指第一个子元素节点(标签).
(2)firstElementChild:
火狐, 谷歌, IE9 + 版本: 都指的是第一个子元素节点(标签).
总结: 为了获取第一个子元素节点, 我们可以这样做: 在 IE678 中用 firstChild, 在火狐谷歌 IE9 + 以后用 firstElementChild, 于是, 综合这两个属性, 可以这样写:
第一个子元素节点 = 节点. firstElementChild || 节点. firstChild
2, 最后一个子节点 | 最后一个子元素节点:
(1)lastChild:
火狐, 谷歌, IE9 + 版本: 都指的是最后一个子节点(包括标签, 空文档和换行节点).
IE678 版本: 指最后一个子元素节点(标签).
(2)lastElementChild:
火狐, 谷歌, IE9 + 版本: 都指的是最后一个子元素节点(标签).
总结: 为了获取最后一个子元素节点, 我们可以这样做: 在 IE678 中用 lastChild, 在火狐谷歌 IE9 + 以后用 lastElementChild, 于是, 综合这两个属性, 可以这样写:
最后一个子元素节点 = 节点. lastElementChild || 节点. lastChild
获取所有的子节点
(1)childNodes: 标准属性. 返回的是指定元素的子节点的集合(包括元素节点, 所有属性, 文本节点). 是 W3C 的亲儿子.
火狐 谷歌等高本版会把换行也看做是子节点.
用法:
子节点数组 = 父节点. childNodes; // 获取所有节点.
(2)children: 非标准属性. 返回的是指定元素的子元素节点的集合.[重要]
它只返回 HTML 节点, 甚至不返回文本节点.
在 IE6/7/8 中包含注释节点(在 IE678 中, 注释节点不要写在里面).
虽然不是标准的 DOM 属性, 但它和 innerHTML 方法一样, 得到了几乎所有浏览器的支持.
用法:(用的最多)
子节点数组 = 父节点. children; // 获取所有节点. 用的最多.
nodeType
这里讲一下 nodeType.
nodeType == 1 表示的是元素节点(标签) . 记住: 元素就是标签.
nodeType == 2 表示是属性节点 了解
nodeType == 3 是文本节点 了解
DOM 节点操作(重要)
上一段的内容: 节点的访问关系都是属性.
本段的内容: 节点的操作都是函数(方法).
创建节点
格式如下:
新的标签 (元素节点) = document.createElement("标签名");
比如, 如果我们想创建一个 li 标签, 或者是创建一个不存在的 adbc 标签, 可以这样做:
<script type="text/javascript">
var a1 = document.createElement("li"); // 创建一个 li 标签
var a2 = document.createElement("adbc"); // 创建一个不存在的标签
console.log(a1);
console.log(a2);
console.log(typeof a1);
console.log(typeof a2);
</script>
打印结果:
插入节点
插入节点有两种方式, 它们的含义是不同的.
方式 1:
父节点. appendChild(新的子节点);
解释: 父节点的最后插入一个新的子节点.
方式 2:
父节点. insertBefore(新的子节点, 作为参考的子节点)
解释:
在参考节点前插入一个新的节点.
如果参考节点为 null, 那么他将在父节点最后插入一个子节点.
我们可以看到, li 标签确实被插入到了 box1 标签的里面, 和 box2 并列了.
方式 2 的举例:
我们可以看到, b1 标签被插入到了 box1 标签的里面, 和 a1 标签并列, 在 a1 标签的前面.
删除节点
格式如下:
父节点. removeChild(子节点);
解释: 用父节点删除子节点. 必须要指定是删除哪个子节点.
如果我想删除自己这个节点, 可以这么做:
node1.parentNode.removeChild(node1);
复制节点(克隆节点)
格式如下:
要复制的节点. cloneNode(); // 括号里不带参数和带参数 false, 效果是一样的.
要复制的节点. cloneNode(true);
括号里带不带参数, 效果是不同的. 解释如下:
不带参数 / 带参数 false: 只复制节点本身, 不复制子节点.
带参数 true: 既复制节点本身, 也复制其所有的子节点.
设置节点的属性
我们可以获取节点的属性值, 设置节点的属性值, 删除节点的属性.
我们就统一拿下面这个标签来举例:
<img src="images/1.jpg" class="image-box" title="美女图片" alt="地铁一瞥" id="a1">
下面分别介绍.
1, 获取节点的属性值
方式 1:
元素节点. 属性;
元素节点 [属性];
举例:(获取节点的属性值)
<body>
<img src="images/1.jpg" class="image-box" title="美女图片" alt="地铁一瞥" id="a1">
<script type="text/javascript">
var myNode = document.getElementsByTagName("img")[0];
console.log(myNode.src);
console.log(myNode.className); // 注意, 是 className, 不是 class
console.log(myNode.title);
console.log("------------");
console.log(myNode["src"]);
console.log(myNode["className"]); // 注意, 是 className, 不是 class
console.log(myNode["title"]);
</script>
</body>
上方代码中的 img 标签, 有各种属性, 我们可以逐一获取, 打印结果如下:
方式 2:(推荐)
元素节点. getAttribute("属性名称");
举例:
console.log(myNode.getAttribute("src"));
console.log(myNode.getAttribute("class")); // 注意是 class, 不是 className
console.log(myNode.getAttribute("title"));
打印结果:
方式 1 和方式 2 的区别在于: 前者是直接操作标签, 后者是把标签作为 DOM 节点. 推荐方式 2.
2, 设置节点的属性值
方式 1 举例:(设置节点的属性值)
myNode.src = "images/2.jpg" // 修改 src 的属性值
myNode.className = "image2-box"; // 修改 class 的 name
方式 2:(推荐)
元素节点. setAttribute(属性名, 新的属性值);
方式 2 举例:(设置节点的属性值)
myNode.setAttribute("src", "images/3.jpg");
myNode.setAttribute("class", "image3-box");
myNode.setAttribute("id", "你好");
3, 删除节点的属性
格式:
元素节点. removeAttribute(属性名);
举例:(删除节点的属性)
myNode.removeAttribute("class");
myNode.removeAttribute("id");
来源: https://www.cnblogs.com/smyhvae/p/8366012.html