MDN 详细 API : https://developer.mozilla.org/zh-CN/docs/Web/API
DOM 文档对象模型 , 处理 HTML 和 XML 文件的标准编程接口 , 通过这些接口可以改变网页内容 . 结构和样式
DOM 树
DOM 把以上内容都看做是对象
我们获取过来的DOM元素是一个对象(object),所以称为 文档对象模型
getElementByid() 和 document.querySelector(“选择器”) 返回的是元素
get 获得 element 元素 by 通过 驼峰命名法
文档下面找元素 : document.getElementById("time")
参数 id 是大小写敏感的字符串 , 必须加引号 ("time")
返回的是一个元素对象 object
var timer = document.getElementById("time");
console.log(timer);
console.log(typeof timer); // object
// console .dir 打印我们返回的元素对象,更好的查看里面的属性和方法
console.dir(timer)
获取某一类元素或者某一类标签
// 1. 返回的对象
var lis = document.getElementsByTagName("li");
console.log(lis);
console.log(lis[0]); // 获取索引为0的元素
// 2.要依次打印里面的元素对象 , 可以用遍历
for (var i = 0; i < lis.length; i++) {
console.log(lis[i]);
}
还可以获取某个元素 ( 父元素 ) 内部所有指定标签名的子元素
element.getElementsByTagName (“标签名”)
注意 : 父元素必须是单个对象 ( 必须指明是哪一个元素的对象 ) , 获取的时候不包括父元素自己
<ol id="ol">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ol>
// 方法一 父元素必须是单个对象 必须得加索引
// var ol = document.getElementsByTagName("ol"); //[ol]; // 伪数组
// console.log(ol[0].getElementsByTagName("li"));
// 方法二 元素必须是单个对象 给父元素取个 id 名
var ol = document.getElementById("ol"); //ol;
// element.getElementsByTagName ("标签名")
console.log(ol.getElementsByTagName("li"));
缺点 : 不兼容低版本浏览器 IE 6 7 8 , 移动端不限制
document.getElementsByClassName("类名")
根据类名返回元素对象集合document.querySelector("选择器")
根据指定选择器返回第一个元素对象 , 里面选择器需要加符号#nav .boxdocument.querySelectorAll ( "选择器" )
返回指定选择器的所有元素对象集合 //1. getElementsByClassName("类名")根据类名获得某些元素集合
var boxs = document.getElementsByClassName("box");
console.log(boxs);
//2. document.querySelector("选择器")根据选择器返回的第一个元素对象
var firstBox = document.querySelector(".box"); // 盒子1
console.log(firstBox);
var nav = document.querySelector("#nav"); // 首页,产品
console.log(nav);
var li = document.querySelector("li"); // 首页
console.log(li);
//3. querySelectorAll(选择器) 返回指定选择器的所有元素对象集合
var allBox = document.querySelectorAll(".box");
console.log(allBox);
var lis=document.querySelectorAll("li")
console.log(lis);
document.body; //
返回body 对象
// 1. 获取body 元素
var bodyEle = document.body;
console.log(bodyEle);
console.dir(bodyEle);
document.documentElement;
返回 html 对象
// 2. 获取html 元素
var htmlEle = document.documentElement;
console.log(htmlEle);
console.dir(htmlEle)
事件是可以被 JAveScript 侦测到的行为
简单理解 : 触发 — 响应机制
网页中的每个元素都可以产生某些可以触发 JAveScript 的事件 , 例如可以在用户点击某按钮时产生一个事件 , 然后去执行某些操作
事件有三部分组成 : 事件源 事件类型 事件处理程序
// 事件源
var btn = document.getElementById("btn");
// 事件源.事件类型=事件处理程序
btn.onclick = function () {
alert("点秋香");
};
// 点击div 控制台输出 我被选中了
// 1. 获取事件源
var div = document.querySelector("div");
// 2. 绑定事件 onclick
// 3. 添加事件处理程序
div.onclick=function(){
console.log("我被点击了");
}
常见的鼠标事件
JavaScript 的 DOM 操作可以改变网页内容 , 结构样式 , 我们可以利用 DOM 操作元素来改变元素里面的内容 , 属性等 . 注意以下都是属性
element . innerText
div.innerText = "内容";
从起始位置到终止位置的内容 , 但它去除 html 标签 , 同时空格和换行也会去掉 (不保留格式) , 非标准
案例 - 点击按钮 div 里面的内容发生变化
// 当我们点击了按钮 div 里面的文字会发生变化
// 1.获取元素
var btn = document.querySelector("button");
var div = document.querySelector("div");
// 2.绑定事件 3.添加事件处理程序
btn.onclick = function () {
// div.innerText = "2022-02-02";
// 修改 div 里面的内容 , 调用时间函数赋值到div
div.innerText = getDate();
};
// 封装一个时间函数
function getDate() {
var date = new Date();
var year = date.getFullYear();
var month = date.getMonth() + 1;
var dates = date.getDate();
var arr = ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"];
var day = date.getDay();
var h = date.getHours();
h = h < 10 ? "0" + h : h; // 不足10 补0
var m = date.getMinutes();
m = m < 10 ? "0" + m : m;
var s = date.getSeconds();
s = s < 10 ? "0" + s : s;
return "今年是:" + year + "年" + month + "月" + dates + "日 " + arr[day] + "\t" + h + ":" + m + ":" + s;
}
元素可以不用添加事件
// 1. 获取元素
var p = document.querySelector("p");
// 修改 p 里面的内容 , 调用时间函数赋值到 p
// 打开页面后直接显示时间,不用添加点击事件
p.innerText = getDate();
element . innerHTML
div.innerHTML = "内容";
起始位置到终止位置的全部内容 , 包括 html 标签 , 同时保留空格和换行 (保留格式) W3C标准
innerText 和 innerHTML 区别
这两个属性是可读写的 , 可以获取元素里面的内容
console.log(p.innerText); // 我是文字 123 不保留格式不识别html 标签
console.log(p.innerHTML); // 我是文字 123
innerText会去除空格和换行 (非标准的 ),而innerHTML会保留空格和换行 ( W3C 标准的)
innerText不会识别html,而innerHTML会识别
获取属性的值
元素对象.属性名
设置属性的值
元素对象.属性名 = 值
<body>
<button id="ldh">刘德华</button>
<button id="zxy">张学友</button>
<img src="./images/ldh.jpg" alt="" title="刘德华" />
<script>
// 1. 获取元素
var zxy = document.getElementById("zxy");
var ldh = document.getElementById("ldh");
var img = document.querySelector("img");
// 2. 绑定事件 3. 处理程序
zxy.onclick = function () {
// 修改图片地址属性
img.src = "./images/zxy.jpg";
// 修改图片提示文本属性
img.title = "张学友";
};
ldh.onclick = function () {
img.src = "./images/ldh.jpg";
img.title = "刘德华";
};
</script>
</body>
案例 - 分时问候并显示不同图片案例
案例分析
<body>
<img src="./images/s.gif" alt="" />
<div>上午好</div>
<script>
// 1. 获取元素
var img = document.querySelector("img");
var div = document.querySelector("div");
// 2.日期内置对象 得到当前小时数
var date = new Date();
var h = date.getHours();
// 3.判断小时数改变图片和文字信息
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>
</body>
利用 DOM 可以操作如下表单元素的属性 :
type , value , checked , selected , disabled
获取属性的值
元素对象.属性名
设置属性的值
// this 值向的是事件函数的调用者 btn
元素对象.属性名 = 值
表单元素中有一些属性如:disabled、checked、selected,元素对象的这些属性的值是布尔型。
<body>
<button>按钮</button>
<input type="text" value="输入内容" />
<script>
// 1.获取元素
var btn = document.querySelector("button");
var input = document.querySelector("input");
// 2. 绑定事件 3.事件处理程序
btn.onclick = function () {
// 表单里面的值是通过 value 开修改的
input.value = "我被点击了";
// 按钮点过一次后就禁用 谁禁用就给谁加
// btn.disabled = true;
this.disabled = true;
// this 值向的是事件函数的调用者 btn
};
</script>
</body>
案例 - 显示密码案例
案例分析
<style>
.box {
position: relative;
width: 400px;
border-bottom: 1px solid #ccc;
margin: 100px auto;
}
.box input {
width: 370px;
height: 30px;
outline: none;
border: 0;
}
.box img {
position: absolute;
top: 3px;
right: 3px;
width: 24px;
}
</style>
</head>
<body>
<!-- 布局 先放一个 div 里面放input和label
div 给一个下边框 , input 去掉边框和轮廓线
label 里面放 眼睛图片 ,用定位移动位置 -->
<div class="box">
<label for="">
<img src="./images/close.png" alt="" id="eye" />
</label>
<input type="password" name="" id="pwd" />
</div>
<script>
// 1.获取元素
var eye = document.getElementById("eye");
var pwd = document.getElementById("pwd");
// 2. 绑定事件 3.处理程序
var flag = 0;
eye.onclick = function () {
if (flag == 0) {
// 判断条件
pwd.type = "text";
eye.src = "./images/open.png";
flag = 1; // 赋值
} else {
pwd.type = "password";
eye.src = "./images/close.png";
flag = 0; // 赋值
}
// flag = !flag; // true false
};
</script>
</body>
我们可以通过 JS 修改元素的大小、颜色、位置等样式。
常用方式
元素对象.style.样式属性 = 值; 适用于修改样式少的
注意 :
<body>
<div></div>
<script>
// 1.获取元素
var div = document.querySelector("div");
// 2.注册事件 3 处理程序
div.onclick = function () {
// div.style 属性名 采用驼峰命名法
this.style.backgroundColor = "green";
this.style.width = "300px"; // this 指向函数的调用者
};
</script>
</body>
案例 - 淘宝二维码
案例分析
<body>
<div class="box">
淘宝二维码
<img src="./images/tao.png" alt="" />
<i class="close-btn">×</i>
<script>
// 1.获取元素
var btn = document.querySelector(".close-btn");
var box = document.querySelector(".box");
// 2.绑定事件 3.程序处理
btn.onclick = function () {
// 修改样式隐藏
box.style.display = "none";
};
</script>
</div>
</body>
案例 - 循环精灵图
案例分析
<script>
// 1. 获取元素 所有的小li
var lis = document.querySelectorAll("li");
console.log(lis);
for (var i = 0; i < lis.length; i++) {
// 让索引号 乘以 -44 就是每个li 的背景y坐标 index就是我们的y坐标
var index = i * -44; // 修改每个 li 的坐标 注意字符串拼接
lis[i].style.backgroundPosition = "0 " + index + "px";
}
</script>
获得焦点 onfocus
失去焦点 onblur
var text = document.querySelector("input");
text.onfocus = function () {
console.log("得到焦点");
};
text.onblur = function () {
console.log("失去焦点");
};
案例 - 显示隐藏文本框内容
案例分析
<body>
<input type="text" value="手机" />
<script>
var text = document.querySelector("input");
text.onfocus = function () {
// 判断里面的内容是不是默认文字
if (text.value === "手机") {
text.value = ""; // 是默认文字就清空表单
}
// 获得焦点需要把文本框里面的文字颜色变黑
text.style.color = "#333";
};
text.onblur = function () {
// 判断表单内容是否为空
if (text.value === "") {
text.value = "手机"; // 为空内容就改为默认文字
}
// 失去焦点要把文本框里面的颜色变浅色
text.style.color = "#999";
};
</script>
</body>
元素对象.className = 值; 适用于修改样式较多的
因为class是关键字,所以使用className。
className 会直接更改元素的类名 , 会覆盖原先的类名
this.className = "change";
需要在css 中写一个 class 类名样式
this.className = "first change";
如果想要保留原来的类名 , 可以采用在第一个类名后面追加你要的类名
文本
<style>
div {
width: 100px;
height: 100px;
text-align: center;
line-height: 100px;
background-color: pink;
}
.change {
background-color: green;
color: #fff;
font-size: 25px;
margin-top: 100px;
}
</style>
</head>
<body>
<div>文本</div>
<script>
// 1.获取元素
var div = document.querySelector("div");
// 2.绑定事件 3.事件处理程序
div.onclick = function () {
// 在 css中写一个class 样式
// 修改元素的 className 更改元素样式 适用于条件较多的情况下
this.className = "change";
// 如果想要保留原来的类名 , 可以采用在第一个类名后面追加你要的类名
this.className = "first change";
};
</script>
</body>
案例 - 密码长度验证信息
案例分析
div {
width: 600px;
margin: 100px auto;
}
.message {
display: inline-block;
font-size: 12px;
color: #999;
background: url(./images/mess.png) no-repeat left center;
padding-left: 20px;
}
/* 错误提示的信息 */
.wrong {
color: red;
background-image: url(./images/wrong.png);
}
/* 正确提示的信息 */
.right {
color: green;
background-image: url(./images/right.png);
}
</style>
</head>
<body>
<div class="register">
<input type="password" class="ipt" />
<p class="message">请输入6~16位密码</p>
</div>
<script>
// 1.获取元素
var ipt = document.querySelector(".ipt");
var message = document.querySelector(".message");
// 2.绑定事件 onblur 3.处理程序
ipt.onblur = function () {
// 根据表单里面值的长度 ipt.value.
// if (this.value.length <= 16 && this.value.length >= 6)
if (this.value.length < 6 || this.value.length > 16) {
console.log("错误");
// 用className 更改样式
// 不满足条件时
message.className = "message wrong";
message.innerHTML = "您输入的位数不对要求6~16位";
} else {
// 满足条件时
message.className = "message right";
message.innerHTML = "您输入的正确";
}
};
</script>
</body>
案例 - 开关灯
知识点
<body>
<button>关灯</button>
<script>
// 1.获取元素
var btn = document.querySelector("button");
// var bodyEle = document.body;
// 2.绑定事件 3.事件处理程序
var flag = 0; // flag 要写在函数体外面
btn.onclick = function () {
if (flag == 0) {
// bodyEle.style.backgroundColor = "black";
document.body.style.backgroundColor = "black";
this.innerHTML = "开灯";
flag = 1;
} else {
// bodyEle.style.backgroundColor = "#fff";
document.body.style.backgroundColor = "#fff";
this.innerHTML = "关灯";
flag = 0;
}
};
</script>
</body>
首先排除其他人 , 然后才设置自己的样式 , 这种排除其他人的思想我们称为排他思想
如果有同一组元素,我们想要某一个元素实现某种样式, 需要用到循环的排他思想算法:
核心思想 : 先用 for 循环把所有按钮背景颜色去掉 , 在设置让当前元素背景颜色
内循环干掉他人 ,外循环留下自己
知识点
注意 : 双重 for 循环 里面两个 i , 它们在不同函数里面 , 在不同作用域中
外循环不能用 btns[i] 涉及闭包
<script>
// 1.获取元素
var btns = document.getElementsByTagName("button");
// btns 得到的是伪数组 ,里面的每一个元素 btns[i]
// 用 for 循环给每个数组绑定事件
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) 然后才让当前元素的元素背景颜色改为绿色 留下自己
this.style.backgroundColor = "green";
// 不能用btns[i] 涉及闭包
};
}
//内循环干掉他人 ,外循环留下自己
</script>
① 这个案例练习的是给一组元素注册事件
② 给4个小图片利用循环注册点击事件
③ 当我们点击了这个图片,让我们页面背景改为当前的图片
④ 核心算法: 把当前图片的src 路径取过来,给 body 做为背景即可
知识点
// 1. 获取元素 获取百度下面的img
var imgs = document.querySelector(".baidu").querySelectorAll("img");
// 2.循环绑定事件
for (var i = 0; i < imgs.length; i++) {
imgs[i].onclick = function () {
// 点击图片 ,把当前图片的 src 地址给 body
// 当前图片地址 this.src 用字符串拼接
document.body.style.backgroundImage = "url(" + this.src + ")";
document.body.style.backgroundImage=`url(${this.src})`
};
}
</script>
案例分析
① 用到新的鼠标事件 鼠标经过 onmouseover 鼠标离开 onmouseout
② 核心思路:鼠标经过 tr 行,当前的行变背景颜色, 鼠标离开去掉当前的背景颜色
③ 注意: 第一行(thead里面的行)不需要变换颜色,因此我们获取的是 tbody 里面的行
知识点
<script>
// 1.获取元素 获取的是 tbody 里面所有的行
var trs = document.querySelector("tbody").querySelectorAll("tr");
console.log(trs);
// 2.利用循环绑定注册事件
for (var i = 0; i < trs.length; i++) {
// 3.鼠标经过 onmouseover
trs[i].onmouseover = function () {
this.className = "bg";
};
// 4.鼠标离开 onmouseout
trs[i].onmouseout = function () {
this.className = "";
};
}
</script>
业务需求:
方法一
案例分析
var j_cbAll = document.querySelector("#j_cbAll");
var j_tbs = document.querySelectorAll("#j_tb input");
var count = 0;
j_cbAll.onclick = function () {
for (var i = 0; i < j_tbs.length; i++) {
j_tbs[i].checked = this.checked;
}
// 全选框点击完, count 重新计数
if (this.checked) {
count = j_tbs.length;
} else {
count = 0;
}
};
for (var i = 0; i < j_tbs.length; i++) {
j_tbs[i].onclick = function () {
if (this.checked) {
count++;
} else {
count--;
}
if (count == j_tbs.length) {
j_cbAll.checked = true;
} else {
j_cbAll.checked = false;
}
};
}
方法二
// 1. 全选和取消全选做法: 让下面所有复选框的checked属性(选中状态) 跟随 全选按钮即可
// 获取元素
var j_cbAll = document.querySelector("#j_cbAll");
var j_tbs = document.querySelectorAll("#j_tb 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 == false) {
flag = false;
break; // 优化 检测到false 就不继续检测
}
}
// 当遍历已结束,还没有发现有一个小复选框是未勾选时,说明小复选框已全部被勾选
j_cbAll.checked = flag;
};
}
案例分析
① 全选和取消全选做法: 让下面所有复选框的checked属性(选中状态) 跟随 全选按钮即可
② 下面复选框需要全部选中, 上面全选才能选中做法: 给下面所有复选框绑定点击事件,每次点击,都要循环查看下面所有的复选框是否有没选中的,如果有一个没选中的, 上面全选就不选中。
③ 可以设置一个变量,来控制全选是否选中
知识点
全选和取消全选做法
// 1. 全选和取消全选做法: 让下面所有复选框的checked属性(选中状态) 跟随 全选按钮即可
// 获取元素
var j_cbAll = document.getElementById("j_cbAll"); // 全选按钮
var j_tbs = document.getElementById("j_tb").getElementsByTagName("input"); // 下面所有的复选框
// 绑定事件
j_cbAll.onclick = function ()
// console.log(j_cbAll.checked); // 打印上面复选框转态
// this.checked 它可以得到当前复选框的选中状态如果是true 就是选中,如果是false 就是未选中
// 用for 循环进行遍历 下面的复选框
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 () {
// flag 控制全选按钮是否被选中
var flag = true;
// 每次点击下面的复选框都要循环检查者4个小按钮是否全被选中
for (var i = 0; i < j_tbs.length; i++) {
if (!j_tbs[i].checked) {
// 判断条件 如果有四个中有一未选中
flag = false; // j_cbAll 默认选中转态就为false
break; // 退出for循环 这样可以提高执行效率 因为只要有一个没有选中,剩下的就无需循环判断了
}
}
j_cbAll.checked = flag;
};
}
console.log(div.id);
console.log(div.getAttribute("index"));
区别:
element.属性 获取内置属性值(元素本身自带的属性)img.src
element.getAttribute(‘属性’); 主要获得自定义的属性 (标准) 我们程序员自定义的属性
div.id = "test";
div.className="navs" // 写的className class 关键字
div.setAttribute("index", 2);
div.setAttribute("class","footer") // class比较特殊 就是写的class
区别:
element.属性 设置内置属性值
element.setAttribute(‘属性’); 主要设置自定义的属性 (标准), 还可以修改内置属性
element.removeAttribute(‘属性’);
div.removeAttribute("index")
案例分析
① Tab栏切换有2个大的模块
② 上面的模块选项卡,点击某一个,当前这一个底色会是红色,其余不变(排他思想) 修改类名的方式
③ 下面的模块内容,会跟随上面的选项卡变化。所以下面模块变化写到点击事件里面。
④ 规律:下面的模块显示内容和上面的选项卡一一对应,相匹配。
⑤ 核心思路: 给上面的tab_list 里面的所有小li 添加自定义属性,属性值从0开始编号。
⑥ 当我们点击tab_list 里面的某个小li,让tab_con 里面对应序号的 内容显示,其余隐藏(排他思想)
知识点
lis[i].setAttribute("index", i);
, 给每个 li 绑定点击事件 , 内 for 循环把每个 li lis[i].className = "";
属性设置为空清除 , 外循环给单前点击的 li 添加 this.className = "current";
属性 (排他思想)var index = this.getAttribute("index");
items[i].style.display = "none";
items[index].style.display = "block";
(排他思想) <script>
// 获取元素
var lis = document.querySelectorAll("li");
var items = document.querySelectorAll(".item");
// items 的返回值是一个数组 , 它的索引号正好和 li 的索引号数量相等
// 1. 上面 tab 栏
for (var i = 0; i < lis.length; i++) {
// for 循环绑定点击事件
lis[i].setAttribute("index", i); // 用for 循环动态添加自定义属性
// 1.上面的模块选项卡,点击某一个,当前这一个底色会是红色,其余不变(排他思想) 修改类名的方式;
lis[i].onclick = function () {
for (var i = 0; i < lis.length; i++) {
lis[i].className = ""; // 干掉所有人 清除所有li class 这个类名
}
// var currentLi=document.querySelector(".current")
// currentLi.className=""
this.className = "current"; // 留下自己
// 2.下面的显示内容模块
var index = this.getAttribute("index"); // 获取到当前 li 的自定义索引
// 所有的 items 设置隐藏
for (var i = 0; i < items.length; i++) {
items[i].style.display = "none";
}
items[index].style.display = "block"; // 当前点击索引对应的 item 设置为显示
};
}
</script>
自定义属性目的:是为了保存并使用数据。有些数据可以保存到页面中而不用保存到数据库中。
自定义属性获取是通过getAttribute(‘属性’) 获取。
但是有些自定义属性很容易引起歧义,不容易判断是元素的内置属性还是自定义属性。
H5给我们新增了自定义属性:
H5规定自定义属性data-开头做为属性名并且赋值。
比如
或者使用 JS 设置
element.setAttribute(‘data-index’, 2) div.setAttribute("data-index", 20);
div.getAttribute("data-index")
div.setAttribute("data-index", "1");
div.setAttribute("data-time", "20");
div.setAttribute("data-list-name", "slf");
console.log(div.getAttribute("data-index"));
console.log(div.getAttribute("data-time"));
console.log(div.getAttribute("data-list-name"));
注意 :
getAttribute 什么开头的属性都可以获取
dataset 只能获取 data- 开头的
<div data-index="10" data-time="20" data-list-name="andy"></div>
console.log(div.dataset.index); // 10
console.log(div.dataset["time"]); // 20
// 如果自定义属性里面有多个连接的单词 ,我们获取的时候采取 驼峰命名法
console.log(div.dataset.listName);
console.log(div.dataset["listName"]);
获取元素通常使用两种方式:
document.getElementById()
document.getElementsByTagName()
document.querySelector 等
逻辑性不强、繁琐
利用父子兄节点关系获取元素
逻辑性强, 但是兼容性稍差
这两种方式都可以获取元素节点,我们后面都会使用,但是节点操作更简单
网页中的所有内容都是节点(标签、属性、文本、注释等),在DOM 中,节点使用 node 来表示。
HTML DOM 树中的所有节点均可通过 JavaScript 进行访问,所有 HTML 元素(节点)均可被修改,也可以创建或删除。
一般地,节点至少拥有nodeType(节点类型)、nodeName(节点名称)和nodeValue(节点值)这三个基本属性。
我们在实际开发中,节点操作主要操作的是元素节点
利用 DOM 树可以把节点划分为不同的层级关系,常见的是父子兄层级关系。
node.parentNode
var span = document.querySelector(".son");
console.log(span.parentNode);
console.log(ul.childNodes);
console.log(ul.childNodes[0]);
console.log(ul.childNodes[0].nodeType); // 3 文本节点
console.log(ul.childNodes[1].nodeType); // 1 元素节点
parentNode.childNodes 返回包含指定节点的子节点的集合,该集合为即时更新的集合。
注意:返回值里面包含了所有的子节点,包括元素节点,文本节点等。
如果只想要获得里面的元素节点,则需要专门处理。 所以我们一般不提倡使用childNodes
father.children // 推荐 , 返回的是元素节点
father.childNodes // 存在兼容性问题
第一个子节点&最后一个子节点
father.children[0]; // 推荐 ,返回的是元素节点
father.children[father.children.length-1]; // 推荐
// 返回所有节点
father.firstChild;
father.lastChild;
// 兼容性问题 , 返回元素节点
father.firstElementChild;
father.lastElementChild;
var ul = document. querySelector(‘ul’);
for(var i = 0; i < ul.childNodes.length;i++) {
if (ul.childNodes[i].nodeType == 1) {
// ul.childNodes[i] 是元素节点
console.log(ul.childNodes[i]);
}
}
parentNode.children 是一个只读属性,返回所有的子元素节点。它只返回子元素节点,其余节点不返回 (这个是我们==重点掌握==的)。
虽然children 是一个非标准,但是得到了各个浏览器的支持,因此我们可以放心使用
实际开发中 :
// 返回的是元素节点 ,推荐 ie 9 以上支持
son.nextElementSibling;
son.previousElementSibling;
// 返回的是所有节点
son.nextSibling;
son.previousElementSibling;
长的有兼容性
案例分析
① 导航栏里面的li 都要有鼠标经过效果,所以需要循环注册鼠标事件
② 核心原理: 当鼠标经过li 里面的 第二个孩子 ul 显示, 当鼠标离开,则ul 隐藏
知识点
var lis = nav.children;
lis[i].children[1]
this.children[1].style.display = "block";
<script>
// 1. 获取元素
var nav = document.querySelector(".nav");
var lis = nav.children; // 得到四个小li 伪数组
// 2.循环绑定事件
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";
};
}
</script>
下一个兄弟元素节点(有兼容性问题)
1. node.nextSibling
nextSibling 返回当前元素的下一个兄弟元素节点,找不到则返回null。同样,也是包含所有的节点。包含元素节点或者文本节点等等
2. node.previousSibling
previousSibling 返回当前元素上一个兄弟元素节点,找不到则返回null。同样,也是包含所有的节点。
console.log(div.nextSibling); // 下一个兄弟节点 包含元素节点或者文本节点等等
console.log(div.previousSibling);
3. node.nextElementSibling
nextElementSibling 返回当前元素下一个兄弟元素节点,找不到则返回null。
node.previousElementSibling
previousElementSibling 返回当前元素上一个兄弟节点,找不到则返回null。
注意:这两个方法有兼容性问题, IE9 以上才支持。
console.log(div.nextElementSibling); // 下一个兄弟元素节点
console.log(div.previousElementSibling);
解决方案 : 封装一个函数
function getNextElementSibling(element) {
var el = element;
while (el = el.nextSibling) {
if (el.nodeType === 1) { // 判断条件节点类型是否位为元素节点
return el;
}
}
return null;
}
document. Element('tagName')
document.createElement() 方法创建由 tagName 指定的 HTML 元素。因为这些元素原先不存在,
是根据我们的需求动态生成的,所以我们也称为动态创建元素节点。
node 父亲, child 子级
node.appendChild() 方法将一个节点添加到指定父节点的子节点列表末尾。类似于 CSS 里面的
after 伪元素。
node.insertBefore() 方法将一个节点添加到父节点的指定子节点前面。类似于 CSS 里面的 before
伪元素。
页面要添加一个新的元素 :1.创建元素 2.添加元素
// 1.创建节点
var li = document.createElement("li");
// 2.添加节点 node.appendChild(child)
var ul = document.querySelector("ul");
ul.appendChild(li); // 父亲下面的儿子最后面添加节点 追加元素
// 3.添加节点 node.insertBefore(child ,指定元素前面)
var lili = document.createElement("li"); //
ul.insertBefore(lili, ul.children[0]);
案例分析
① 核心思路: 点击按钮之后,就动态创建一个li,添加到ul 里面。
② 创建li 的同时,把文本域里面的值通过li.innerHTML 赋值给 li
③ 如果想要新的留言后面显示就用 appendChild 如果想要前面显示就用insertBefore
知识点
var li = document.createElement("li");
li.innerHTML = text.value;
ul.insertBefore(li, ul.children[0]);
<script>
// 1. 获取元素
var btn = document.querySelector("button");
var text = document.querySelector("textarea");
var ul = document.querySelector("ul");
// 2.绑定事件
btn.onclick = function () {
if (text.value == "") {
// 判断条件
alert("没有输入内容");
return false; //后面的代码不会执行 , 所以不用else
}
// (1)创建元素
var li = document.createElement("li");
// 先有li 才能赋值
li.innerHTML = text.value;
// ul.appendChild(li); // 添加到父元素孩子后面
// (2)添加元素
ul.insertBefore(li, ul.children[0]); // 添加到第一个子元素前边
};
</script>
node.removeChild(child)
node.removeChild () 方法从 DOM 中删除一个子节点,返回删除的节点。
<script>
var ul = document.querySelector("ul");
var btn = document.querySelector("button");
btn.onclick = function () {
// 如果ul里面的孩子长度为 0说明都删完了
// if (!ul.children.length)
if (ul.children.length == 0) {
// 让按钮禁用
this.disabled = true;
} else {
// 删除节点 node.removeChild(child)
ul.removeChild(ul.children[0]);
}
};
</script>
知识点
li.innerHTML = text.value + '删除';
链接设置阻止跳转 href="javascript:;"
<script>
// 1. 获取元素
var btn = document.querySelector("button");
var text = document.querySelector("textarea");
var ul = document.querySelector("ul");
// 2.绑定事件
btn.onclick = function () {
// if (!text.value)
if (text.value == "") {
// 判断条件
alert("没有输入内容");
return false;
} else {
// (1)创建元素
var li = document.createElement("li");
// 先有li 才能赋值
li.innerHTML = text.value + '删除';
// ul.appendChild(li); // 添加到父元素孩子后面
// (2)添加元素
ul.insertBefore(li, ul.children[0]); // 添加到第一个子元素前边
// (3)删除元素 删除的是当前链接的 li 它的父亲
var as = document.querySelectorAll("a");
for (var i = 0; i < as.length; i++) {
as[i].onclick = function () {
// node.removechild(child)
// 删除的是当前链接的父亲 li
ul.removeChild(this.parentNode);
// 删除父节点下面的子节点 (子节点是当前按钮的父亲)
};
}
}
};
</script>
node.cloneNode()
node.cloneNode() 方法返回调用该方法的节点的一个副本。
注意:
案例分析
① 因为里面的学生数据都是动态的,我们需要js 动态生成。 这里我们模拟数据,自己定义好数据。 数据我们采取对象形式存储。
② 所有的数据都是放到tbody里面的行里面。
③ 因为行很多,我们需要循环创建多个行(对应多少人)
④ 每个行里面又有很多单元格(对应里面的数据),我们还继续使用循环创建多个单元格,并且把数据存入里面(双重for循环)
⑤ 最后一列单元格是删除,需要单独创建单元格。
⑥ 最后添加删除操作,单击删除,可以删除当前行。
知识点
var tr = document.createElement("tr");
tbody.appendChild(tr);
var td = document.createElement("td");
tr.appendChild(td);
td.innerHTML = datas[i][k];
var td = document.createElement("td");
td.innerHTML = '删除';
tr.appendChild(td);
tbody.removeChild(this.parentNode.parentNode);
*点击a 删除a 所在的行 链接的爸爸的爸爸 <script>
// 1.先去准备好学生的数据
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,
},
];
// 2. 往tbody 里面创建行: 有几个人(通过数组的长度)我们就创建几行
var tbody = document.querySelector("tbody");
for (var i = 0; i < datas.length; i++) {
// 外面的 for 循环控制行
// 1.创建 tr 行
var tr = document.createElement("tr");
// 把行插入到 tbody 中
tbody.appendChild(tr);
// 行里面创建单元格 td 单元格的数量取决于每个对象属性的个数
for (var k in datas[i]) {
// datas 数组里面的第i个对象
// 2.创建单元格 (跟数据有关系的3个单元格)
var td = document.createElement("td");
// 把单元格添加到当前行里面 列
// 把对象里面的属性的值 给 td 单元格
td.innerHTML = datas[i][k];
tr.appendChild(td);
} // 里面的for 循环控制列
// 3. 循环创建有删除的单元格 ,
var td = document.createElement("td");
td.innerHTML = '删除';
// 创建到当前行后面
tr.appendChild(td);
}
// 4删除操作
var as = document.querySelectorAll("a");
for (var i = 0; i < as.length; i++) {
as[i].onclick = function () {
// 点击a 删除a 所在的行 链接的爸爸的爸爸
tbody.removeChild(this.parentNode.parentNode);
};
}
// for(var k in obj) {
// k 得到的是属性名
// obj[k] 得到是属性值
// }
</script>
区别
innerTHML和createElement效率对比
innerHTML字符串拼接方式(效率低)
<script>
function fn() {
var d1 = +new Date();
var str = '';
for (var i = 0; i < 1000; i++) {
document.body.innerHTML += '';
}
var d2 = +new Date();
console.log(d2 - d1);
}
fn();
</script>
createElement方式(效率一般)
<script>
function fn() {
var d1 = +new Date();
for (var i = 0; i < 1000; i++) {
var div = document.createElement('div');
div.style.width = '100px';
div.style.height = '2px';
div.style.border = '1px solid red';
document.body.appendChild(div);
}
var d2 = +new Date();
console.log(d2 - d1);
}
fn();
</script>
innerHTML数组方式(效率高)⭐️⭐️
<script>
function fn() {
var d1 = +new Date();
var array = [];
for (var i = 0; i < 1000; i++) {
array.push('');
}
document.body.innerHTML = array.join('');
var d2 = +new Date();
console.log(d2 - d1);
}
fn();
</script>
关于dom操作,我们主要针对于元素的操作。主要有创建、增、删、改、查、属性操作、事件操作。
删除节点的父元素 ,亲父子关系
主要修改dom的元素属性,dom元素的内容、属性, 表单的值等
主要获取查询 dom 的元素
主要针对于自定义属性
给元素注册事件, 采取 事件源.事件类型 = 事件处理程序
注册事件有两种方式:传统方式和方法监听注册方式
传统注册方式
方法监听注册方式
该方法接收三个参数 :
btns[0].addEventListener("click", function () {
console.log(11);
});
btns[2].attachEvent("onclick",function(){
console.log(123);
})
封装一个函数,函数中判断浏览器的类型:
function addEventListener(element, eventName, fn) {
// 判断当前浏览器是否支持 addEventListener 方法
if (element.addEventListener) {
element.addEventListener(eventName, fn); // 第三个参数 默认是false
} else if (element.attachEvent) {
element.attachEvent('on' + eventName, fn);
} else {
// 相当于 element.onclick = fn; 传统方式
element['on' + eventName] = fn;
}
1. 传统注册方式
eventTarget.onclick = null;
divs[0].onclick = function () {
alert("11");
divs[0].onclick = null; // 删除事件
};
2.方法监听注册方式
① eventTarget.removeEventListener(type, listener[, useCapture]);
② eventTarget.detachEvent(eventNameWithOn, callback);
// 删除事件 removeEventListener
divs[1].addEventListener("click", fn);
function fn() {
divs[1].removeEventListener("click", fn);
}
div2[2].attachEvent("onclick", fn1);
function fn1() {
alert(44);
// 删除事件 detachEvent
div[2].detachEvent("onclick", fn1);
}
**删除事件兼容性解决方案 **(了解)
function removeEventListener(element, eventName, fn) {
// 判断当前浏览器是否支持 removeEventListener 方法
if (element.removeEventListener) {
element.removeEventListener(eventName, fn); // 第三个参数 默认是false
} else if (element.detachEvent) {
element.detachEvent('on' + eventName, fn);
} else {
element['on' + eventName] = null;
}
基于 DOM 树型结构触发事件的顺序 , 称为事件流
事件发生时会在元素节点之间按照特定的顺序传播 , 这个传播过程即 DOM 事件流
DOM 事件流会经历3个阶段:
注意
代码演示
事件冒泡
<div class="father">
<div class="son">son盒子</div>
</div>
<script>
// onclick 和 attachEvent(ie) 在冒泡阶段触发
// 冒泡阶段 如果addEventListener 第三个参数是 false 或者 省略
// son -> father ->body -> html -> document
var son = document.querySelector('.son');
// 给son注册单击事件
son.addEventListener('click', function() {
alert('son');
}, false);
// 给father注册单击事件
var father = document.querySelector('.father');
father.addEventListener('click', function() {
alert('father');
}, false);
// 给document注册单击事件,省略第3个参数
document.addEventListener('click', function() {
alert('document');
})
</script>
事件捕获
<div class="father">
<div class="son">son盒子</div>
</div>
<script>
// 如果addEventListener() 第三个参数是 true 那么在捕获阶段触发
// document -> html -> body -> father -> son
var son = document.querySelector('.son');
// 给son注册单击事件,第3个参数为true
son.addEventListener('click', function() {
alert('son');
}, true);
var father = document.querySelector('.father');
// 给father注册单击事件,第3个参数为true
father.addEventListener('click', function() {
alert('father');
}, true);
// 给document注册单击事件,第3个参数为true
document.addEventListener('click', function() {
alert('document');
}, true)
</script>
事件发生后,跟事件相关的一系列信息数据的集合都放到这个对象里面,这个对象就是事件对象。
事件对象只有有了事件才会存在 , 它是系统给我们自动创建的 , 不需要我们传递参数
比如:
事件触发发生时就会产生事件对象 (even),并且系统会以实参的形式传给事件处理函数。
所以,在事件处理函数中声明1个形参用来接收事件对象。
div.onclick = function (e) {
// 这个 event 就是事件对象
};
div.addEventListener("click", function (event) {
// 这个 event 就是事件对象
});
这个 event 是个形参,系统帮我们设定为事件对象,不需要传递实参过去。
当我们事件时, event 对象就会被系统自动创建,并依次传递给事件监听器(事件处理函数)。
事件对象本身的获取存在兼容问题:
解决:
e = e || window.event;
只要“||”前面为false, 不管“||”后面是true 还是 false,都返回 “||” 后面的值。
只要“||”前面为true, 不管“||”后面是true 还是 false,都返回 “||” 前面的值。
<div>123</div>
<script>
var div = document.querySelector('div');
div.onclick = function(e) {
// 事件对象
e = e || window.event;
console.log(e);
}
</script>
常情况下terget 和 this是一致的,
但有一种情况不同,那就是在事件冒泡时(父子元素有相同事件,单击子元素,父元素的事件处理函数也会被触发执行),
这时候this指向的是父元素,因为它是绑定事件的元素对象,
而target指向的是子元素,因为他是触发事件的那个具体元素对象。
事件冒泡下的e.target和this
// 区别: e.target 返回的是触发事件的对象 , this 返回的是绑定事件的对象
var ul = document.querySelector("ul");
ul.addEventListener("click", function (e) {
console.log(this); // ul this指向绑定事件的元素 ul
console.log(e.target); // li e.target 指向触发事件的的那个对象 li
});
e.defaultPrevented()
注意 : 方法要加小括号
html中一些标签有默认行为,例如a标签被单击后,默认会进行页面跳转。
<a href="http://www.baidu.com">百度</a>
<script>
// 2. 阻止默认行为 让链接不跳转
var a = document.querySelector('a');
a.addEventListener('click', function(e) {
e.preventDefault(); // dom 标准写法
});
// 3. 传统的注册方式
a.onclick = function(e) {
// 普通浏览器 e.preventDefault(); 方法
e.preventDefault();
// 低版本浏览器 ie678 returnValue 属性
e.returnValue = false;
// 我们可以利用return false 也能阻止默认行为 没有兼容性问题
return false;
}
</script>
// 传统方法
a.onclick = function () {
return false;
};
事件冒泡
e.stopPropagation();
e.cancelBubble = true;
注意 : 方法要加小括号
son.addEventListener("click",function (e) {
alert("son");
e.stopPropagation(); // 阻止冒泡
e.cancelBubble=true; // 非标准
},false
);
阻止事件冒泡的兼容性处理
if(e && e.stopPropagation){
e.stopPropagation();
}else{
window.event.cancelBubble = true;
}
事件委托也称为事件代理,在 jQuery 里面称为事件委派。
把本该绑定到当前元素的事件,绑定给他的父元素
说白了就是,不给子元素注册事件,给父元素注册事件,把处理代码在父元素的事件中执行。
不是每个子节点单独设置事件监听器,而是事件监听器设置在其父节点上,然后利用冒泡原理影响设置每个子节点。
给父元素注册事件,利用事件冒泡,当子元素的事件触发,会冒泡到父元素,然后去控制相应的子元素。
我们只操作了一次 DOM ,提高了程序的性能。
可以动态创建新的子元素绑定事件
<ul>
<li>知否知否,点我应有弹框在手!</li>
<li>知否知否,点我应有弹框在手!</li>
<li>知否知否,点我应有弹框在手!</li>
<li>知否知否,点我应有弹框在手!</li>
<li>知否知否,点我应有弹框在手!</li>
</ul>
<script>
// 事件委托的核心原理:给父节点添加侦听器, 利用事件冒泡影响每一个子节点
// 原理 点击li,因为li有事件冒泡 .li传给了ul , ul有点击事件
var ul = document.querySelector('ul');
ul.addEventListener('click', function(e) {
// e.target 这个可以得到我们点击的对象
e.target.style.backgroundColor = 'pink';
})
</script>
案例 - 动态添加子元素
知识点
.addEventListener
document.createElement("li");
innerHTML
value
li.innerHTML.length == 0
<script>
// 给按钮添加事件监听
document.querySelector("button").addEventListener("click", function () {
// 创建一个 li
var li = document.createElement("li");
// 把输入框的值赋值给 li
li.innerHTML = document.querySelector("input").value;
if (li.innerHTML.length == 0) {
alert("请输入有效内容");
} else {
// 把 li 插入到 ul 的子元素中
document.querySelector("ul").appendChild(li);
}
});
</script>
1.禁止鼠标右键菜单
contextmenu主要控制应该何时显示上下文菜单,主要用于程序员取消默认的上下文菜单
document.addEventListener("contextmenu", function (e) {
e.preventDefault();
});
2.禁止鼠标选中(selectstart 开始选中)
document.addEventListener("selectstart", function (e) {
e.preventDefault();
});
event对象代表事件的状态,跟事件相关的一系列信息的集合。现阶段我们主要是用鼠标事件对象
MouseEvent 和键盘事件对象 KeyboardEvent。
<script>
// 鼠标事件对象 MouseEvent
document.addEventListener('click', function(e) {
// 1. client 鼠标在可视区的x和y坐标
console.log(e.clientX);
console.log(e.clientY);
console.log('---------------------');
// 2. page 鼠标在页面文档的x和y坐标
console.log(e.pageX);
console.log(e.pageY);
console.log('---------------------');
// 3. screen 鼠标在电脑屏幕的x和y坐标
console.log(e.screenX);
console.log(e.screenY);
})
</script>
案例分析
① 鼠标不断的移动,使用鼠标移动事件: mousemove
② 在页面中移动,给document注册事件
③ 图片要移动距离,而且不占位置,我们使用绝对定位即可
④ 核心原理: 每次鼠标移动,我们都会获得最新的鼠标坐标, 把这个x和y坐标做为图片的top和left 值就可以移动图片
知识点
mouseover
e.pageX
pic.style.left = x +"px"
<body>
<img src="images/angel.gif" alt="" />
<script>
varlocation 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>
</body>
事件除了使用鼠标触发,还可以使用键盘触发, 注意给文档 document 添加键盘事件
键盘事件 | 触发条件 |
---|---|
onkeyup | 某个键盘按键被松开时触发 |
onkeydown | 某个按键被按下时触发 , 不区分大小写 . 只有大写 |
onkeypress | 某个按键被按下时触发 但它不识别功能键 如 shift window 箭头键 |
注意: onkeypress 和前面2个的区别是,它不识别功能键,比如左右箭头,shift 等。
三个事件的执行顺序 keydown – keypress – keyup
<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');
})
// 4. 三个事件的执行顺序 keydown -- keypress -- keyup
</script>
键盘事件对象属性 | 说明 |
---|---|
keyCode | 返回该键的ASCII值 |
注意:
案例分析
① 核心思路: 检测用户是否按下了s 键,如果按下s 键,就把光标定位到搜索框里面
② 使用键盘事件对象里面的keyCode 判断用户按下的是否是s键
③ 搜索框获得焦点: 使用 js 里面的 focus() 方法
知识点
keyup
if (e.keyCode === 83)
search.focus();
<script>
var search = document.querySelector("input");
// 用keyup 当键盘弹起后在进行判断 , 这样就不会把字符输入到输入框中了
document.addEventListener("keyup", function (e) {
console.log(e.keyCode); // 83
if (e.keyCode === 83) {
search.focus();
}
});
</script>
案例分析
① 快递单号输入内容时, 上面的大号字体盒子(con)显示(这里面的文字
② 同时把快递单号里面的值(value)获取过来赋值给 con盒子(innerText)做为内容
③ 如果快递单号里面内容为空,则隐藏大号字体盒子(con)盒子
④ 注意: keydown 和 keypress 在文本框里面的特点: 他们两个事件触发的时候,文字还没有落入文本框中。
⑤ keyup事件触发的时候, 文字已经落入文本框里面了
⑥ 当我们失去焦点,就隐藏这个con盒子
⑦ 当我们获得焦点,并且文本框内容不为空,就显示这个con盒子
知识点
<script>
// 快递单号输入内容时, 上面的大号字体盒子(con)显示(这里面的字号更大)
// 表单检测用户输入: 给表单添加键盘事件
// 同时把快递单号里面的值(value)获取过来赋值给 con盒子(innerText)做为内容
// 如果快递单号里面内容为空,则隐藏大号字体盒子(con)盒子
var con = document.querySelector(".con");
var jd_input = document.querySelector(".jd");
jd_input.addEventListener("keyup", function () {
// console.log('输入内容啦');
if (this.value == "") {
con.style.display = "none";
} else {
con.style.display = "block";
con.innerText = this.value;
}
});
// 当我们失去焦点,就隐藏这个con盒子
jd_input.addEventListener("blur", function () {
con.style.display = "none";
});
// 当我们获得焦点,就显示这个con盒子
jd_input.addEventListener("focus", function () {
if (this.value !== "") {
con.style.display = "block";
}
});
</script>
BOM(Browser Object Model)即浏览器对象模型,它提供了独立于内容而与浏览器窗口进行交互的对象,其核心对象是 window。
BOM 由一系列相关的对象构成,并且每个对象都提供了很多方法与属性。
BOM 缺乏标准,JavaScript 语法的标准化组织是 ECMA,DOM 的标准化组织是 W3C,BOM 最初是Netscape 浏览器标准的一部分。
window 对象是浏览器的顶级对象,它具有双重角色。
它是 JS 访问浏览器窗口的一个接口。
它是一个全局对象。定义在全局作用域中的变量、函数都会变成 window 对象的属性和方法。
在调用的时候可以省略 window,前面学习的对话框都属于 window 对象方法,如 alert()、prompt() 等。
注意:window下的一个特殊属性 window.name
this 指向 window 打印name 输出是 this.name=""
在全局对象中 this 指向window
onload
window.onload = function () {};
或者
window.addEventListener("load", function () {});
window.onload 是窗口 (页面)加载事件,当文档内容完全加载完成会触发该事件(包括图像、脚本文件、CSS 文件等), 就调用的处理函数。
注意
DOMContentLoaded
document.addEventListener("DOMContentLoaded", function () {
alert(33);
});
DOMContentLoaded 是 DOM 加载完毕 (标签) , 不包含图片 flash css 等就可以执行 , 加载速度的比 onload 要快一些
load 等页面内容全部加载完毕 , 包含页面 dom 元素 图片 flash css 等
页面加载顺序
<script>
window.addEventListener('load', function() {
var btn = document.querySelector('button');
btn.addEventListener('click', function() {
alert('点击我');
})
})
window.addEventListener('load', function() {
alert(22);
})
document.addEventListener('DOMContentLoaded', function() {
alert(33);
})
</script>
window.onresize=function(){}
window.addEventListener("resize", function () {})
window.onresize 是调整窗口大小加载事件, 当触发时就调用的处理函数。
window.addEventListener("resize", function () {
if (window.innerWidth <= 800) {
div.style.display = "none";
} else {
div.style.display = "block";
}
});
注意:
只要窗口大小发生像素变化,就会触发这个事件。
我们经常利用这个事件完成响应式布局。 window.innerWidth 当前屏幕的宽度
window 对象给我们提供了 2 个非常好用的方法-定时器。
setTimeout()
setInterval()
开启定时器
window.setTimeout(调用函数, [延迟的毫秒数]);
// 1.直接写函数
setTimeout(function () {
console.log("时间到了");
}, 2000);
// 2.写函数名
function callback() {
console.log("爆炸了");
}
var timer1 = setTimeout(callback, 3000);
var timer2 = setTimeout(callback, 5000);
setTimeout() 方法用于设置一个定时器,该定时器在定时器到期后执行调用函数。
注意:
setTimeout() 这个调用函数我们也称为回调函数 callback普通函数是按照代码顺序直接调用。而这个函数,需要等待时间,时间到了才去调用这个函数,因此称为回调函数。
简单理解: 回调,就是回头调用的意思。上一件事干完,再回头再调用这个函数。
以前我们讲的 element.onclick = function(){} 或者 element.addEventListener(“click”, fn); 里面的 函数也是回调
函数。
案例分析
<img src="./images/1.jpg" alt="" class="ad" />
<script>
var ad = document.querySelector(".ad");
setTimeout(function () {
ad.style.display = "none";
}, 5000);
window.clearTimeout(timeoutID)
clearTimeout()方法取消了先前通过调用 setTimeout() 建立的定时器。
btn.addEventListener("click", function () {
clearTimeout(timer);
});
开启定时器
window.setInterval(回调函数, [间隔的毫秒数]);
setInterval() 方法重复调用一个函数,每隔这个时间,就去调用一次回调函数。
setInterval(function () {
console.log("重复调用");
}, 2000);
setTimeout() 和 setInterval()区别
案列分析
① 这个倒计时是不断变化的,因此需要定时器来自动变化(setInterval)
② 三个黑色盒子里面分别存放时分秒
③ 三个黑色盒子利用innerHTML 放入计算的小时分钟秒数
④ 第一次执行也是间隔毫秒数,因此刚刷新页面会有空白
⑤ 最好采取封装函数的方式, 这样可以先调用一次这个函数,防止刚开始刷新页面有空白问题
<div>
<span class="hour">1</span>
<span class="minute">2</span>
<span class="second">3</span>
</div>
<script>
// 1. 获取元素(时分秒盒子)
var hour = document.querySelector('.hour'); // 小时的黑色盒子
var minute = document.querySelector('.minute'); // 分钟的黑色盒子
var second = document.querySelector('.second'); // 秒数的黑色盒子
var inputTime = +new Date("2021-11-29 00:00:00"); // 返回的是用户输入时间总的毫秒数
countDown(); // 我们先调用一次这个函数,防止第一次刷新页面有空白
// 2. 开启定时器
setInterval(countDown, 1000);
function countDown() {
var nowTime = +new Date(); // 返回的是当前时间总的毫秒数
var times = (inputTime - nowTime) / 1000; // times是剩余时间总的秒数
var h = parseInt(times / 60 / 60 % 24); //时
h = h < 10 ? '0' + h : h;
hour.innerHTML = h; // 把剩余的小时给 小时黑色盒子
var m = parseInt(times / 60 % 60); // 分
m = m < 10 ? '0' + m : m;
minute.innerHTML = m;
var s = parseInt(times % 60); // 当前的秒
s = s < 10 ? '0' + s : s;
second.innerHTML = s;
}
</script>
clearInterval ( intervalID );
clearInterval ()方法取消了先前通过调用 setInterval()建立的定时器。
timer 应设置全局变量这样其他函数才可以访问这个变量 , 变量赋值 null 合适 , 如果不赋值就是 undefined
<script>
var begin = document.querySelector(".begin");
var stop = document.querySelector(".stop");
var timer = null; // 全局变量 null 是一个空对象 只有设为全局变量 其他函数才能访问这个变量 ,变量赋值 null 合适
begin.addEventListener("click", function () {
timer = setInterval(function () {
console.log("你好吗");
}, 1000);
});
stop.addEventListener("click", function () {
clearInterval(timer);
});
</script>
案例分析
① 按钮点击之后,会禁用 disabled 为true
② 同时按钮里面的内容会变化, 注意 button 里面的内容通过 innerHTML修改
③ 里面秒数是有变化的,因此需要用到定时器
④ 定义一个变量,在定时器里面,不断递减
⑤ 如果变量为0 说明到了时间,我们需要停止定时器,并且复原按钮初始状态。
<body>
手机号码: <input type="number"> <button>发送</button>
<script>
var btn = document.querySelector('button');
var time = 3; // 定义剩下的秒数
btn.addEventListener('click', function() {
btn.disabled = true;
var timer = setInterval(function() {
if (time == 0) {
// 清除定时器和复原按钮
clearInterval(timer);
btn.disabled = false;
btn.innerHTML = '发送';
} else {
btn.innerHTML = '还剩下' + time + '秒';
time--;
}
}, 1000);
})
</script>
</body>
this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,一般情况下this的最终指向的是那个调用它的对象。
<script>
// 1.全局作用域中 普通函数中(定时器中this 指向window)
function a() {
console.log(this); // this 指向window
}
a();
setTimeout(function () {
console.log(this); // this 指向window
}, 1000);
// 2.对象方法中的 this 表示这个对象
var person = {
name: "张三",
age: 18,
eat: function (foodName) {
console.log(this); // this 指向这个对象 person
},
};
person.eat("苞谷");
// 3. 绑定事件的时候,事件处理函数中的this 表示绑定事件的调用者
document.querySelector("button").addEventListener("click", function () {
console.log(this); // this 指向调用者 btn
});
// 4.构造函数中 , this 表示创建出来的实例对象 , 在调用对象的时候才知道
function Cat(name, age) {
this.name = name;
this.age = age;
this.xx = function () {
console.log(this);
};
}
var c1 = new Cat("黑马", 11); // 表示创建出来的实例对象 C1
var c2 = new Cat("白马", 11); // 表示创建出来的实例对象 C2
</script>
JavaScript 语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。
console.log(1);
setTimeout(function () {
console.log(3);
}, 1000);
console.log(2);
// 1
// 2
// 3
利用多核 CPU 的计算能力,HTML5 提出 Web Worker 标准,允许 JavaScript 脚本创建多个线程。于是,JS 中出现了同步和异步。
前一个任务结束后再执行后一个任务,程序的执行顺序与任务的排列顺序是一致的、同步的。
可以同时进行多个任务 , 上一个任务等待时间较长可以进行下个任务 .
他们的本质区别: 这条流水线上各个流程的执行顺序不同。
console.log(1);
setTimeout(function () {
console.log(3);
}, 0);
console.log(2);
// 1
// 2
// 3
同步任务
同步任务都在主线程上执行,形成一个执行栈。
异步任务
JS 的异步是通过回调函数实现的。( 回调函数属于异步任务 )
一般而言,异步任务有以下三种类型:
异步任务相关回调函数添加到任务队列中(任务队列也称为消息队列)。
JS 把所有任务分为同步任务和异步任务 , 同步任务放到执行栈中 , 回调函数作为异步任务处理放到任务队列中 , 执行机制是先把执行栈中的同步任务全部执行完毕 , 再到任务队列中看有没有异步任务 , 如果有的话就把异步任务放到执行栈中再去执行 , 先执行同步任务再执行异步任务.
window 对象给我们提供了一个 location 属性用于获取或设置窗体的 URL,并且可以用于解析 URL 。 因为这个属性返回的是一个对象,所以我们将这个属性也称为 location 对象。
统一资源定位符 (Uniform Resource Locator, URL) 是互联网上标准资源的地址。互联网上的每个文件都有一个唯一的 URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。
URL 的一般语法格式为:
protocol://host[:port]/path/[?query]#fragment
http://www.itcast.cn/index.html?name=andy&age=18#link
location 对象属性 | 返回值 |
---|---|
location . href | 获取或设置整个 URL |
location . host | 返回主机域名 www.baidu.com |
location . port | 返回端口号 如果未返回 空字符串 |
location . pathname | 返回路径 |
location . search | 返回参数 ? 问号后面的参数 |
location . hash | 返回片段 #后面内容常见于锚点链接 |
重点记住: href 和 search
案例分析
① 利用定时器做倒计时效果
② 时间到了,就跳转页面。 使用 location.href
③封装函数 , 定时器开启之前调用一次 , 避免出现空白
<button>点击</button>
<div></div>
<script>
var btn = document.querySelector("button");
var div = document.querySelector("div");
btn.addEventListener("click", function () {
location.href = "http://www.mi.com";
// 点击跳转页面
});
var timer = 5;
countDown(); // 先调用一次这个函数,防止第一次刷新页面有空白
setInterval(countDown, 1000); // 开启定时器
function countDown() { // 封装函数
if (timer == 0) {
location.href = "http://www.mi.com";
} else {
div.innerHTML = "您将在" + timer + "秒后返回主页";
timer--;
}
}
</script>
</body>
案例分析
① 第一个登录页面,里面有提交表单, action 提交到 index.html页面
② 第二个页面,可以使用第一个页面的参数,这样实现了一个数据不同页面之间的传递效果
③ 第二个页面之所以可以使用第一个页面的数据,是利用了URL 里面的location.search参数
④ 在第二个页面中,需要把这个参数提取。
⑤ 第一步去掉? 利用 substr
⑥ 第二步 利用=号分割 键 和 值 split(‘=‘)
⑦ 第一个数组就是键 第二个数组就是值
<script>
console.log(location.search); // ?uname=slf
// 1. 先去掉? substr(起始位置, 截取几个字符,默认所有)
var params = location.search.substr(1); // uname=slf
// 2. 利用等号把字符串分割成数组 split split("=")
var arr = params.split("="); // ['uname', 'slf']
// 把数据写入 div 中
var div = document.querySelector("div");
div.innerHTML = arr[1] + "欢迎您";
</script>
location 对象方法 | 返回值 |
---|---|
location . assign() | 跟 href 一样 ,可以跳转页面 (也称为重定向页面) |
location . replace() | 替换当前页面 , 因为不记录历史 , 所以不能后退页面 |
location . reload() | 重新加载页面 , 相当于刷新按钮或者 f5 如果参数为true 强制刷新 ctrl + f5 |
<script>
var btn = document.querySelector("button");
btn.addEventListener("click", function () {
location.assign("http://www.mi.com"); // 记录浏览历史 , 可以后退
location.replace("http://www.mi.com"); // 不记录浏览历史 , 不可以后退
location.reload(); // 重新刷新页面
});
</script>
navigator 对象包含有关浏览器的信息,它有很多属性,我们最常用的是 userAgent,该属性可以返回由客户机发送服务器的 user-agent 头部的值。
下面前端代码可以判断用户那个终端打开页面,实现跳转
if((navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|
Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS
|Symbian|Windows Phone)/i))) {
window.location.href = ""; //手机
} else {
window.location.href = ""; //电脑
}
window 对象给我们提供了一个 history 对象,与浏览器历史记录进行交互。该对象包含用户(在浏览器窗口中)访问过的 URL。
history 对象方法 | 作用 |
---|---|
back() | 后退 |
forward () | 前进 |
go(参数) | 前进后退功能 , 1前进一个页面,-1后退一个页面 |
一般在一些 OA 办公系统中会遇到
<script>
var btn = document.querySelector("button");
btn.addEventListener("click", function () {
history.back();
history.forward();
});
</script>
offset 翻译过来就是偏移量, 我们使用 offset系列相关属性可以动态的得到该元素的位置(偏移)、大小等。
不包含 offset带来的偏移量
offset 系列常用属性
<body>
<div class="father">
<div class="son"></div>
</div>
<div class="w"></div>
<script>
// offset 系列
var father = document.querySelector('.father');
var son = document.querySelector('.son');
// 1.可以得到元素的偏移 位置 返回的不带单位的数值
console.log(father.offsetTop);
console.log(father.offsetLeft);
// 它以带有定位的父亲为准 如果么有父亲或者父亲没有定位 则以 body 为准
console.log(son.offsetLeft);
var w = document.querySelector('.w');
// 2.可以得到元素的大小 宽度和高度 是包含padding + border + width
console.log(w.offsetWidth);
console.log(w.offsetHeight);
// 3. 返回带有定位的父亲 否则返回的是body
console.log(son.offsetParent); // 返回带有定位的父亲 否则返回的是body
console.log(son.parentNode); // 返回父亲 是最近一级的父亲 亲爸爸 不管父亲有没有定位
</script>
</body>
offsetParent 和 parentNode 区别 ★
offset 可以得到任意样式表中的样式值
offset 系列获得的数值是没有单位的
offsetWidth 包含padding+border+width
offsetWidth 等属性是只读属性,只能获取不能赋值
所以,我们想要获取元素大小位置,用offset更合适
style 只能得到行内样式表中的样式值
style.width 获得的是带有单位的字符串
style.width 获得不包含padding和border 的值
style.width 是可读写属性,可以获取也可以赋值
所以,我们想要给元素更改值,则需要用style改变
案例分析
① 我们在盒子内点击,想要得到鼠标距离盒子左右的距离。
② 首先 标(e.pageX, e.pageY)
③ 其次得到盒子在页面中的距离 ( box.offsetLeft, box.offsetTop)
④ 用鼠标距离页面的坐标减去盒子在页面中的距离,得到 鼠标在盒子内的坐标
⑤ 如果想要移动一下鼠标,就要获取最新的坐标,使用鼠标移动事件 mousemove
<script>
var box = document.querySelector(".box");
box.addEventListener("click", function (e) {
// console.log("鼠标距离body右边框" + e.pageX);
// console.log("鼠标距离body上边框" + e.pageY);
// console.log("盒子距离页面右边距" + box.offsetLeft);
// console.log("盒子距离页面上边距" + box.offsetTop);
var x = e.pageX - this.offsetLeft;
var y = e.pageY - this.offsetTop;
this.innerHTML = "x坐标是" + x + "y坐标是" + y;
});
</script>
案例分析
弹出框,我们也称为模态框。
返回顶部
③ 在页面中拖拽的原理: 鼠标按下并且移动, 之后松开鼠标
④ 触发事件是鼠标按下 mousedown, 鼠标移动mousemove 鼠标松开 mouseup
⑤ 拖拽过程: 鼠标移动过程中,获得最新的值赋值给模态框的left和top值, 这样模态框可以跟着鼠标走了
⑥ 鼠标按下触发的事件源是 最上面一行,就是 id 为 title
⑦ 鼠标的坐标 减去 鼠标在盒子内的坐标, 才是模态框真正的位置。
⑧ 鼠标按下,我们要得到鼠标在盒子的坐标。
⑨ 鼠标移动,就让模态框的坐标 设置为 : 鼠标坐标 减去盒子坐标即可,注意移动事件写到按下事件里面。
⑩ 鼠标松开,就停止拖拽,就是可以让鼠标移动事件解除
<script>
// 1. 获取元素
var login = document.querySelector('.login');
var mask = document.querySelector('.login-bg');
var link = document.querySelector('#link');
var closeBtn = document.querySelector('#closeBtn');
var title = document.querySelector('#title');
// 2. 点击弹出层这个链接 link 让mask 和login 显示出来
link.addEventListener('click', function() {
mask.style.display = 'block';
login.style.display = 'block';
})
// 3. 点击 closeBtn 就隐藏 mask 和 login
closeBtn.addEventListener('click', function() {
mask.style.display = 'none';
login.style.display = 'none';
})
// 4. 开始拖拽
// (1) 当我们鼠标按下, 就获得鼠标在盒子内的坐标
title.addEventListener('mousedown', function(e) {
var x = e.pageX - login.offsetLeft;
var y = e.pageY - login.offsetTop;
// (2) 鼠标移动的时候,把鼠标在页面中的坐标,减去 鼠标在盒子内的坐标就是模态框的left和top值
document.addEventListener('mousemove', move) // 加给 document 不加给 title 是因为鼠标快速移动出title 框会停止移动
function move(e) {
login.style.left = e.pageX - x + 'px';
login.style.top = e.pageY - y + 'px';
}
// (3) 鼠标弹起,就让鼠标移动事件移除
document.addEventListener('mouseup', function() {
document.removeEventListener('mousemove', move);
})
})
</script>
问题 : 绑定鼠标移动和事件移出鼠标弹起的时候为什么要用 document?
案例分析
window.addEventListener('load', function() {
var preview_img = document.querySelector('.preview_img');
var mask = document.querySelector('.mask');
var big = document.querySelector('.big');
// 1. 当我们鼠标经过 preview_img 就显示和隐藏 mask 遮挡层 和 big 大盒子
preview_img.addEventListener('mouseover', function() {
mask.style.display = 'block';
big.style.display = 'block';
})
preview_img.addEventListener('mouseout', function() {
mask.style.display = 'none';
big.style.display = 'none';
})
// 2. 鼠标移动的时候,让黄色的盒子跟着鼠标来走
preview_img.addEventListener('mousemove', function(e) {
// (1). 先计算出鼠标在盒子内的坐标
var x = e.pageX - this.offsetLeft;
var y = e.pageY - this.offsetTop;
// console.log(x, y);
// (2) 减去盒子高度 300的一半 是 150 就是我们mask 的最终 left 和top值了
// (3) 我们mask 移动的距离
var maskX = x - mask.offsetWidth / 2;
var maskY = y - mask.offsetHeight / 2;
// (4) 如果x 坐标小于了0 就让他停在0 的位置
// 遮挡层的最大移动距离
var maskMax = preview_img.offsetWidth - mask.offsetWidth;
if (maskX <= 0) {
maskX = 0;
} else if (maskX >= maskMax) {
maskX = maskMax;
}
if (maskY <= 0) {
maskY = 0;
} else if (maskY >= maskMax) {
maskY = maskMax;
}
mask.style.left = maskX + 'px';
mask.style.top = maskY + 'px';
// 3. 大图片的移动距离 = 遮挡层移动距离 * 大图片最大移动距离 / 遮挡层的最大移动距离
// 大图
var bigIMg = document.querySelector('.bigImg');
// 大图片最大移动距离
var bigMax = bigIMg.offsetWidth - big.offsetWidth;
// 大图片的移动距离 X Y
var bigX = maskX * bigMax / maskMax;
var bigY = maskY * bigMax / maskMax;
bigIMg.style.left = -bigX + 'px';
bigIMg.style.top = -bigY + 'px';
})
})
client 翻译过来就是客户端,我们使用 client 系列的相关属性来获取元素可视区的相关信息。通过 client系列的相关属性可以动态的得到该元素的边框大小、元素大小等。
console.log(div.clientWidth); // 200px 不包含边框
console.log(div.offsetWidth); // 220px 包含边框
console.log(div.clientTop); // 上边框 10px
立即执行函数 (function(){})() 或者 (function(){}())
主要作用: 创建一个独立的作用域 , 里面所有变量都是局部变量。 避免了命名冲突问题
// 1. 立即执行函数: 不需要调用,立马能够自己执行的函数
function fn() {
console.log(1);
}
fn();
// 2. 写法 也可以传递参数进来
// 1.(function() {})() 或者 2. (function(){}());
(function(a, b) {
console.log(a + b);
var num = 10;
})(1, 2); // 第二个小括号可以看做是调用函数
(function sum(a, b) {
console.log(a + b);
var num = 10; // 局部变量
}(2, 3));
下面三种情况都会刷新页面都会触发 load 事件。
1.a标签的超链接
2.F5或者刷新按钮(强制刷新)
3.前进后退按钮
但是 火狐中,有个特点,有个“往返缓存”,这个缓存中不仅保存着页面数据,还保存了DOM和JavaScript的状态;实际上是将整个页面都保存在了内存里。
所以此时后退按钮不能刷新页面。
pageshow 照顾了火狐浏览器 , 后退也可以重新加载页面 , 不管是否来自缓存
pageshow会在load事件触发后触发;根据事件对象中的persisted来判断是否是缓存中的页面触发的 pageshow 事件
注意这个事件给window添加。
scroll 翻译过来就是滚动的,我们使用 scroll 系列的相关属性可以动态的得到该元素的实际大小、滚动距离等。
如果浏览器的高(或宽)度不足以显示整个页面时,会自动出现滚动条。当滚动条向下滚动时,页面上面被隐藏掉的高度,我们就称为页面被卷去的头部。滚动条在滚动时会触发 onscroll事件。
需求
原先侧边栏是绝对定位
当页面滚动到一定位置,侧边栏改为固定定位
页面继续滚动,会让 返回顶部显示出来
案例分析
① 需要用到页面滚动事件 scroll 因为是页面滚动,所以事件源是 document
② 滚动到某个位置,就是判断页面被卷去的上部值。
③ 页面被卷去的头部:可以通过window.pageYOffset 获得 如果是被卷去的左侧 window.pageXOffset
⑤ 其实这个值 可以通过盒子的 offsetTop 可以得到,如果大于等于这个值,就可以让盒子固定定位了
⑥ 页面被卷上去的距离大于主体部分被卷去的头部 . 就显示出来返回按钮 , 否则就隐藏
<script>
//1. 获取元素
var sliderbar = document.querySelector('.slider-bar');
var banner = document.querySelector('.banner');
// banner.offestTop 就是被卷去头部的大小 一定要写到滚动的外面
var bannerTop = banner.offsetTop
// 当我们侧边栏固定定位之后应该变化的数值
var sliderbarTop = sliderbar.offsetTop - bannerTop;
// 获取main 主体元素
var main = document.querySelector('.main');
var goBack = document.querySelector('.goBack');
var mainTop = main.offsetTop;
// 2. 页面滚动事件 scroll
document.addEventListener('scroll', function() {
// window.pageYOffset 页面被卷去的头部
// console.log(window.pageYOffset);
// 3 .当我们页面被卷去的头部大于等于了 172 此时 侧边栏就要改为固定定位
if (window.pageYOffset >= bannerTop) {
sliderbar.style.position = 'fixed';
sliderbar.style.top = sliderbarTop + 'px';
} else {
sliderbar.style.position = 'absolute';
sliderbar.style.top = '300px';
}
// 4. 当我们页面滚动到main盒子,就显示 goback模块
if (window.pageYOffset >= mainTop) {
goBack.style.display = 'block';
} else {
goBack.style.display = 'none';
}
})
</script>
需要注意的是,页面被卷去的头部,有兼容性问题,因此被卷去的头部通常有如下几种写法:
function getScroll() {
return {
left: window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft||0,
top: window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0
};
}
使用的时候 getScroll().left
从外到内一层的减少
他们主要用法:
1.offset系列 经常用于获得元素位置 offsetLeft offsetTop
2.client经常用于获取元素大小 clientWidth clientHeight
3.scroll 经常用于获取滚动距离 scrollTop scrollLeft
4.注意页面滚动的距离通过 window.pageXOffset 获得
mouseover 为什么会触发两次 ?
鼠标经过父盒子触发一次 , 经过子盒子 , 子盒子得到鼠标经过 , 没有绑定事件 , 子盒子会冒泡到父盒子 , 父盒子绑定了事件又会触发一次
核心原理:通过定时器 setInterval() 不断移动盒子位置。
实现步骤:
<script>
// 动画原理
// 1. 获得盒子当前位置
// 2. 让盒子在当前位置加上1个移动距离
// 3. 利用定时器不断重复这个操作
// 4. 加一个结束定时器的条件
// 5. 注意此元素需要添加定位, 才能使用element.style.left
var div = document.querySelector("div");
var timer = setInterval(function () {
if (div.offsetLeft >= 400) {
// 停止动画 本质是停止定时器
return clearInterval(timer);
}
div.style.left = div.offsetLeft + 1 + "px";
}, 30);
</script>
注意函数需要传递2个参数 , obj 目标对象 , target 目标移动到的位置
<script>
// 简单动画函数封装obj目标对象 target 目标位置
function animate(obj, target) {
var timer = setInterval(function () {
if (obj.offsetLeft >= target) {
// 停止动画 本质是停止定时器
return clearInterval(timer);
}
obj.style.left = obj.offsetLeft + 1 + "px";
}, 30);
}
var div = document.querySelector("div");
var span = document.querySelector("span");
// 调用函数
animate(div, 300);
animate(span, 200);
</script>
原理 : 利用给对象添加属性方法来实现赋值的操作 ;
原理 : 利用 JS 是一门动态语言,可以很方便的给当前对象添加属性。
避免了 var 声明变量 , 不用去内存中开辟空间 , 节约了内存资源
通过元素名赋值 , 使每个元素都有各自的定时器
var obj={};
obj.name="andy"
按钮点击越来越快?
连续点击会不断的开启新的定时器 , 所以速度会越来越快 , 解决方案是只让有一个定时器 , 运行之前先清除以前的定时器 , 只保留当前的一个定时器来运行.
clearInterval(obj.timer);
代码演示
function animate(obj, target) {
// 当我们不断的点击按钮,这个元素的速度会越来越快,因为开启了太多的定时器
// 解决方案就是 让我们元素只有一个定时器执行
// 先清除以前的定时器,只保留当前的一个定时器执行
clearInterval(obj.timer);
obj.timer = setInterval(function() {
if (obj.offsetLeft >= target) {
// 停止动画 本质是停止定时器
return clearInterval(obj.timer);
}
obj.style.left = obj.offsetLeft + 1 + 'px';
}, 30);
}
function animate(obj, target, callback) {
clearInterval(obj.timer); // 调用之前清除定时器
obj.timer = setInterval(function () {
if (obj.offsetLeft == target) {
// if (callback) { // 存在callback 就执行这个函数
// callback();
// }
callback && callback(); // 逻辑与判断 回调函数
return clearInterval(obj.timer);
}
obj.style.left = obj.offsetLeft + 1 + "px";
});
}
var box = document.querySelector(".box");
var box2 = document.querySelector(".box2");
// 页面加载完 box 调用动画函数,并且有回调函数
animate(box, 100, function () {
box.style.backgroundColor = "red";
});
// 点击按钮 box2 调用动画函数,没有回调函数
var btn = document.querySelector("button");
btn.addEventListener("click", function () {
animate(box2, 300, function () {
box2.style.backgroundColor = "green";
});
});
缓动动画就是让元素运动速度有所变化,最常见的是让速度慢慢停下来
思路:
可以让动画函数从 800 移动到 500。
当我们点击按钮时候,判断步长是正值还是负值
1.如果是正值,则步长往大了取整
2.如果是负值,则步长 向小了取整
案例分析
2. 核心算法:(目标值 - 现在的位置) / 10 做为每次移动的距离 步长
3. 停止的条件是: 让当前盒子位置等于目标位置就停止定时器
<script>
// 缓动动画函数封装obj目标对象 target 目标位置
// 思路:
// 1. 让盒子每次移动的距离慢慢变小, 速度就会慢慢落下来。
// 2. 核心算法:(目标值 - 现在的位置) / 10 做为每次移动的距离 步长
// 3. 停止的条件是: 让当前盒子位置等于目标位置就停止定时器
function animate(obj, target) {
// 先清除以前的定时器,只保留当前的一个定时器执行
clearInterval(obj.timer);
obj.timer = setInterval(function() {
// 步长值写到定时器的里面
// 把我们步长值改为整数 不要出现小数的问题
// var step = Math.ceil((target - obj.offsetLeft) / 10);
var step = (target - obj.offsetLeft) / 10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);
if (obj.offsetLeft == target) {
// 停止动画 本质是停止定时器
clearInterval(obj.timer);
}
// 把每次加1 这个步长值改为一个慢慢变小的值 步长公式:(目标值 - 现在的位置) / 10
obj.style.left = obj.offsetLeft + step + 'px';
}, 15);
}
var span = document.querySelector('span');
var btn500 = document.querySelector('.btn500');
var btn800 = document.querySelector('.btn800');
btn500.addEventListener('click', function() {
// 调用函数
animate(span, 500);
})
btn800.addEventListener('click', function() {
// 调用函数
animate(span, 800);
})
// 匀速动画 就是 盒子是当前的位置 + 固定的值 10
// 缓动动画就是 盒子当前的位置 + 变化的值(目标值 - 现在的位置) / 10)
</script>
回调函数 :上一个事件执行完后再来执行这个事件
回调函数原理:函数可以作为一个参数。将这个函数作为参数传到另一个函数里面,当那个函数执行完之后,再执行传进去的这个函数,这个过程就叫做回调。
回调函数写的位置:定时器结束的位置。
function animate(obj, target, callback) {
// console.log(callback); callback = function() {} 调用的时候 callback()
// 先清除以前的定时器,只保留当前的一个定时器执行
clearInterval(obj.timer);
obj.timer = setInterval(function() {
// 步长值写到定时器的里面
// 把我们步长值改为整数 不要出现小数的问题
// var step = Math.ceil((target - obj.offsetLeft) / 10);
var step = (target - obj.offsetLeft) / 10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);
if (obj.offsetLeft == target) {
// 停止动画 本质是停止定时器
clearInterval(obj.timer);
// 回调函数写到定时器结束里面
// if (callback) {
// // 调用函数
// callback();
// }
callback && callback();
}
// 把每次加1 这个步长值改为一个慢慢变小的值 步长公式:(目标值 - 现在的位置) / 10
obj.style.left = obj.offsetLeft + step + 'px';
}, 15);
}
<script>
// 1. 获取元素
var sliderbar = document.querySelector('.sliderbar');
var con = document.querySelector('.con');
// 当我们鼠标经过 sliderbar 就会让 con这个盒子滑动到左侧
// 当我们鼠标离开 sliderbar 就会让 con这个盒子滑动到右侧
sliderbar.addEventListener('mouseenter', function() {
// animate(obj, target, callback);
animate(con, -160, function() {
// 当我们动画执行完毕,就把 ← 改为 →
sliderbar.children[0].innerHTML = '→';
});
})
sliderbar.addEventListener('mouseleave', function() {
// animate(obj, target, callback);
animate(con, 0, function() {
sliderbar.children[0].innerHTML = '←';
});
})
</script>
轮播图四部分组成
轮播图也称为焦点图,是网页中比较常见的网页特效。
功能需求:
1.鼠标经过轮播图模块,左右按钮显示,离开隐藏左右按钮。
2.点击右侧按钮一次,图片往左播放一张,以此类推,左侧按钮同理。
3.图片播放的同时,下面小圆圈模块跟随一起变化。
4.点击小圆圈,可以播放相应图片。
5.鼠标不经过轮播图,轮播图也会自动播放图片。
6.鼠标经过,轮播图模块, 自动播放停止。
// 2. 鼠标经过focus 就显示隐藏左右按钮
focus.addEventListener('mouseenter', function() {
arrow_l.style.display = 'block';
arrow_r.style.display = 'block';
});
focus.addEventListener('mouseleave', function() {
arrow_l.style.display = 'none';
arrow_r.style.display = 'none';
});
案例分析
① 动态生成小圆圈
② 核心思路:小圆圈的个数要跟图片张数一致
③ 所以首先先得到ul里面图片的张数(图片放入li里面,所以就是li的个数)
④ 利用循环动态生成小圆圈(这个小圆圈要放入ol里面)
⑤ 创建节点 createElement(‘li’)
⑥ 插入节点 ol. appendChild(li)
⑦ 第一个小圆圈需要添加 current 类
① 小圆圈的排他思想
② 点击当前小圆圈,就添加current类
③ 其余的小圆圈就移除这个current类
④ 注意: 我们在刚才生成小圆圈的同时,就可以直接绑定这个点击事件了。
// 3. 动态生成小圆圈 有几张图片,我就生成几个小圆圈
var ul = focus.querySelector('ul');
var ol = focus.querySelector('.circle');
// console.log(ul.children.length);
for (var i = 0; i < ul.children.length; i++) {
// 创建一个小li
var li = document.createElement('li');
// 记录当前小圆圈的索引号 通过自定义属性来做
li.setAttribute('index', i);
// 把小li插入到ol 里面
ol.appendChild(li);
// 4. 小圆圈的排他思想 我们可以直接在生成小圆圈的同时直接绑定点击事件
li.addEventListener('click', function() {
// 干掉所有人 把所有的小li 清除 current 类名
for (var i = 0; i < ol.children.length; i++) {
ol.children[i].className = '';
}
// 留下我自己 当前的小li 设置current 类名
this.className = 'current';
})
}
案例分析
① 点击小圆圈滚动图片
② 此时用到animate动画函数,将js文件引入(注意,因为index.js 依赖 animate.js 所以,animate.js 要写到 index.js 上面)
③ 使用动画函数的前提,该元素必须有定位
④ 注意是ul 移动 而不是小li
⑤ 滚动图片的核心算法: 点击某个小圆圈 , 就让图片滚动 小圆圈的索引号乘以图片的宽度做为ul移动距离
⑥ 此时需要知道小圆圈的索引号, 我们可以在生成小圆圈的时候,给它设置一个自定义属性,点击的时候获取这个自定义属性即可。
// 5. 点击小圆圈,移动图片 当然移动的是 ul
// ul 的移动距离 小圆圈的索引号 乘以 图片的宽度 注意是负值
// 当我们点击了某个小li 就要把这个li 的索引号给 num
var index = this.getAttribute("index");
// 图片的宽度
var focusWidth = focus.offsetWidth;
animate(ul, -index * focusWidth);
案例分析
① 点击右侧按钮一次,就让图片滚动一张。
② 声明一个变量num, 点击一次,自增1, 让这个变量乘以图片宽度,就是 ul 的滚动距离。
③ 图片无缝滚动原理
④ 把ul 第一个li 复制一份,放到ul 的最后面
⑤ 当图片滚动到克隆的最后一张图片时, 让ul 快速的、不做动画的跳到最左侧: left 为0
⑥ 同时num 赋值为0,可以从新开始滚动图片了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gOMqwnAu-1646565991682)(https://gitee.com/shen-longfei001/image-bed/raw/master/ImageWebApls/202203061831607.gif)]
var num = 0;
arrow_r.addEventListener("click", function () {
if (num == ul.children.length-1) {
ul.style.left = 0;
num = 0;
}
num++;
animate(ul, -num * focusWidth);
① 克隆第一张图片
② 克隆ul 第一个li cloneNode() 加true 深克隆 复制里面的子节点 false 浅克隆
③ 添加到 ul 最后面 appendChild
// 6. 克隆第一张图片(li)放到ul 最后面
// 写在for 循环 生成li外 , 因为不能让ol循环到
var first = ul.children[0].cloneNode(true);
ul.appendChild(first);
案例分析
① 点击右侧按钮, 小圆圈跟随变化
② 最简单的做法是再声明一个变量circle,每次点击自增1,注意,左侧按钮也需要这个变量,因此要声明全局变量。
③ 但是图片有5张,我们小圆圈只有4个少一个,必须加一个判断条件
④ 如果circle == 4 就 从新复原为 0
// circle 控制小圆圈的播放
var circle = 0;
circle++;
// 等于 他的长度说明到了最后一张了 . 需要把索引改为 0
if (circle == ol.children.length) {
circle = 0;
}
for (var i = 0; i < ol.children.length; i++) { // 其他小圆圈 清除样式
ol.children[i].className = "";
} // 当前小圆圈添加 样式
ol.children[circle].className = "current";
// 当我们点击了某个小li 就要把这个li 的索引号给 num
num = index;
// 当我们点击了某个小li 就要把这个li 的索引号给 circle
circle = index;
// num = circle = index;
// 左侧按钮
arrow_l.addEventListener("click", function () {
if (num == 0) {
// 到了第一张 直接跳到最后一张
ul.style.left = -(ul.children.length - 1) * focusWidth + "px";
num = ul.children.length - 1;
}
num--;
animate(ul, -num * focusWidth);
// 点击左侧按钮,小圆圈跟随一起变化 可以再声明一个变量控制小圆圈的播放
circle--;
// 如果circle < 0 说明第一张图片,则小圆圈要改为第4个小圆圈(3)
if (circle < 0) {
circle = ol.children.length - 1;
}
// circle = circle < 0 ? ol.children.length - 1 : circle;
for (var i = 0; i < ol.children.length; i++) {
ol.children[i].className = "";
}
ol.children[circle].className = "current";
// 可优化 外面封装一个函数调用它
function circleChildren() {
for (var i = 0; i < ol.children.length; i++) {
ol.children[i].className = "";
}
ol.children[circle].className = "current";
}
});
案例分析
① 自动播放功能
② 添加一个定时器
③ 自动播放轮播图,实际就类似于点击了右侧按钮
④ 此时我们使用手动调用右侧按钮点击事件 arrow_r.click()
⑤ 鼠标经过focus 就停止定时器
⑥ 鼠标离开focus 就开启定时器
var timer = setInterval(function () {
arrow_r.click();
}, 2000);
防止轮播图按钮连续点击造成播放过快。
节流阀目的:当上一个函数动画内容执行完毕,再去执行下一个函数动画,让事件无法连续触发。
核心实现思路:利用回调函数,添加一个变量来控制,锁住函数和解锁函数。
开始设置一个变量 var flag = true;
If(flag) {flag = false; do something} 关闭水龙头
利用回调函数 动画执行完毕, flag = true 打开水龙头
// if (callback) {
// callback(); // 调用函数
// }
callback && callback();
利用短路运算 , 要求左边和右边都为 true 才去执行
如果 && 左边为 true (有参数传进来) , 就去看右边的 , (调用函数)
如果 && 左边为 false (没有参数传进来) , 就结束 (不去看右边的)
滚动窗口至文档中的特定位置。
window.scroll(x, y)
注意,里面的x和y 不跟单位,直接写数字
案例分析
① 带有动画的返回顶部
② 此时可以继续使用我们封装的动画函数
③ 只需要把所有的left 相关的值 改为 跟页面垂直滚动距离相关就可以了
④ 页面滚动了多少,可以通过 window.pageYOffset 得到
⑤ 最后是页面滚动,使用 window.scroll(x,y)
// 3. 当我们点击了返回顶部模块,就让窗口滚动的页面的最上方
goBack.addEventListener('click', function() {
// 里面的x和y 不跟单位的 直接写数字即可
// window.scroll(0, 0);
// 因为是窗口滚动 所以对象是window
animate(window, 0);
});
// 动画函数
function animate(obj, target, callback) {
// console.log(callback); callback = function() {} 调用的时候 callback()
// 先清除以前的定时器,只保留当前的一个定时器执行
clearInterval(obj.timer);
obj.timer = setInterval(function() {
// 步长值写到定时器的里面
// 把我们步长值改为整数 不要出现小数的问题
// var step = Math.ceil((target - obj.offsetLeft) / 10);
var step = (target - window.pageYOffset) / 10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);
if (window.pageYOffset == target) {
// 停止动画 本质是停止定时器
clearInterval(obj.timer);
// 回调函数写到定时器结束里面
// if (callback) {
// // 调用函数
// callback();
// }
callback && callback();
}
// 把每次加1 这个步长值改为一个慢慢变小的值 步长公式:(目标值 - 现在的位置) / 10
// obj.style.left = window.pageYOffset + step + 'px';
window.scroll(0, window.pageYOffset + step); // window 滚动 window.scroll(x, y) xy 轴
}, 15);
}
要求
鼠标经过某个小li, 筋斗云跟这到当前小li位置
鼠标离开这个小li, 筋斗云复原为原来的位置
鼠标点击了某个小li, 筋斗云就会留在点击这个小li 的位置
案例分析
① 利用动画函数做动画效果
② 原先筋斗云的起始位置是0
③ 鼠标经过某个小li, 把当前小li 的 offsetLeft 位置 做为目标值即可
④ 鼠标离开某个小li, 就把目标值设为 0
⑤ 如果点击了某个小li, 就把li当前的位置存储起来,做为筋斗云的起始位置 current = this.offsetLeft;
<script>
window.addEventListener('load', function() {
// 1. 获取元素
var cloud = document.querySelector('.cloud');
var c_nav = document.querySelector('.c-nav');
var lis = c_nav.querySelectorAll('li');
// 2. 给所有的小li绑定事件
// 这个current 做为筋斗云的起始位置
var current = 0;
for (var i = 0; i < lis.length; i++) {
// (1) 鼠标经过把当前小li 的位置做为目标值
lis[i].addEventListener('mouseenter', function() {
animate(cloud, this.offsetLeft);
});
// (2) 鼠标离开就回到起始的位置
lis[i].addEventListener('mouseleave', function() {
animate(cloud, current);
});
// (3) 当我们鼠标点击,就把当前位置做为目标值
lis[i].addEventListener('click', function() {
current = this.offsetLeft;
});
}
})
</script>
移动端浏览器兼容性较好,我们不需要考虑以前 JS 的兼容性问题,可以放心的使用原生 JS 书写效果,但是移动
端也有自己独特的地方。比如触屏事件 touch(也称触摸事件),Android 和 IOS 都有。
触屏 touch 事件 | 说明 |
---|---|
touchstart | 手指触摸到一个 DOM 元素时触发 |
touchmove | 手指在一个 DOM 元素上滑动时触发 |
touchend | 手指从一个 DOM 元素上移开时触发 |
<script>
// 1. 获取元素
// 2. 手指触摸DOM元素事件
var div = document.querySelector("div");
div.addEventListener("touchstart", function () {
console.log("我模了你");
});
// 3. 手指在DOM元素上移动事件
div.addEventListener("touchmove", function () {
console.log("我继续模你");
});
// 3. 手指离开DOM元素事件
div.addEventListener("touchend", function () {
console.log("我走了");
});
</script>
触摸列表 | 说明 |
---|---|
touches | 正在触摸屏幕的所有列表 |
targetTouches | 正在触摸当前 DOM 元素上的手指的一个列表 |
changedTouches | 手指状态发生了改变的列表 , 从无到有 从有到无的变化 |
事件小结
因为平时我们都是给元素注册触摸事件,所以重点记住 targetTocuhes
p.addEventListener("touchstart", function (e) {
// e.targetTouches[0] 就可以得到正在触摸 dom 元素的第一个手指的相关信息 如手指坐标 哪个元素等等
console.log(e.targetTouches[0]);
});
div.addEventListener("touchend", function (e) {
// 当我们手指离开屏幕的时候 , 就没有touches 和 targetTouches 列表
// 但是会有 changedTouches
console.log(e);
});
拖动元素三步曲:
盒子需要加定位
(1) 触摸元素 touchstart: 获取手指初始坐标,同时获得盒子原来的位置
(2) 移动手指 touchmove: 计算手指的滑动距离,并且移动盒子
(3) 离开手指 touchend:
注意: 手指移动也会触发滚动屏幕所以这里要阻止默认的屏幕滚动 e.preventDefault();
<script>
// (1) 触摸元素 touchstart: 获取手指初始坐标,同时获得盒子原来的位置
// (2) 移动手指 touchmove: 计算手指的滑动距离,并且移动盒子
// (3) 离开手指 touchend:
var div = document.querySelector('div');
var startX = 0; //获取手指初始坐标
var startY = 0;
var x = 0; //获得盒子原来的位置
var y = 0;
div.addEventListener('touchstart', function(e) {
// 获取手指初始坐标
startX = e.targetTouches[0].pageX;
startY = e.targetTouches[0].pageY;
x = this.offsetLeft;
y = this.offsetTop;
});
div.addEventListener('touchmove', function(e) {
// 计算手指的移动距离: 手指移动之后的坐标减去手指初始的坐标
var moveX = e.targetTouches[0].pageX - startX;
var moveY = e.targetTouches[0].pageY - startY;
// 移动我们的盒子 盒子原来的位置 + 手指移动的距离
this.style.left = x + moveX + 'px';
this.style.top = y + moveY + 'px';
e.preventDefault(); // 阻止屏幕滚动的默认行为
});
</script>
移动端轮播图功能和基本PC端一致。
案例分析
// 1. 获取元素
var focus = document.querySelector('.focus');
var ul = focus.children[0];
// 获得focus 的宽度
var w = focus.offsetWidth;
var ol = focus.children[1];
// 2. 利用定时器自动轮播图图片
var index = 0;
var timer = setInterval(function() {
index++;
var translatex = -index * w;
ul.style.transition = 'all .3s';
ul.style.transform = 'translateX(' + translatex + 'px)';
// ul.style.transform = `translateX(${translatex}px)`;
}, 2000);
注意,我们判断条件是要等到图片滚动完毕再去判断,就是过渡完成后判断
此时需要添加检测过渡完成事件 transitionend
判断条件:如果索引号等于 3 说明走到最后一张图片,此时 索引号要复原为 0
此时图片,去掉过渡效果,然后移动
如果索引号小于0, 说明是倒着走, 索引号等于2
此时图片,去掉过渡效果,然后移动
// 等着我们过渡完成之后,再去判断 监听过渡完成的事件 transitionend
ul.addEventListener('transitionend', function() {
// 无缝滚动
if (index >= 3) {
index = 0;
// console.log(index);
// 去掉过渡效果 这样让我们的ul 快速的跳到目标位置
ul.style.transition = 'none';
// 利用最新的索引号乘以宽度 去滚动图片
var translatex = -index * w;
ul.style.transform = 'translateX(' + translatex + 'px)';
} else if (index < 0) {
index = 2;
ul.style.transition = 'none';
// 利用最新的索引号乘以宽度 去滚动图片
var translatex = -index * w;
ul.style.transform = 'translateX(' + translatex + 'px)';
}
案例分析
// 3. 小圆点跟随变化
// 把ol里面li带有current类名的选出来去掉类名 remove
ol.querySelector('.current').classList.remove('current');
// 让当前索引号 的小li 加上 current add
ol.children[index].classList.add('current');
案例分析
// 4. 手指滑动轮播图
// 触摸元素 touchstart: 获取手指初始坐标
var startX = 0;
var moveX = 0; // 后面我们会使用这个移动距离所以要定义一个全局变量
var flag = false;
ul.addEventListener("touchstart", function (e) {
startX = e.targetTouches[0].pageX;
// 手指触摸的时候就停止定时器
clearInterval(timer);
});
// 移动手指 touchmove: 计算手指的滑动距离, 并且移动盒子
ul.addEventListener("touchmove", function (e) {
// 计算移动距离
moveX = e.targetTouches[0].pageX - startX;
// 移动盒子: 盒子原来的位置 + 手指移动的距离
var translateX = -index * w + moveX;
// 手指拖动的时候,不需要动画效果所以要取消过渡效果
ul.style.transition = "none";
ul.style.transform = "translateX(" + translateX + "px)";
});
flag = true; // 如果用户手指移动过我们再去判断否则不做判断效果
e.preventDefault(); // 阻止滚动屏幕的行为
// 手指离开 根据移动距离去判断是回弹还是播放上一张下一张
ul.addEventListener("touchend", function (e) {
if (flag) {
// (1) 如果移动距离大于50像素我们就播放上一张或者下一张
if (Math.abs(moveX) > 50) {
// 如果是右滑就是 播放上一张 moveX 是正值
if (moveX > 0) {
index--;
} else {
// 如果是左滑就是 播放下一张 moveX 是负值
index++;
}
var translatex = -index * w;
ul.style.transition = "all .3s";
ul.style.transform = "translateX(" + translatex + "px)";
} else {
// (2) 如果移动距离小于50像素我们就回弹
var translatex = -index * w;
ul.style.transition = "all .1s";
ul.style.transform = "translateX(" + translatex + "px)";
}
}
// 手指离开的时候就重新开启定时器
clearInterval(timer);
timer = setInterval(function () {
index++;
var translatex = -index * w;
ul.style.transition = "all .3s";
ul.style.transform = "translateX(" + translatex + "px)";
}, 2000);
});
案例分析
// 返回顶部模块制作
var goBack = document.querySelector(".goBack");
var nav = document.querySelector("nav");
window.addEventListener("scroll", function () {
if (window.pageYOffset >= nav.offsetTop) {
goBack.style.display = "block";
} else {
goBack.style.display = "none";
}
});
goBack.addEventListener("click", function () {
window.scroll(0, 0);
});
window.addEventListener('load', function() {
// alert(1);
// 1. 获取元素
var focus = document.querySelector(".focus");
var ul = focus.children[0];
// 获得focus 的宽度
var w = focus.offsetWidth;
var ol = focus.children[1];
// 2. 利用定时器自动轮播图图片
var index = 0;
var timer = setInterval(function () {
index++;
var translatex = -index * w;
ul.style.transition = "all .3s";
ul.style.transform = "translateX(" + translatex + "px)";
}, 2000);
// 等着我们过渡完成之后,再去判断 监听过渡完成的事件 transitionend
ul.addEventListener("transitionend", function () {
// 无缝滚动
if (index >= 3) {
index = 0;
// console.log(index);
// 去掉过渡效果 这样让我们的ul 快速的跳到目标位置
ul.style.transition = "none";
// 利用最新的索引号乘以宽度 去滚动图片
var translatex = -index * w;
ul.style.transform = "translateX(" + translatex + "px)";
} else if (index < 0) {
index = 2;
ul.style.transition = "none";
// 利用最新的索引号乘以宽度 去滚动图片
var translatex = -index * w;
ul.style.transform = "translateX(" + translatex + "px)";
}
// 3. 小圆点跟随变化
// 把ol里面li带有current类名的选出来去掉类名 remove
ol.querySelector(".current").classList.remove("current");
// 让当前索引号 的小li 加上 current add
ol.children[index].classList.add("current");
});
// 4. 手指滑动轮播图
// 触摸元素 touchstart: 获取手指初始坐标
var startX = 0;
var moveX = 0; // 后面我们会使用这个移动距离所以要定义一个全局变量
var flag = false;
ul.addEventListener("touchstart", function (e) {
startX = e.targetTouches[0].pageX;
// 手指触摸的时候就停止定时器
clearInterval(timer);
});
// 移动手指 touchmove: 计算手指的滑动距离, 并且移动盒子
ul.addEventListener("touchmove", function (e) {
// 计算移动距离
moveX = e.targetTouches[0].pageX - startX;
// 移动盒子: 盒子原来的位置 + 手指移动的距离
var translatex = -index * w + moveX;
// 手指拖动的时候,不需要动画效果所以要取消过渡效果
ul.style.transition = "none";
ul.style.transform = "translateX(" + translatex + "px)";
flag = true; // 如果用户手指移动过我们再去判断否则不做判断效果
e.preventDefault(); // 阻止滚动屏幕的行为
});
// 手指离开 根据移动距离去判断是回弹还是播放上一张下一张
ul.addEventListener("touchend", function (e) {
if (flag) {
// (1) 如果移动距离大于50像素我们就播放上一张或者下一张
if (Math.abs(moveX) > 50) {
// 如果是右滑就是 播放上一张 moveX 是正值
if (moveX > 0) {
index--;
} else {
// 如果是左滑就是 播放下一张 moveX 是负值
index++;
}
var translatex = -index * w;
ul.style.transition = "all .3s";
ul.style.transform = "translateX(" + translatex + "px)";
} else {
// (2) 如果移动距离小于50像素我们就回弹
var translatex = -index * w;
ul.style.transition = "all .1s";
ul.style.transform = "translateX(" + translatex + "px)";
}
}
// 手指离开的时候就重新开启定时器
clearInterval(timer);
timer = setInterval(function () {
index++;
var translatex = -index * w;
ul.style.transition = "all .3s";
ul.style.transform = "translateX(" + translatex + "px)";
}, 2000);
});
// 返回顶部模块制作
var goBack = document.querySelector(".goBack");
var nav = document.querySelector("nav");
window.addEventListener("scroll", function () {
if (window.pageYOffset >= nav.offsetTop) {
goBack.style.display = "block";
} else {
goBack.style.display = "none";
}
});
goBack.addEventListener("click", function () {
window.scroll(0, 0);
});
})
classList属性是HTML5新增的一个属性,返回元素的类名。但是ie10以上版本支持。
该属性用于在元素中添加,移除及切换 CSS 类。有以下方法
添加类:
element.classList.add(’类名’); 是在后面追加类名不会覆盖以前的类名 注意前面不需要加.
focus.classList.add('current');
移除类:
element.classList.remove(’类名’);
focus.classList.remove('current');
切换类:
element.classList.toggle(’类名’);
focus.classList.toggle('current');
注意:以上方法里面,所有类名都不带点
// classList 返回元素的类名
var div = document.querySelector('div');
// console.log(div.classList[1]);
// 1. 添加类名 是在后面追加类名不会覆盖以前的类名 注意前面不需要加.
div.classList.add('three');
// 2. 删除类名
div.classList.remove('one');
// 3. 切换类
var btn = document.querySelector('button');
btn.addEventListener('click', function() {
document.body.classList.toggle('bg');
})
移动端 click 事件会有 300ms 的延时,原因是移动端屏幕双击会缩放(double tap to zoom) 页面。
解决方案:
1. 禁用缩放。 浏览器禁用默认的双击缩放行为并且去掉300ms 的点击延迟。
<meta name="viewport" content="user-scalable=no">
2.利用touch事件自己封装这个事件解决300ms 延迟。
原理就是:
代码如下:
//封装tap,解决click 300ms 延时
function tap (obj, callback) {
var isMove = false;
var startTime = 0; // 记录触摸时候的时间变量
obj.addEventListener('touchstart', function (e) {
startTime = Date.now(); // 记录触摸时间
});
obj.addEventListener('touchmove', function (e) {
isMove = true; // 看看是否有滑动,有滑动算拖拽,不算点击
});
obj.addEventListener('touchend', function (e) {
if (!isMove && (Date.now() - startTime) < 150) { // 如果手指触摸和离开时间小于150ms 算点击
callback && callback(); // 执行回调函数
}
isMove = false; // 取反 重置
startTime = 0;
});
}
//调用
tap(div, function(){ // 执行代码 });
移动端要求的是快速开发,所以我们经常会借助于一些插件来帮我完成操作,那么什么是插件呢?
JS 插件是 js 文件,它遵循一定规范编写,方便程序展示效果,拥有特定功能且方便调用。如轮播图和瀑布流插件。
特点:它一般是为了解决某个问题而专门存在,其功能单一,并且比较小。
我们以前写的animate.js 也算一个最简单的插件
fastclick 插件解决 300ms 延迟。 使用延时
GitHub官网地址: https://github.com/ftlabs/fastclick
if ('addEventListener' in document) {
document.addEventListener('DOMContentLoaded', function() {
FastClick.attach(document.body);
}, false);
}
// 使用插件
var div = document.querySelector("div");
div.addEventListener("click", function () {
alert("1");
});
中文官网地址: https://www.swiper.com.cn/
<link rel="stylesheet" href="./css/swiper.min.css">
<script src="./js/swiper.min.js">script>
<script src="js/index.js">script>
<div class="swiper-container">
<div class="swiper-wrapper">
<div class="swiper-slide">Slide 1div>
<div class="swiper-slide">Slide 2div>
<div class="swiper-slide">Slide 3div>
<div class="swiper-slide">Slide 4div>
<div class="swiper-slide">Slide 5div>
<div class="swiper-slide">Slide 6div>
<div class="swiper-slide">Slide 7div>
<div class="swiper-slide">Slide 8div>
<div class="swiper-slide">Slide 9div>
<div class="swiper-slide">Slide 10div>
div>
<div class="swiper-pagination">div>
div>
/* 复制swiper css样式到index.css */
.swiper-container {
width: 100%;
height: 100%;
}
.swiper-slide {
text-align: center;
font-size: 18px;
background: #fff;
/* Center slide text vertically */
display: -webkit-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
-webkit-justify-content: center;
justify-content: center;
-webkit-box-align: center;
-ms-flex-align: center;
-webkit-align-items: center;
align-items: center;
}
.swiper-pagination-bullet {
width: 20px;
height: 20px;
text-align: center;
line-height: 20px;
font-size: 12px;
color:#000;
opacity: 1;
background: rgba(0,0,0,0.2);
}
.swiper-pagination-bullet-active {
color:#fff;
background: #007aff;
}
// 引入 index.js
window.addEventListener("load", function () {
var swiper = new Swiper(".swiper-container", {
pagination: {
el: ".swiper-pagination", // 调用分页器
clickable: true, // 是否可以点击
renderBullet: function (index, className) { // 显示分页序号
return '+ className + '">' + (index + 1) + "";
},
},
});
});
lsuperslide: http://www.superslide2.com/
l iscroll: https://github.com/cubiq/iscroll
1.确认插件实现的功能 ( 轮播图 , Tab 栏切换 )
2.去官网查看使用说明
3.下载插件
4.打开demo实例文件,查看需要引入的相关文件,并且引入
5.复制demo实例文件中的结构html,样式css以及js代码
H5 给我们提供了 video 标签,但是浏览器的支持情况不同。
不同的视频格式文件,我们可以通过source解决。
但是外观样式,还有暂停,播放,全屏等功能我们只能自己写代码解决。
这个时候我们可以使用插件方式来制作。
我们可以通过 JS 修改元素的大小、颜色、位置等样式。
x11.复制 html 结构 ,复制 css 样式22.引入 css 样式33.引入 js44.调用 js
框架,顾名思义就是一套架构,它会基于自身的特点向用户提供一套较为完整的解决方案。框架的控制权在框架本身,使用者要按照框架所规定的某种规范进行开发。
插件一般是为了解决某个问题而专门存在,其功能单一,并且比较小。
前端常用的框架有 Bootstrap、Vue、Angular、React 等。既能开发PC端,也能开发移动端
前端常用的移动端插件有 swiper、superslide、iscroll等。
框架: 大而全,一整套解决方案
插件: 小而专一,某个功能的解决方案
Bootstrap 官网
Bootstrap 是一个简洁、直观、强悍的前端开发框架,它让 web 开发更迅速、简单。
它能开发PC端,也能开发移动端
Bootstrap JS插件使用步骤:
<link rel="stylesheet" href="./bootstrap/css/bootstrap.min.css" />
<script src="./bootstrap/js/jquery.min.js">script>
<script src="./bootstrap/js/bootstrap.min.js">script>
1.引入相关js 文件
2.复制HTML 结构
3.修改对应样式
.carousel,
.carousel img {
height: 300px !important;
width: 100%;
}
4.修改相应JS 参数
<script>
$(".carousel").carousel({
interval: 1000,
});
</script>
1、数据存储在用户浏览器中
2、设置、读取方便、甚至页面刷新不丢失数据
3、容量较大,sessionStorage约5M、localStorage约20M
4、只能存储字符串,可以将对象JSON.stringify() 编码后存储
sessionStorage 存储的值 , 只能是字符串 , 如果传入的不是字符串 将会自动转为字符串
sessionStorage 会话存储
存储数据:
sessionStorage.setItem(key, value)
获取数据:
sessionStorage.getItem(key)
删除数据:
sessionStorage.removeItem(key)
清空数据:(所有都清除掉)
sessionStorage.clea)
<script>
// console.log(localStorage.getItem('username'));
var ipt = document.querySelector('input');
var set = document.querySelector('.set');
var get = document.querySelector('.get');
var remove = document.querySelector('.remove');
var del = document.querySelector('.del');
set.addEventListener('click', function() {
// 当我们点击了之后,就可以把表单里面的值存储起来
var val = ipt.value;
sessionStorage.setItem('uname', val);
sessionStorage.setItem('pwd', val);
});
get.addEventListener('click', function() {
// 当我们点击了之后,就可以把表单里面的值获取过来
console.log(sessionStorage.getItem('uname'));
});
remove.addEventListener('click', function() {
//
sessionStorage.removeItem('uname');
});
del.addEventListener('click', function() {
// 当我们点击了之后,清除所有的
sessionStorage.clear();
});
</script>
存储数据:
localStorage.setItem(key, value)
获取数据:
localStorage.getItem(key)
删除数据:
localStorage.removeItem(key)
清空数据:(所有都清除掉)
localStorage.clear()
相同点 : 使用方法是一样的 ; 都就可以把数据存储到浏览器中
不同点 :
如果勾选记住用户名, 下次用户打开浏览器,就在文本框里面自动显示上次登录的用户名
案例分析
<input type="text" id="username"> <input type="checkbox" name="" id="remember"> 记住用户名
<script>
var username = document.querySelector('#username');
var remember = document.querySelector('#remember');
if (localStorage.getItem('username')) {
username.value = localStorage.getItem('username');
remember.checked = true;
}
remember.addEventListener('change', function() {
if (this.checked) {
localStorage.setItem('username', username.value)
} else {
localStorage.removeItem('username');
}
})
</script>
<body>
账号:<input type="text" placeholder="请输入账号" />
<br />
<br />
密码:<input type="password" placeholder="请输入密码" />
<hr />
<input type="checkbox" />记住账号密码
<script>
// 给记住我 绑定单击事件,单机的时候获取表单数据,将表单数据存到localstorage中
document.querySelector("input[type='checkbox']").addEventListener("click", function () {
// 2.判断单前复选框是否选中,通过checked,如果该值是true,表示选中 false 表示未选中
if (this.checked) {
// 2.1 如果选中,就获取表单数据,将表单数据存入localstorage中
var username = document.querySelectorAll("input")[0].value;
var password = document.querySelectorAll("input")[1].value;
localStorage.setItem("username", username);
localStorage.setItem("password", password);
} else {
// 2.2 如果没有选中就删除localstorage中的数据
localStorage.removeItem("username");
localStorage.removeItem("password");
}
});
// 页面加载完毕后将账户密码回显到输入框
var username = localStorage.getItem("username");
var password = localStorage.getItem("password");
//如果本地有数据
if (username) {
document.querySelectorAll("input")[0].value = username;
document.querySelectorAll("input")[1].value = password;
document.querySelectorAll("input")[2].checked = true;
}
</script>
</body>
案例
<input type="text" id="username" />
<input type="checkbox" id="remember" />是否记住用户名
<br />
<button>登录</button>
var username = document.querySelector("#username"); // 输入框
var remember = document.querySelector("#remember"); // 复选框
var btn = document.querySelector("button"); // 登录
// 加载页面时 判断 localstorage 中是否有数据 , 有数据就把值赋值给输入框 , 并勾选复选框
if (localStorage.getItem("username")) {
username.value = localStorage.getItem("username");
remember.checked = true;
}
// 点击按钮判断 复选框有没被勾选,勾选上把输入框的值添加到 localstorage中 ,否知清除localstorage数据
btn.onclick = function () {
if (remember.checked) {
localStorage.setItem("username", username.value);
} else {
localStorage.removeItem("username");
}
};
JSON : 对象或数组的字符串展示形式
JSON : 存储数据 , 数据交换
前后端进行数据交互的时候 ,采用 JSON 格式的数据进行交换
var obj = {
name: "lll",
age: 19,
sex: "男",
};
// JSON.stringify() 将对象转为字符串
localStorage.setItem("xx", obj);
console.log(localStorage.getItem("xx"));
console.log(localStorage.getItem("obj"));
// JSON.parse() 将json 字符串转为对象
console.log(JSON.parse(localStorage.getItem("obj")));
console.log(obj.name); // 111
console.log(obj.age); // age
<select name="" id="">
<option value="1">山西</option>
<option value="2">山东</option>
<option value="3">陕西</option>
<option value="4">上海</option>
</select>
<script>
var select = document.querySelector("select");
// 获取下拉单的值 通过change事件
// selsect 的值 value 是option里面的 value 属性的值
select.addEventListener("change", function () {
console.log(this.value); // 1-2-3-4
});
</script>
$("#myForm").reset() 方法可把表单中的元素重置为它们的默认值