JavaScript = ECMAScript(JS基础) + DOM +BOM
本文旨在介绍DOM (Document Object Model) 和 BOM (Browser Object Model) 的基本概念和相关操作,以及如何利用它们来实现动态交互效果和特效开发。
在DOM部分中,我们将详细介绍节点操作,包括如何访问元素节点、节点之间的关系、如何改变元素节点的内容,以及节点的创建、移除和克隆等操作。我们还将介绍如何使用事件监听来处理各种事件,并探讨事件传播和事件对象的相关知识。
另外,我们将深入讨论定时器和延时器的用法,以及如何利用它们来实现动画效果。我们还会介绍一些常用的BOM对象,如window对象、Navigator对象、History对象和Location对象,以及如何利用它们来实现一些特效开发。
通过阅读本文,你将掌握DOM和BOM的基本概念和操作方法,了解如何利用它们来制作动态交互和特效效果,提升网页的用户体验和交互性。
请你继续阅读,深入了解DOM和BOM的精彩世界。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Documenttitle>
head>
<body>
<h1>DOM基本概念h1>
<div>
<h2>程序员的梦工厂h2>
<img src="logo.png">
<div class="box">
DOM节点树
div>
div>
body>
html>
什么是document对象 ?
- document对象是DOM中最重要的东西,几乎所有DOM的功能都封装在了document对象中
- document对象也表示整个HTML文档,它是DOM节点树的根
- document对象的nodeType.属性值是9
<div id="box">我是一个盒子div>
var oBox = document.getElementById('box'); //参数就是元素节点的id,注意不要写#号
<p> 我是段落1 p>
<p> 我是段落2 p>
<p> 我是段落3 p>
var ps = document.getElementsByTagName('p'); //得到p标签的数组
var p2 = ps.getElementsByTagName('p')[1]; //得到第二个p标签
<div class="spec">
<p class="para"> 我是段落 p>
<p> 我是段落 p>
div>
var spec_divs = document.getElementsByClassName('spec');//注意不要写.号
var p1 = spec.getElementsByClassName('para');
<div id="box">
<p>我是段落p>
<p class="spec para">我是段落p>
div>
var the_p = document.querySelector('#box .spec');
<ul id="list1">
<li>我是lili>
<li>我是lili>
<li>我是lili>
<li>我是lili>
<li>我是lili>
ul>
<ul id="list2">
<li>我是lili>
<li>我是lili>
<li>我是lili>
<li>我是lili>
<li>我是lili>
ul>
var list1 = document.querySelectorAll('#list1 li'); //得到list1中li的内容
<div id="box">
<p>我是段落Ap>
<p>我是段落ap>
<p id="para">我是段落B
<span>1span>
<span id="sp">2span>
<span>3span>
p>
<p>我是段落bp>
<p>我是段落Cp>
<p>我是段落cp>
div>
var box = document.getElementById('box');
var para = document.getElementById('para');
// 1.封装第一个函数,这个函数可以返回元素的所有子元素节点(兼容到IE6)
function getChildren(node){
// 结果数组
var children = [];
// 遍历node这个节点的所有子节点,判断每一个子节点的nodeType的值
// 如果是1,就推入数组
for( var i =0; i < node.childNodes.length; i++){
if(node.childNodes[i].nodeType == 1){
children.unshift(node.childNodes[i]);
}
}
return children;
}
console.log(getChildren(box));
console.log(getChildren(para));
// 2.封装第二个函数,这个函数可以返回元素的前一个元素兄弟节点(兼容到IE6),类似previousElementSibling的功能
function getElementPrevSibling(node){
var o = node;
//使用while语句
while(o.previousSibling != null){
if(o.previousSibling.nodeType == 1){
//结束循环,找到了
return o.previousSibling;
}
// 让o成为它的前一个节点
o = o.previousSibling;
}
return null;
}
console.log(getElementPrevSibling(para));
console.log(getElementPrevSibling(sp));
// 3.封装第三个函数,这个函数可以返回元素的所有元素兄弟节点
function getAllElementSilbling(node){
// 前面的元素兄弟节点
var prevs = [];
// 后面的元素兄弟节点
var nexts = [];
var o = node;
//遍历node前面的节点
while(o.previousSibling != null){
if(o.previousSibling.nodeType == 1){
prevs.push(o.previousSibling);
}
o = o.previousSibling;
}
//遍历node后面的节点
o = node;
while (o.nextSibling != null){
if(o.nextSibling.nodeType == 1){
nexts.push(o.nextSibling);
}
o = o.nextSibling
}
return prevs.concat(nexts);
}
console.log(getAllElementSilbling(para));
<div id="box">div>
oBox.innerHTML = 'innerHTML学习'; //输出innerHTML学习
oBox.innerHTML = '- 牛奶
- 咖啡
'; //输出·牛奶 ·咖啡
oBox.innerText = 'innerText学习' ;//输出 innerText学习
oBox.innerText = '- 牛奶
- 咖啡
' ; //输出- 牛奶
- 咖啡
document.createElement0方法用于创建一个指定tagname的HTML元素
新创建出的节点是“孤儿节点”,这意味着它并没有被挂载到DOM树上,我们无法看见它
必须继续使用appendChild()或insertBefore()方法将孤儿节点插入到DOM树上
appendChild():
父节点.appendChild(孤儿节点);
insertBefore():
父节点.insertBefore(孤儿节点,标杆节点);
示例:
<div id="box">
<p>我是原本的段落0p>
<p>我是原本的段落1p>
<p>我是原本的段落2p>
div>
//访问需要添加的节点(父节点)
var oBox = document.getElementById('box');
// 第一步: 创建孤儿节点
var oP = document.createElement('p');
//设置内部文字
oP.innerText = '我是新来的';
// 第二步: 上树 appendChild()方法
oBox.appendChild(oP);
// 第二步: 上树 insertBefore()方法
var oPs = oBox.getElementsByTagName('p');
oBox.insertBefore(oP.oPs[0]);
如果将已经挂载到DOM树上的节点成为appendChild()或者insertBefore()的参数,这个节点将会被移动
新父节点.appendChild(已经有父亲的节点);
新父节点.insertBefore(已经有父亲的节点,标杆子节点);
这意味着一个节点不能同时位于DOM树的两个位置
示例:
<div id="box1">
<p id="para">我是段落p>
div>
<div id="box2">
<p>我是box2的原有p1标签p>
<p>我是box2的原有p2标签p>
div>
var box2 = document.getElementById('box2');
var para = document.getElementById('para');
var ps_box2 = box2.getElementsByTagName('p');
// box2.appendChild(para);
box2.insertBefore(para,ps_box2[1]);//将para插到box2的p2之前
removeChild()方法从DOM中删除一个子节点
节点不能主动删除自己,必须由父节点删除它
父节点.removeChild(要删除的节点);
box1.removeChild(para);
var 孤儿节点 = 老节点.cloneNode();
//相当于falsevar 孤儿节点 = 老节点.cloneNode(true);
oBox.style.cssText = "background-color: red; font-size: 32px;";
标准W3C属性,如src、href等,只需要打点进行更改即可
oImg.src = 'imgages/pic.jpg';
不符合W3C标准的属性,要使用setAttribute()和getAttribute()来设置、读取
bBox.setAttribute('data-n',10); //设置 (更改)
var n = oBox.getAttribute('data-n'); //读取
alert(n); //弹出10
事件 : 用户与网页的交互动作
监听 : 为了随时能够发现这个事件发生了,而执行预先编写的一些程序
最简单的设置事件监听的方法——设置它们的onxxx属性:
oBox.onclick = function(){
// 点击盒子时,将执行这里的语句
}
addEventListener()方法 addEventListener(type,listener,useCapture)
oBox.addEventListener('click',function(){
// 点击盒子时,将执行这里的语句
},true);
研究: 当盒子嵌套时事件监听的执行顺序 ? (鼠标点击中间盒子)
事件的传播顺序 : 先从外到内,再从内到外
注意事项:
oBox.onmousemove = function(e){}; //对象e就是这次事件的"事件对象"
e.clientX
e.preventDefault();
e.stopPropagation();
优点
原理
事件委托实现
e.target属性: 事件源元素,返回事件的目标节点
oBox.addEventListener('click',function(e){
e.target.style.backgroundColor = 'red'; //点击盒子时,盒子将变红
})
标准浏览器用 ev.target, IE浏览器用event.srcElement
e.currentTarget: 事件处理程序附加到的元素
使用场景
适用事件委托的事件
注意事项
setInterval()函数可以重复调用一个函数,在每次调用之间有固定的时间间隔
函数的参数
setInterval(function (a,b){
// 形式参数a的值是88,形式参数b的值是66
},2000,88,66);
具名函数也可以传入setInterval()
var a = 0
function fun(){
console.log(++a);
}
setInterval(fun,2000);//具名函数当中第一个参数,注意这里没有()
clearInterval()函数可以清除一个定时器
var a = 0;
//设置定时器,并且用变量timer接受这个定时器
var timer = setInterval(function(){
clearInterval(timer); //设表先关
console.log(++a);
},2000);
//点击按钮时,清除定时器
oBtn.onclick = function(){
clearInterval(timer); //清除定时器时,要传入定时器变量
}
设表先关 : 在多次点击按钮时会导致定时器叠加,数字会快速增加, 为了防止定时器叠加,我们应该在设置定时器时,也要清除之前的定时器
var timer = setTimeout(function(){ //这个函数会再两秒后执行一次 },2000);
clearInterval(timer);
使用定时器实现动画较为不便:
oList.innerHTML += oList.innerHTML;
var left = 0;
left = 0
,JS+CSS3结合实现动画
BOM(Browser Object Model,浏览器对象模型)是JS与浏览器窗口交互的接口
一些与浏览器改变尺寸、滚动条滚动相关的特效,都要借助BOM技术
window对象是当前JS脚本运行所处的窗口,而这个窗口中包含DOM结构,window.document属性就是document对象
在有标签页功能的浏览器中,每个标签都拥有自己的window对象;也就是说,同一个窗口的标签页之间不会共享一个window对象
全局变量会成为window对象的属性
内置函数普遍是window的方法
多个js文件之间是共享全局作用域的,即JS文件没有作用域隔离功能
document.documentElement.clientWidth
resize事件
在窗口大小改变之后,就会触发resize事件,可以用window.onresize或者window.addEventLinstener(‘resize’)来绑定事件处理函数
window.onresize = function (){
console.log('窗口宽度事件被触发了'+ window.innerWidth); //窗口宽度事件被触发了1613
}
已卷动高度
window.scrollY属性表示在垂直方向已滚动的像素值 (只读)
document.documentElement.scrollTop属性也表示窗口卷动高度(不是只读的)
var scrollTop = window.scrollY || document.documentElement.scrollTop; //提高网页兼容性的一种方式
scroll事件
在窗口被卷动之后,就会触发scroll事件,可以使用window.onscroll或者window.addEventListener(‘scroll’)来绑定事件处理函数
window.onscroll = function(){
console.log('窗口卷动事件被触发了'+window.scrollY); //窗口事件被触发了888
}
window.navigator属性可以检索navigator对象,它内部含有用户此次活动的浏览器的相关属性和表标识
console.log('浏览器官方名称:' + navigator.appName);//浏览器官方名称:Netscape
console.log('浏览器版本:' + navigator.appVersion);//浏览器版本:5.0
console.log('用户代理:' + navigator.userAgent); //用户代理:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36
console.log('用户操作系统:' + navigator.platform);//用户操作系统:Win32
history.back() 同于点击浏览器的回退按钮
history.go(-1) 等同于history.back();
btn.onclick = function (){
history.go(-1);
}
window.location = 'https://www.csdn.net/';
window.location.reload(true);
*{
margin: 0;
padding: 0;
}
.content-part{
width: 1200px;
margin: 0 auto;
margin-bottom: 30px;
background-color: #aeaeae;
font-size: 50px;
padding: 20px;
}
.floornav{
position: fixed;
right: 40px;
top: 50%;
margin-top: -120px;
height: 240px;
width:80px;
background-color: greenyellow;
}
.floornav ul {
list-style: none;
}
.floornav ul li{
width: 80px;
height: 40px;
font-size: 26px;
line-height: 40px;
text-align: center;
cursor:pointer;
}
.floornav ul li.current{
background-color: pink;
color:white;;
}
<nav class="floornav" id="floornav">
<ul id="list">
<li data-n = '体育' class="current">体育li>
<li data-n = '科技'>科技li>
<li data-n = '视频'>视频li>
<li data-n = '娱乐'>娱乐li>
<li data-n = '新闻'>新闻li>
<li data-n = '美食'>美食li>
ul>
nav>
<section class="content-part" style="height: 400px" data-n = '体育'>体育栏目section>
<section class="content-part" style="height: 200px" data-n = '科技'>科技栏目section>
<section class="content-part" style="height: 300px" data-n = '视频'>视频栏目section>
<section class="content-part" style="height: 500px" data-n = '娱乐'>娱乐栏目section>
<section class="content-part" style="height: 600px" data-n = '新闻'>新闻栏目section>
<section class="content-part" style="height: 400px" data-n = '美食'>美食栏目section>
// 使用事件委托给li添加监听
let list = document.getElementById('list');
let current = document.querySelector('.current');
let contenParts = document.querySelectorAll('.content-part');
let lis = document.querySelectorAll('#list li');
list.onclick = function (e){
if(e.target.tagName.toLowerCase() === 'li'){
// getAttribute表示得到标签身上的某个属性值
var n = e.target.getAttribute('data-n');
// 可以用属性选择器(就是方括号选择器)来寻找带有相同data-n的content-part
let contentPart = document.querySelector('.content-part[data-n='+n+']');
//让页面的卷动自动成为这个盒子的offsetTop值
document.documentElement.scrollTop = contentPart.offsetTop;
}
}
// 在页面加载好之后,将所有的content-part盒子的offsetTop值推入数组
var offsetTopArr = [];
// 遍历所有的contentPart,将他们的净位置推入数组
for( var i= 0;i < contenParts.length; i++){
offsetTopArr.push(contenParts[i].offsetTop);
}
console.log(offsetTopArr);
// 为了最后一项可以方便比较,我们可以推入一个无穷大
offsetTopArr.push(Infinity);
//当前楼层
var nowfloor =-1;
// 窗口的卷动
window.onscroll = function (){
// 遍历offsetTopArr数组,看看当前的scrollTop值在哪两个楼层之间
for(var i = 0;i<offsetTopArr.length;i++){
var scrolltop = document.documentElement.scrollTop;
if(scrolltop >= offsetTopArr[i] && scrolltop < offsetTopArr[i+1]){
break;
}
}
// 退出循环的时候,i是几,就表示当前楼层是几
console.log(i);
// 如果当前所在楼层不是i,表示环楼了(也是一种节流机制)
if(nowfloor !== i){
// 让全局变量改变为这个楼层号
nowfloor = i;
//设置下标为i的项有current
for (var j=0;j<lis.length;j++){
if(j===i){
lis[j].className = 'current';
}else{
lis[j].className = '';
}
}
}
};