前端学习 之 JavaScript DOM 与 BOM
一. DOM介绍
1. 什么是DOM?
DOM:文档对象模型。DOM 为文档提供了结构化表示,并定义了如何通过脚本来访问文档结构。
目的其实就是为了能让js操作html元素而制定的一个规范。
DOM就是由节点组成的。
2. 解析过程
HTML加载完毕,渲染引擎会在内存中把HTML文档,生成一个DOM树,getElementById是获取内中DOM上的元素节点。然后操作的时候修改的是该元素的属性。
3. DOM树(一切都是节点)
元素节点:HMTL标签。
文本节点:标签中的文字(比如标签之间的空格、换行)
属性节点::标签的属性。
整个html文档就是一个文档节点。所有的节点都是Object。
4. DOM可以做什么?
找对象(元素节点)
设置元素的属性值
设置元素的样式
动态创建和删除元素
事件的触发响应:事件源、事件、事件的驱动程序
DOM的数据结构如下:
上图可知,在HTML当中,一切都是节点:(非常重要)
文档节点(document对象):代表整个文档
元素节点(element 对象):代表一个元素(标签)
文本节点(text对象):代表元素(标签)中的文本
属性节点(attribute对象):代表一个属性,元素(标签)才有属性
注释是注释节点(comment对象)
JavaScript 可以通过DOM创建动态的 HTML:
JavaScript 能够改变页面中的所有 HTML 元素
JavaScript 能够改变页面中的所有 HTML 属性
JavaScript 能够改变页面中的所有 CSS 样式
JavaScript 能够对页面中的所有事件做出反应(鼠标点击事件,鼠标移动事件等)整个html文档就是一个文档节点。所有的节点都是Object。
4.DOM可以做什么?
找对象(元素节点)
设置元素的属性值
设置元素的样式
动态创建和删除元素
事件的触发响应:事件源、事件、事件的驱动程序
二. DOM节点的获取
DOM节点的获取方式其实就是获取事件源的方式。
操作元素节点,必须首先找到该节点。有三种方式可以获取DOM节点:
var div1 = document.getElementById("box"); //方式一:通过id获取单个标签
var arr1 = document.getElementsByTagName("div"); //方式二:通过 标签名 获得 标签数组,所以有s
var arr2 = document.getElementsByClassName("box"); //方式三:通过 类名 获得 标签数组,所以有s
既然方式二、方式三获取的是标签数组,那么习惯性是先遍历之后再使用。
特殊情况:数组中的值只有1个。即便如此,这一个值也是包在数组里的。这个值的获取方式如下:
document.getElementsByTagName("div")[0]; //取数组中的第一个元素
document.getElementsByClassName("box")[0]; //取数组中的第一个元素
三.DOM访问关系的获取
节点的访问关系,是以属性的方式存在的。
JS中的父子兄访问关系:
这里我们要重点知道parentNode和children这两个属性的用法比较常用。下面分别介绍。
下面的都以这个html为例子进行操作:
<div class="father">
<span class="s1"></span>
<p id="p1"><span></span></p>
<span class="s2"></span>
</div>
<div class="f">
<span class="s3"></span>
</div>
1. 获取父节点
调用者就是节点。一个节点只有一个父节点,调用方式就是
节点.parentNode
//获取父亲节点的方法具体例子
//获取p1节点
var p1 = document.getElementById('p1');
// 获取父亲节点
var fa = p1.parentNode;
console.log(fa);
2.获取兄弟节点
(1)下一个
(1)nextSibling:
指的是下一个节点(包括标签、空文档和换行节点)
火狐、谷歌、IE9+版本:都指的是下一个节点(包括标签、空文档和换行节点)。
IE678版本:指下一个元素节点(标签)。
本文用的是谷歌浏览器,所有这个方式只能获取到空文档代码见(2)后的例子及截图结果。
(2)nextElementSibling:
火狐、谷歌、IE9+版本:都指的是下一个元素节点(标签)。
js代码如下:
// 获取下一个节点
// 法一:
var pnext1 = p1.nextSibling; //var pnext1 = p1.nextSibling.nextSibling;
console.log(pnext1)
// 法二
var pnext2 = p1.nextElementSibling;
console.log(pnext2);
效果图为:
总结:为了获取下一个元素节点,我们可以这样做:在IE678中用nextSibling,在火狐谷歌IE9+以后用nextElementSibling。于是,综合这两个属性,可以这样写:
下一个兄弟节点 = 节点.nextElementSibling || 节点.nextSibling
// 综合方法三:
var pnext = p1.nextElementSibling || p1.nextSibling ;
console.log(pnext)
(2)前一个
previous的中文是: 前一个
(1)previousSibling:
火狐、谷歌、IE9+版本:都指的是前一个节点(包括标签、空文档和换行节点)。
IE678版本:指前一个元素节点(标签)。
本文用的是谷歌浏览器,所有这个方式只能获取到空文档代码见(2)后的例子及截图结果。
(2)previousElementSibling:
火狐、谷歌、IE9+版本:都指的是前一个元素节点(标签)。
// 获取上一个节点
// 法一:
var pre1 = p1.previousSibling; //var pre1 = p1.previousSibling.previousSibling;
console.log(pre1)
// 法二
var pre2 = p1.previousElementSibling;
console.log(pre2);
总结:为了获取前一个元素节点,我们可以这样做:在IE678中用previousSibling,在火狐谷歌IE9+以后用previousElementSibling,于是,综合这两个属性,可以这样写:
前一个兄弟节点 = 节点.previousElementSibling || 节点.previousSibling
// 综合方法三:
var pre = p1.previousElementSibling || p1.previousSibling;
console.log(pre)
补充:获得任意一个兄弟节点:
节点自己.parentNode.children[index]; //随意得到兄弟节点
节点自己.parentNode.children; //获取所有的兄弟节点(伪数组,包含自己在内)
3.获取单个的子元素
和2中的一样3中的(1)的方法也是存在不兼容问题,所有都可以2的解放方法一样,就不一一举例。
(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
4.获取所有的子节点
(1)childNodes
标准属性。返回的是指定元素的子节点的集合(包括元素节点、所有属性、文本节点)。是W3C的亲儿子。
火狐 谷歌等高本版会把换行也看做是子节点。(了解)
用法:
子节点数组 = 父节点.childNodes; //获取所有节点。
(2)children
非标准属性。返回的是指定元素的子元素节点的集合。【重要】
它只返回HTML节点,甚至不返回文本节点。
在IE6/7/8中包含注释节点(在IE678中,注释节点不要写在里面)。
虽然不是标准的DOM属性,但它和innerHTML方法一样,得到了几乎所有浏览器的支持。
用法:(用的最多)
子节点数组 = 父节点.children; //获取所有节点。用的最多。
四.关于DOM的事件操作
JS是以事件驱动为核心的一门语言。
HTML 4.0 的新特性之一是有能力使 HTML 事件触发浏览器中的动作(action),比如当用户点击某个 HTML 元素时启动一段 JavaScript。下面是一个属性列表,这些属性可插入 HTML 标签来定义事件动作。
常见事件如下:
onclick 当用户点击某个对象时调用的事件句柄。
ondblclick 当用户双击某个对象时调用的事件句柄。
onfocus 元素获得焦点。 // 练习:输入框
onblur 元素失去焦点。 应用场景:用于表单验证,用户离开某个输入框时,代表已经输入完了,我们可以对它进行验证.
onchange 域的内容被改变。 应用场景:通常用于表单元素,当元素内容被改变时触发.(select联动)
onkeydown 某个键盘按键被按下。 应用场景: 当用户在最后一个输入框按下回车按键时,表单提交.
onkeypress 某个键盘按键被按下并松开。
onkeyup 某个键盘按键被松开。
onload 一张页面或一幅图像完成加载。
onmousedown 鼠标按钮被按下。
onmousemove 鼠标被移动。
onmouseout 鼠标从某元素移开。
onmouseover 鼠标移到某元素之上。
onselect 在文本框中的文本被选中时发生。
onsubmit 确认按钮被点击,使用的对象是form。
1. 事件的三要素
事件的三要素:事件源、事件、事件驱动程序。
比如,我用手去按开关,灯亮了。这件事情里,事件源是:手。事件是:按开关。事件驱动程序是:灯的开和关。
再比如,网页上弹出一个广告,我点击右上角的X,广告就关闭了。这件事情里,事件源是:X。事件是:onclick。事件驱动程序是:广告关闭了。
于是我们可以总结出:谁引发的后续事件,谁就是事件源。
总结如下:
事件源:引发后续事件的html标签。
事件:js已经定义好了(见下图)。
事件驱动程序:对样式和html的操作。也就是DOM。
代码书写步骤如下:
获取事件源:document.getElementById(“box”);
绑定事件: 事件源box.事件onclick = function(){ 事件驱动程序 };
书写事件驱动程序:关于DOM的操作
代码举例一个事件例子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
div{
width: 100px;
height: 100px;
background: green;
}
</style>
</head>
<body>
<div class="box" onclick="foo()"></div><!-- 方式三绑定事件 -->
<script>
// 1.获取事件源
var div1 = document.getElementsByClassName("box")[0];
// 2.绑定事件
// 方式一:(通过匿名函数绑定,常用的)
div1.onclick = function () {
// 3.事件驱动程序
div1.style.backgroundColor = 'red';
};
// 方式二:
div1.onclick = foo; // 注意没有括号
// 通过命名函数绑定
function foo() {
// 3.事件驱动程序
div1.style.backgroundColor = 'red';
}
</script>
</body>
</html>
下面针对这事件的三要素,进行分别介绍。
(1)获取事件源的方式
// 1.获取事件源
var div1 = document.getElementsByClassName("box")[0];
(2)绑定事件的方式
方式一:直接绑定匿名函数(最常用)
<script>
var odiv = document.getElementById("box");
odiv.onclick = function(){
odiv.style.background = "red";
}
</script>
方式二:先单独定义函数,再绑定
<script>
var odiv = document.getElementById("box");
odiv.onclick = fn; //注意,这里是fn,不是fn()。fn()指的是返回值。
//单独定义函数
function fn(){
odiv.style.background = "red";
}
</script>
方式三:行内绑定
// 行内绑定
<div id="box" onclick="fn()"></div>
// 注意第一行代码,绑定时,是写的"fn()",不是写的"fn"。因为绑定的这段代码不是写在js代码里的,而是被识别成了字符串。
<script>
function fn(){
odiv.style.background = "red";
}
</script>
(3)事件驱动程序
我们上面的例子中改变背景颜色,就是事件驱动程序。
需求:
1. 默认盒子宽度和高度为100px,背景色为绿色;
2. 单击后的效果为盒子宽度和高度为200px,背景色为红色;
3. 让其上面的效果可以来回进行切换。
html代码:
<div class="box" ></div>
css样式:
<style>
div{
width: 100px;
height: 100px;
background: green;
}
</style>
js代码:
<script>
var div1 = document.getElementsByClassName("box")[0];
var flag = false;
div1.onclick = function () {
if (!flag) {
div1.style.width = "200px";
div1.style.height = "200px";
div1.style.backgroundColor = "red";
flag = true;
} else {
div1.style.width = "100px";
div1.style.height = "100px";
div1.style.backgroundColor = "green";
flag = false;
}
}
</script>
上方代码的注意事项:
在js里写属性值时,要用引号
在js里写属性名时,是backgroundColor,不是CSS里面的background-Color。
记得所有的像css属性的text-*,line-*、backgroun-*等在js中都写成驼峰。
(4)onload事件
当页面加载(文本和图片)完毕的时候,触发onload事件。
举例:
<head>
<script>
window.onload = function () {
console.log(111);
}
</script>
</head>
<body>
<script>
console.log(222);
</script>
</body>
有一点我们要知道:js的加载是和html同步加载的。因此,如果使用元素在定义元素之前,容易报错。这个时候,onload事件就能派上用场了,我们可以把使用元素的代码放在onload里,就能保证这段代码是最后执行。
建议是:整个页面上所有元素加载完毕在执行js内容。所以,window.onload可以预防使用标签在定义标签之前。
2. 事件案例
案例一:盒子的显示与隐藏
css样式:
<style>
div {
width: 100px;
height: 100px;
background: green;
}
</style>
html内容:
<input type="text" id="input1" value="隐藏">
<button id="btn1">隐藏</button>
<div id="box"></div>
js代码:
<script>
var oBtn = document.getElementById("btn1");
var oDiv = document.getElementById("box");
var oInput = document.getElementById("input1");
var flag = false;
oBtn.onclick = function () {
if (!flag){
oInput.value = "显示";
oDiv.style.display = "none";
oBtn.innerText = "<span>显示</span>";
flag = true;
}else{
//只进行赋值操作
oInput.value = "隐藏";
//事件驱动程序
oDiv.style.display = "block";
oBtn.innerHTML = "<span>隐藏</span>";
flag = false;
}
}
</script>
三个知识点:1.oInput.value 2.oBtn.innerText 3. oBtn.innerHTML
innerText 与innerHTML的区别在于,innerText直接将 "<span>显示</span>"里面的内容直接变成文本显示,而innerHTML将 "<span>隐藏</span>"里面的span内容语意话不显示span,
如下效果图:
innerText效果:
innerHTML效果:
案例二:
要求实现效果:
1. 当鼠标悬停在img上时,更换为另外一张图片;
2. 鼠标离开时,还原为本来的图片。
<img src="https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2121206715,2955288754&fm=26&gp=0.jpg" alt="" id="image">
<script>
var oImg = document.getElementById("image");
oImg.onmouseover = function () {
this.src = "https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3488187566,3835668069&fm=26&gp=0.jpg";
};
oImg.onmouseout = function () {
this.src = "https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2121206715,2955288754&fm=26&gp=0.jpg";
}
</script>
3. 事件冒泡
事件冒泡的条件:
必须是父子级关系。
必须是相同的绑定的方式(相同的事件)。
例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.father{
width: 500px;
height: 500px;
background: green;
}
.box1{
width: 300px;
height: 300px;
background: blue;
}
.box2{
width: 100px;
height: 100px;
background: red;
}
</style>
</head>
<body>
<div class="father">
<div class="box1">
<div class="box2"></div>
</div>
</div>
<script>
var father = document.getElementsByClassName("father")[0];
var box1 = document.getElementsByClassName("box1")[0];
var box2 = document.getElementsByClassName("box2")[0];
father.onclick = function () {
console.log("我是 father")
};
//加event防止冒泡
box1.onclick = function (event) {
event.stopPropagation();
console.log("我是 box1")
};
//function里面加event阻止冒泡
//然后加event.stopPropagation();阻止冒泡
box2.onclick = function (event) {
event.stopPropagation();
console.log("我是 box2")
};
</script>
</body>
</html>
解决方法:
function里面加event阻止冒泡
然后加event.stopPropagation();阻止冒泡
五. DOM节点操作(重要)
前面的内容:节点的访问关系都是属性。
节点的操作都是函数(方法)
1. 创建节点
格式如下:
新的标签(元素节点) = 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>
相应的效果图为:
2. 插入节点
插入节点有两种方式,它们的含义是不同的。
方式1:
父节点.appendChild(新的子节点);
解释:父节点的最后插入一个新的子节点。
方式2:
父节点.insertBefore(新的子节点,作为参考的子节点);
解释:
在参考节点前插入一个新的节点。
如果参考节点为null,那么他将在父节点最后插入一个子节点。
3. 删除节点
格式如下:
父节点.removeChild(子节点);
解释:用父节点删除子节点。必须要指定是删除哪个子节点。
如果我想删除自己这个节点,可以这么做:
node1.parentNode.removeChild(node1);
4. 复制节点(克隆节点)
格式如下:
要复制的节点.cloneNode(); //括号里不带参数和带参数false,效果是一样的。
要复制的节点.cloneNode(true);
括号里带不带参数,效果是不同的。解释如下:
不带参数/带参数false:只复制节点本身,不复制子节点。
带参数true:既复制节点本身,也复制其所有的子节点。
六.设置节点的属性
我们可以获取节点的属性值、设置节点的属性值、删除节点的属性。
我们就统一拿下面这个标签来举例:
<img src="https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2121206715,2955288754&fm=26&gp=0.jpg"
class="image_box" title="美女图片" alt="美女走丢了" id="a1">
下面分别介绍。
1. 获取节点的属性值
方式1:
元素节点.属性;
元素节点[属性];
举例:(获取节点的属性值)
<script>
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>
方式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
myNode['title'] = "漂亮吗"; // 修改title的属性值
方式2:(推荐)
元素节点.setAttribute(属性名, 新的属性值);
举例:(设置节点的属性值)
myNode.setAttribute("src","images/3.jpg");
myNode.setAttribute("class","image3-box");
myNode.setAttribute("id","aaa");
3. 删除节点属性
格式:
元素节点.removeAttribute(属性名);
举例:(删除节点的属性)
myNode.removeAttribute("class");
myNode.removeAttribute("id");
七. 定时器
在js中的定时器分两种:1、setTimeout() 2、setInterval()
1. setTimeOut()
只在指定时间后执行一次
<button id="btn1">开始</button>
<button id="btn2">清除</button>
<script>
var oBtn1 = document.getElementById("btn1");
var timer = null;
oBtn1.onclick = function () {
timer = setTimeout(function () {
console.log(1111);
console.log("我睡了三秒");
},3000)
};
var oBtn2 = document.getElementById("btn2");
oBtn2.onclick = function () {
clearTimeout(timer);
}
</script>
开始按完以后,只有按清除才能再按开始才能生效,反之同理。也就是上图需要按完清除才能再打印出我睡了三秒
2.setInterval()
在指定时间为周期循环执行
css样式为::
<style>
#box{
width: 100px;
height: 100px;
background: green;
}
</style>
html和js代码为:
<button id="start">开启定时器</button>
<button id="stop">关闭定时器</button>
<div id="box"></div>
<script>
var strat = document.getElementById("start");
var box = document.getElementById("box");
var stop = document.getElementById("stop");
var margin_left = 0;
var timer = null;
strat.onclick = function () {
clearInterval(timer);
timer = setInterval(function () {
margin_left += 10;
box.style.marginLeft = margin_left + 'px';
}, 500)
};
stop.onclick = function () {
//清除定时器
clearInterval(timer);
}
</script>
两种方法根据不同的场景和业务需求择而取之,
对于这两个方法,需要注意的是如果要求在每隔一个固定的时间间隔后就精确地执行某动作,那么最好使用setInterval。
八. BOM介绍
1. 什么是BOM
BOM:Browser Object Model,浏览器对象模型。
BOM的结构图:
从上图也可以看出:
window对象是BOM的顶层(核心)对象,所有对象都是通过它延伸出来的,也可以称为window的子对象。
DOM是BOM的一部分。
window对象:
window对象是JavaScript中的顶级对象。
全局变量、自定义函数也是window对象的属性和方法。
window对象下的属性和方法调用时,可以省略window。
下面讲一下 BOM 的常见内置方法和内置对象。
2. 弹出系统对话框
比如说,alert(1)是window.alert(1)的简写,因为它是window的子方法。
系统对话框有三种:
alert(); //不同浏览器中的外观是不一样的
confirm(); //兼容不好
prompt(); //不推荐使用
3. 打开窗口和关闭窗口
(1)打开窗口:
window.open(url,target)
参数解释:
url:要打开的地址。
target:新窗口的位置。可以是:_blank、_self、_parent 父框架。
例:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button>打开百度</button>
<button>关闭百度</button>
</body>
<script>
var oBtn = document.getElementsByTagName('button')[0];
var closeBtn = document.getElementsByTagName('button')[1];
var myWindow = null;
oBtn.onclick = function(){
myWindow = open('https://www.baidu.com');
//打开空白页面
// open('about:blank',"_self") };
closeBtn.onclick = function(){
if(confirm("是否关闭?")){
myWindow.close();
}
}
</script>
</html>
4. location对象
window.location可以简写成location。location相当于浏览器地址栏,可以将url解析成独立的片段。
(1) location对象的属性
href:跳转
hash 返回url中#后面的内容,包含#
host 主机名,包括端口
hostname 主机名
pathname url中的路径部分
protocol 协议 一般是http、https
search 查询字符串
location.href属性举例:
举例:点击盒子时,进行跳转
例:
html和js代码为:
<body>
<div>点我</div>
<script>
var div = document.getElementsByTagName("div")[0];
div.onclick = function () {
location.href = "http://www.baidu.com"; //点击div时,跳转到指定链接
// window.open("http://www.baidu.com","_blank"); //方式二
}
</script>
</body>
(2)location对象的方法
location.reload():重新加载
setTimeout(function(){
//3秒之后让网页整个刷新
window.location.reload();
},3000)
5. history对象
(1)后退:
history.back()
history.go(-1):0是刷新
(2) 前进:
history.forward()
history.go(1)
用的不多。因为浏览器中已经自带了这些功能的按钮
九.js中的面向对象
创建对象的几种常用方式:
1.使用Object或对象字面量创建对象
2.工厂模式创建对象
3.构造函数模式创建对象
4.原型模式创建对象
(未完待续)