JavaScript 事件
JavaScript 事件
一、事件基础概念
- 事件触发机制:特效通常由事件触发
- 事件源:任何 HTML 元素(节点)都可作为事件源,一个事件源可添加多个事件
- 事件分类:
- 键盘事件:keypress(按键)、keyup(抬起)、keydown(按下)
- 鼠标事件:click(单击)、dblclick(双击)、mouseover(悬浮)、mouseout(离开)等
- 文档事件:load(加载)
- 表单事件:focus(获得焦点)、blur(失去焦点)、submit(提交)、change(改变)
- 其他事件:scroll(滚动)、select(选择)
二、事件触发方式
方法一:元素。事件 = 匿名函数
直接获取元素后,将事件与匿名函数绑定,适合简单的事件处理。
html
预览
<div class="box1" style="width:100px;height:100px;border:1px solid #000;"></div>
<script>
// 获取元素
var oBox = document.querySelector('.box1');
// 绑定点击事件
oBox.onclick = function() {
// 事件处理逻辑
console.log('匿名函数方式:盒子被点击了');
this.style.backgroundColor = 'lightblue';
};
</script>
方法二:元素。事件 = 函数名称
将事件绑定到命名函数,适合代码量较大或需要复用的事件处理逻辑。
html
预览
<div class="box2" style="width:100px;height:100px;border:1px solid #000;margin-top:10px;"></div>
<script>
// 获取元素
var oBox = document.querySelector('.box2');
// 绑定事件到命名函数
oBox.onclick = changeColor;
// 定义事件处理函数
function changeColor() {
console.log('命名函数方式:盒子被点击了');
this.style.backgroundColor = 'lightgreen';
}
</script>
方法三:HTML 属性直接绑定
在 HTML 标签中直接通过事件属性绑定,可通过 this 传递当前元素。
html
预览
<div class="box3" style="width:100px;height:100px;border:1px solid #000;margin-top:10px;"
onclick="changeBox(this, 'lightcoral')"></div>
<script>
// 定义事件处理函数
function changeBox(element, color) {
console.log('HTML属性方式:盒子被点击了');
console.log('当前元素:', element);
element.style.backgroundColor = color;
}
</script>
- 可通过
this传递当前标签作为参数
三、常用事件详解
1. 鼠标事件
-
常用鼠标事件:
- onclick:单击事件
- ondblclick:双击事件
- onmouseover:鼠标悬浮(支持冒泡)
- onmouseout:鼠标离开(支持冒泡)
- onmouseenter:鼠标进入(不支持冒泡)
- onmouseleave:鼠标离开(不支持冒泡)
- onmousemove:鼠标移动
-
onmouseover 与 onmouseenter 的区别:
- onmouseover/onmouseout:鼠标经过自身或子元素时都触发(支持冒泡)
- onmouseenter/onmouseleave:仅鼠标经过自身时触发,经过子元素不触发(不支持冒泡)
2. 键盘事件
-
常用键盘事件:
- onkeydown:按键按下(持续触发)
- onkeyup:按键抬起(仅一次触发)
-
键盘按键识别:
- 通过
event.keyCode获取按键编码值来判断按下的是哪个键 - 应用场景:表单提交、游戏控制等
- 通过
-
案例:键盘控制元素移动
javascript
运行
<div class="box" style=" margin-left: 0px;margin-top: 0px;"></div>
<script>
var oBox = document.querySelector('.box');
document.onkeyup = function(){
if(event.keyCode==39){
oBox.style.marginLeft = parseFloat(oBox.style.marginLeft)+5 + 'px';
console.log(parseFloat(oBox.style.marginLeft)+5);
}else if(event.keyCode==37){
oBox.style.marginLeft = parseFloat(oBox.style.marginLeft)-5 + 'px';
}else if(event.keyCode==83){
oBox.style.marginTop = parseFloat(oBox.style.marginTop)+5 + 'px';
}
}
</script>
3. 光标事件
-
常用光标事件:
- onfocus:元素获得焦点
- onblur:元素失去焦点
-
应用场景:
- 输入框提示文字显示 / 隐藏
- 搜索框下拉菜单显示 / 隐藏
javascript
运行
// 获取焦点,让value消失;失去焦点,显示提示信息
<input type="radio" name="" id="inp" value="请输入用户名">
<script>
var oInp = document.getElementById('inp');
oInp.onfocus = function(){
oInp.value = '';
}
oInp.onblur = function(){
oInp.value = '请输入用户名';
}
</script>
4. 表单事件
-
常用表单事件:
- onsubmit:表单提交
- onreset:表单重置
- onselect:文本被选中
- onchange:内容改变(失去焦点时触发)
- oninput:内容改变(立即触发)
-
onchange 与 oninput 区别:
- oninput:值改变时立即触发
- onchange:值改变且失去焦点时触发,适用于
<select>等元素
5. 单选框与复选框事件
- 通过
checked属性判断是否选中(true 为选中,false 为未选中) - 应用场景:购物车商品选择与价格计算
javascript
运行
<table border='1' width="500">
<tr>
<td>选择</td>
<td>商品名称</td>
<td>商品价格</td>
</tr>
<tr>
<td>
<input type="checkbox" class="sel"/>
</td>
<td>上衣</td>
<td class="price">100</td>
</tr>
<tr>
<td><input type="checkbox" class="sel"/></td>
<td>裤子</td>
<td class="price">200</td>
</tr>
<tr>
<td><input type="checkbox" class="sel"/></td>
<td>包包</td>
<td class="price">300</td>
</tr>
<tr>
<td>全选:<input type="checkbox"/></td>
<td colspan="2" id='totlePrice'>总价:0</td>
</tr>
</table>
<script>
// 1. 单选 - 给复选框添加一个事件
// 2. 判断复选框的状态
// 3. checked=true计算 / 否则不用计算
// 4. 0-checked-price 1-checked-price 2-checked-price
var oSel = document.querySelectorAll('.sel');
var oPrice = document.querySelectorAll('.price');
var totlePrice = document.querySelector('#totlePrice');
var sum = 0;
for(var i=0;i<oSel.length;i++){
oSel[i].index = i;
oSel[i].onclick = function(){
if(this.checked){
// 计算总价
sum += parseFloat(oPrice[this.index].innerHTML);
}else{
sum -= parseFloat(oPrice[this.index].innerHTML);
}
totlePrice.innerHTML = "总价:"+sum;
}
}
</script>
6. 加载事件
加载事件主要用于控制页面资源加载完成后的代码执行时机,解决 JS 代码因 DOM 未加载完成而无法获取元素的问题。
核心概念
- window.onload:页面中所有资源(HTML 结构、图片、样式表等)完全加载完成后触发,确保 DOM 元素已存在,避免获取元素时返回
null。 - window.onbeforeunload:页面刷新或关闭前触发,可用于执行清理操作(如重置滚动条位置)。
基础案例:解决 DOM 未加载问题
当 JS 代码写在<head>标签中或 DOM 结构之前时,直接获取元素会报错,使用window.onload可避免此问题。
html
预览
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>加载事件基础案例</title>
<script>
// 若不包裹在onload中,此时div#box尚未加载,会报错“无法读取null的innerHTML属性”
window.onload = function() {
// 页面加载完成后再获取元素
var oBox = document.getElementById('box');
console.log('盒子内容:', oBox.innerHTML); // 正常输出:hello world
}
</script>
</head>
<body>
<div id="box">hello world</div>
</body>
</html>
扩展案例:页面刷新时重置滚动条
页面刷新后默认保留上次滚动位置,使用onbeforeunload可在刷新前将滚动条重置到顶部。
html
预览
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>重置滚动条案例</title>
<style>
body {
height: 2000px; /* 制造滚动空间 */
}
</style>
<script>
// 页面刷新/关闭前触发
window.onbeforeunload = function() {
// 滚动到页面左上角(x=0,y=0)
window.scrollTo(0, 0);
}
</script>
</head>
<body>
<h1>向下滚动后刷新页面,滚动条会重置到顶部</h1>
<div style="margin-top: 1000px;">滚动到这里后刷新页面</div>
</body>
</html>
7. 滚动事件
滚动事件(onscroll)响应页面或元素的滚动行为,常见于 “返回顶部”“楼层导航”“滚动显示元素” 等交互场景。
核心概念与方法
- window.onscroll:页面滚动时持续触发(滚动过程中多次执行)。
- 滚动控制方法:
window.scrollTo(x, y):直接滚动到指定坐标(x:水平坐标,y:垂直坐标)。window.scrollTo(options):支持平滑滚动,参数为对象(top:垂直目标位置,left:水平目标位置,behavior:滚动方式,smooth为平滑,instant为瞬间)。
- 滚动距离获取:
- 标准浏览器:
document.documentElement.scrollTop(垂直滚动距离)、document.documentElement.scrollLeft(水平滚动距离)。 - 兼容 IE:
document.body.scrollTop/document.body.scrollLeft。 - 通用写法:
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop。
- 标准浏览器:
基础案例 1:监听页面滚动
页面滚动时触发事件,记录滚动次数。
html
预览
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>监听滚动案例</title>
<style>
body {
height: 10000px; /* 足够长的页面,确保可滚动 */
}
.scroll-count {
position: fixed;
top: 20px;
right: 20px;
background: #fff;
padding: 10px;
border: 1px solid #ccc;
}
</style>
</head>
<body>
<div class="scroll-count">滚动次数:<span id="count">0</span></div>
<script>
var count = 0;
var countEl = document.getElementById('count');
// 页面滚动时触发
window.onscroll = function() {
count++;
countEl.textContent = count;
}
</script>
</body>
</html>
案例 2:楼层导航(平滑切换)
点击导航栏,平滑滚动到对应 “楼层”,并高亮当前导航项(类似电商商品详情页的楼层切换)。
html
预览
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>楼层导航案例</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
ul, li {
list-style: none;
}
/* 导航栏样式 */
.header {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 40px;
background-color: #ffe169;
line-height: 40px;
z-index: 100;
}
.header ul {
display: flex;
justify-content: space-around;
}
.header ul li {
cursor: pointer;
transition: color 0.3s;
}
.header ul li.active {
color: #ff0000;
font-weight: bold;
}
/* 占位元素(避免内容被导航栏遮挡) */
.empty {
height: 40px;
}
/* 楼层样式 */
.floor {
height: 500px;
display: flex;
align-items: center;
justify-content: center;
font-size: 30px;
}
.product {
background-color: #b2ccee;
}
.ratings {
background-color: #b2eebb;
}
.detail {
background-color: #e7eebb;
}
.recommend {
background-color: #f1cadc;
}
</style>
</head>
<body>
<!-- 导航栏 -->
<div class="header">
<ul>
<li class="active">商品</li>
<li>评价</li>
<li>详情</li>
<li>推荐</li>
</ul>
</div>
<!-- 占位 -->
<div class="empty"></div>
<!-- 楼层 -->
<div class="product floor">商品楼层</div>
<div class="ratings floor">评价楼层</div>
<div class="detail floor">详情楼层</div>
<div class="recommend floor">推荐楼层</div>
<script>
// 获取导航项和楼层元素
var navList = document.querySelector('.header ul').querySelectorAll('li');
var floorList = document.querySelectorAll('.floor');
// 为每个导航项绑定点击事件
navList.forEach(function(nav, index) {
nav.onclick = function() {
// 1. 导航项高亮(排他思想)
navList.forEach(function(item) {
item.classList.remove('active');
});
this.classList.add('active');
// 2. 滚动到对应楼层(减去导航栏高度40px,避免被遮挡)
window.scrollTo({
top: floorList[index].offsetTop - 40,
behavior: 'smooth' // 平滑滚动
});
}
});
</script>
</body>
</html>
案例 3:返回顶部按钮(滚动显示 / 隐藏)
模仿淘宝首页 “返回顶部” 效果:页面滚动超过 200px 时显示按钮,点击后平滑回到顶部。
html
预览
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>返回顶部案例</title>
<style>
body {
height: 3000px; /* 制造滚动空间 */
}
.back-top {
position: fixed;
right: 20px;
bottom: 20px;
padding: 15px 10px;
background-color: #ffc0cb;
color: #333;
cursor: pointer;
border-radius: 4px;
display: none; /* 默认隐藏 */
transition: display 0.3s;
}
.back-top:hover {
background-color: #ff9aa2;
}
</style>
</head>
<body>
<div class="back-top" id="backTop">返回顶部</div>
<script>
var backTopBtn = document.getElementById('backTop');
var scrollTimer = null; // 定时器,用于平滑滚动
// 1. 监听页面滚动,控制按钮显示/隐藏
window.onscroll = function() {
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
// 滚动超过200px显示按钮,否则隐藏
if (scrollTop >= 200) {
backTopBtn.style.display = 'block';
} else {
backTopBtn.style.display = 'none';
}
}
// 2. 点击按钮返回顶部(平滑滚动)
backTopBtn.onclick = function() {
// 清除之前的定时器,避免多次点击导致速度异常
clearInterval(scrollTimer);
scrollTimer = setInterval(function() {
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
if (scrollTop <= 0) {
clearInterval(scrollTimer); // 滚动到顶部,停止定时器
return;
}
// 每次向上滚动50px(数值越大,滚动越快)
document.documentElement.scrollTop = scrollTop - 50;
}, 10); // 每10ms执行一次,控制滚动流畅度
}
</script>
</body>
</html>
8. 距离相关属性(offset/client/inner/scroll)
在 JS 中,常用四类属性获取元素或窗口的尺寸、位置、滚动距离,是实现拖拽、滚动、定位等交互的核心。
8.1 offset 系列:获取元素定位与尺寸
offset系列用于获取元素相对于定位父级的位置,以及元素的 “真实尺寸”(包含边框)。
| 属性 | 作用 | 特点 |
|---|---|---|
offsetLeft |
获取元素到最近定位父级的左侧距离(无定位父级则相对于 body) | 计算方式:元素左边框 → 定位父级左边框;只读属性 |
offsetTop |
获取元素到最近定位父级的顶部距离(无定位父级则相对于 body) | 计算方式:元素上边框 → 定位父级上边框;只读属性 |
offsetWidth |
获取元素的真实宽度(内容宽 + padding + border) | 不包含 margin;只读属性,不受元素隐藏(display:none)影响 |
offsetHeight |
获取元素的真实高度(内容高 + padding + border) | 不包含 margin;只读属性,不受元素隐藏(display:none)影响 |
offsetParent |
获取元素的最近定位父级(父级无定位则返回 body) | 定位指position: relative/absolute/fixed;只读属性 |
event.offsetX |
鼠标事件中,鼠标相对于事件源元素的水平坐标 | 基于元素左上角(0,0);与事件绑定,需通过event对象获取 |
event.offsetY |
鼠标事件中,鼠标相对于事件源元素的垂直坐标 | 基于元素左上角(0,0);与事件绑定,需通过event对象获取 |
案例:用 offset 获取元素位置
html
预览
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>offset系列案例</title>
<style>
.parent {
width: 400px;
height: 400px;
margin: 50px;
padding: 20px;
border: 2px solid #333;
position: relative; /* 定位父级 */
}
.child {
width: 200px;
height: 200px;
margin: 30px;
padding: 10px;
border: 1px solid #ff0000;
background-color: #ffe169;
}
.info {
margin: 0 50px;
padding: 10px;
border: 1px solid #ccc;
}
</style>
</head>
<body>
<div class="parent">
<div class="child" id="child"></div>
</div>
<div class="info" id="info"></div>
<script>
var child = document.getElementById('child');
var info = document.getElementById('info');
// 获取offset相关属性
var offsetLeft = child.offsetLeft;
var offsetTop = child.offsetTop;
var offsetWidth = child.offsetWidth;
var offsetHeight = child.offsetHeight;
var offsetParent = child.offsetParent.tagName; // 获取定位父级标签名
// 显示结果
info.innerHTML = `
<p>子元素相对于定位父级(${offsetParent})的左侧距离:${offsetLeft}px</p>
<p>子元素相对于定位父级(${offsetParent})的顶部距离:${offsetTop}px</p>
<p>子元素真实宽度(内容+padding+border):${offsetWidth}px</p>
<p>子元素真实高度(内容+padding+border):${offsetHeight}px</p>
`;
</script>
</body>
</html>
8.2 client 系列:获取元素可视尺寸与鼠标位置
client系列用于获取元素的 “可视尺寸”(不含边框)和鼠标相对于浏览器可视区的位置。
| 属性 | 作用 | 特点 |
|---|---|---|
clientWidth |
获取元素可视宽度(内容宽 + padding) | 不包含 border、margin;元素隐藏时返回 0;只读属性 |
clientHeight |
获取元素可视高度(内容高 + padding) | 不包含 border、margin;元素隐藏时返回 0;只读属性 |
event.clientX |
鼠标事件中,鼠标相对于浏览器可视区的水平坐标 | 基于浏览器左上角(0,0);不包含滚动出去的区域;需通过event获取 |
event.clientY |
鼠标事件中,鼠标相对于浏览器可视区的垂直坐标 | 基于浏览器左上角(0,0);不包含滚动出去的区域;需通过event获取 |
案例:用 client 获取鼠标位置
html
预览
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>client系列案例</title>
<style>
body {
height: 2000px;
}
.box {
width: 300px;
height: 300px;
margin: 50px;
padding: 20px;
border: 2px solid #ff0000;
background-color: #e7eebb;
}
.mouse-info {
margin: 0 50px;
padding: 10px;
border: 1px solid #ccc;
}
</style>
</head>
<body>
<div class="box" id="box"></div>
<div class="mouse-info" id="mouseInfo"></div>
<script>
var box = document.getElementById('box');
var mouseInfo = document.getElementById('mouseInfo');
// 监听鼠标移动事件
document.onmousemove = function(event) {
// 获取鼠标相对于浏览器可视区的位置
var clientX = event.clientX;
var clientY = event.clientY;
// 获取盒子的可视尺寸
var boxClientWidth = box.clientWidth;
var boxClientHeight = box.clientHeight;
// 显示结果
mouseInfo.innerHTML = `
<p>鼠标相对于浏览器可视区的X坐标:${clientX}px</p>
<p>鼠标相对于浏览器可视区的Y坐标:${clientY}px</p>
<p>盒子可视宽度(内容+padding):${boxClientWidth}px</p>
<p>盒子可视高度(内容+padding):${boxClientHeight}px</p>
`;
}
</script>
</body>
</html>
8.3 inner 系列:获取浏览器窗口尺寸
inner系列是窗口专属属性,仅能通过window调用,用于获取浏览器窗口的可视尺寸(包含滚动条)。
| 属性 | 作用 | 特点 |
|---|---|---|
window.innerWidth |
获取浏览器窗口的可视宽度(包含垂直滚动条宽度) | 只读属性;随窗口大小变化而变化 |
window.innerHeight |
获取浏览器窗口的可视高度(包含水平滚动条高度) | 只读属性;随窗口大小变化而变化 |
案例:对比 inner 与 client 的差异
当页面出现滚动条时,innerWidth包含滚动条宽度,document.documentElement.clientWidth不包含。
html
预览
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>inner系列案例</title>
<style>
body {
/* 强制显示滚动条 */
overflow-x: scroll;
overflow-y: scroll;
width: 2000px; /* 超出窗口宽度,触发水平滚动条 */
height: 2000px; /* 超出窗口高度,触发垂直滚动条 */
}
.size-info {
position: fixed;
top: 20px;
left: 20px;
background: #fff;
padding: 10px;
border: 1px solid #ccc;
}
</style>
</head>
<body>
<div class="size-info" id="sizeInfo"></div>
<script>
// 实时更新窗口尺寸信息
function updateSize() {
var innerWidth = window.innerWidth;
var innerHeight = window.innerHeight;
var clientWidth = document.documentElement.clientWidth;
var clientHeight = document.documentElement.clientHeight;
document.getElementById('sizeInfo').innerHTML = `
<p>window.innerWidth(含滚动条):${innerWidth}px</p>
<p>window.innerHeight(含滚动条):${innerHeight}px</p>
<p>document.documentElement.clientWidth(不含滚动条):${clientWidth}px</p>
<p>document.documentElement.clientHeight(不含滚动条):${clientHeight}px</p>
`;
}
// 初始加载时更新
updateSize();
// 窗口大小改变时更新
window.onresize = updateSize;
</script>
</body>
</html>
8.4 scroll 系列:获取元素滚动信息
scroll系列用于获取元素的滚动距离、内容总尺寸,是实现滚动加载、滚动定位的核心。
| 属性 | 作用 | 特点 |
|---|---|---|
scrollWidth |
获取元素内容的总宽度(含不可见滚动部分,内容宽 + padding) | 不包含 border、margin;内容不足时等于clientWidth;只读属性 |
scrollHeight |
获取元素内容的总高度(含不可见滚动部分,内容高 + padding) | 不包含 border、margin;内容不足时等于clientHeight;只读属性 |
scrollTop |
获取元素向上滚动的距离(卷出可视区的顶部高度) | 可读写;标准浏览器用document.documentElement.scrollTop,IE 用document.body.scrollTop |
scrollLeft |
获取元素向左滚动的距离(卷出可视区的左侧宽度) | 可读写;标准浏览器用document.documentElement.scrollLeft,IE 用document.body.scrollLeft |
element.scrollIntoView() |
滚动元素的父容器,使元素进入可视区 | 方法;参数{behavior: 'smooth'}可实现平滑滚动 |
案例 1:获取页面滚动距离
html
预览
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>scroll系列案例1</title>
<style>
body {
height: 3000px;
}
.scroll-info {
position: fixed;
top: 20px;
right: 20px;
background: #fff;
padding: 10px;
border: 1px solid #ccc;
}
</style>
</head>
<body>
<div class="scroll-info" id="scrollInfo"></div>
<script>
// 监听页面滚动
window.onscroll = function() {
// 兼容写法:获取页面垂直滚动距离
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
// 兼容写法:获取页面水平滚动距离
var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft;
document.getElementById('scrollInfo').innerHTML = `
<p>页面垂直滚动距离:${scrollTop}px</p>
<p>页面水平滚动距离:${scrollLeft}px</p>
`;
}
</script>
</body>
</html>
案例 2:用 scrollIntoView 实现平滑滚动到元素
html
预览
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>scroll系列案例2</title>
<style>
body {
height: 2000px;
}
.target {
margin-top: 1500px;
width: 300px;
height: 100px;
background-color: #ffc0cb;
display: flex;
align-items: center;
justify-content: center;
margin-left: 50px;
}
.btn {
position: fixed;
top: 20px;
left: 20px;
padding: 8px 16px;
background-color: #ffe169;
border: none;
cursor: pointer;
border-radius: 4px;
}
</style>
</head>
<body>
<button class="btn" id="scrollBtn">滚动到目标元素</button>
<div class="target" id="target">目标元素</div>
<script>
var scrollBtn = document.getElementById('scrollBtn');
var target = document.getElementById('target');
// 点击按钮,滚动到目标元素
scrollBtn.onclick = function() {
target.scrollIntoView({
behavior: 'smooth', // 平滑滚动
block: 'start' // 元素顶部与可视区顶部对齐(可选:center/end)
});
}
</script>
</body>
</html>
8.5 拖拽移动效果(综合案例)
结合mousedown/mousemove/mouseup事件与offset/client系列属性,实现元素拖拽功能。
html
预览
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>元素拖拽案例</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
#dragBox {
width: 100px;
height: 100px;
background-color: #ffd700;
position: absolute; /* 拖拽元素必须定位 */
top: 100px;
left: 100px;
cursor: move; /* 鼠标悬浮时显示“移动”光标 */
}
</style>
</head>
<body>
<div id="dragBox"></div>
<script>
var dragBox = document.getElementById('dragBox');
var isDragging = false; // 标记是否正在拖拽
var startX, startY; // 鼠标按下时的初始坐标
var startLeft, startTop; // 元素初始位置
// 1. 鼠标按下:记录初始状态
dragBox.onmousedown = function(event) {
isDragging = true;
// 获取鼠标相对于浏览器可视区的初始坐标
startX = event.clientX;
startY = event.clientY;
// 获取元素相对于定位父级的初始位置
startLeft = dragBox.offsetLeft;
startTop = dragBox.offsetTop;
// 防止拖拽时选中页面文字
dragBox.style.userSelect = 'none';
}
// 2. 鼠标移动:更新元素位置
document.onmousemove = function(event) {
if (!isDragging) return; // 未按下鼠标时,不执行拖拽逻辑
// 计算鼠标移动的距离
var moveX = event.clientX - startX;
var moveY = event.clientY - startY;
// 计算元素最终位置(初始位置 + 移动距离)
var endLeft = startLeft + moveX;
var endTop = startTop + moveY;
// 更新元素位置(避免元素超出窗口,可选)
endLeft = Math.max(0, Math.min(endLeft, window.innerWidth - dragBox.offsetWidth));
endTop = Math.max(0, Math.min(endTop, window.innerHeight - dragBox.offsetHeight));
// 应用新位置
dragBox.style.left = endLeft + 'px';
dragBox.style.top = endTop + 'px';
}
// 3. 鼠标抬起:结束拖拽
document.onmouseup = function() {
if (isDragging) {
isDragging = false;
// 恢复文字选中功能
dragBox.style.userSelect = 'auto';
}
}
</script>
</body>
</html>
8.6 getBoundingClientRect ():获取元素完整位置信息
Element.getBoundingClientRect()方法返回元素相对于浏览器可视区的位置和尺寸,返回值为DOMRect对象,包含top/right/bottom/left/width/height等属性,是判断元素是否在可视区的常用工具。
核心属性说明
| 属性 | 作用 | 计算基准 |
|---|---|---|
top |
元素上边框到浏览器可视区顶部的距离 | 可视区顶部(0,0)向下为正 |
bottom |
元素下边框到浏览器可视区顶部的距离 | 可视区顶部(0,0)向下为正 |
left |
元素左边框到浏览器可视区左侧的距离 | 可视区左侧(0,0)向右为正 |
right |
元素右边框到浏览器可视区左侧的距离 | 可视区左侧(0,0)向右为正 |
width |
元素的真实宽度(等同于offsetWidth) |
内容宽 + padding + border |
height |
元素的真实高度(等同于offsetHeight) |
内容高 + padding + border |
关键特点
- 相对可视区:属性值会随页面滚动而变化(滚动后元素位置相对可视区改变)。
- 实时更新:每次调用方法都会重新计算最新位置,确保数据准确。
- 兼容性好:支持所有现代浏览器及 IE8+,无需额外兼容处理。
案例 1:获取元素位置信息
html
预览
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>getBoundingClientRect() 基础案例</title>
<style>
body {
height: 2000px;
padding-top: 100px;
}
.target-box {
width: 200px;
height: 150px;
margin-left: 50px;
padding: 20px;
border: 3px solid #ff4400;
background-color: #f0f8ff;
}
.info-panel {
position: fixed;
top: 20px;
right: 20px;
background: #fff;
padding: 15px;
border: 1px solid #ccc;
width: 300px;
}
.info-panel p {
margin: 5px 0;
font-size: 14px;
}
</style>
</head>
<body>
<div class="target-box" id="targetBox">目标元素</div>
<div class="info-panel" id="infoPanel"></div>
<script>
var targetBox = document.getElementById('targetBox');
var infoPanel = document.getElementById('infoPanel');
// 定义更新位置信息的函数
function updateRectInfo() {
// 调用方法获取DOMRect对象
var rect = targetBox.getBoundingClientRect();
// 渲染位置信息
infoPanel.innerHTML = `
<h4>元素位置信息(相对可视区)</h4>
<p>top(上边框到可视区顶部):${Math.round(rect.top)}px</p>
<p>bottom(下边框到可视区顶部):${Math.round(rect.bottom)}px</p>
<p>left(左边框到可视区左侧):${Math.round(rect.left)}px</p>
<p>right(右边框到可视区左侧):${Math.round(rect.right)}px</p>
<p>width(元素真实宽度):${Math.round(rect.width)}px</p>
<p>height(元素真实高度):${Math.round(rect.height)}px</p>
`;
}
// 初始加载时更新
updateRectInfo();
// 页面滚动时实时更新(因相对可视区位置会变化)
window.onscroll = updateRectInfo;
// 窗口大小改变时更新
window.onresize = updateRectInfo;
</script>
</body>
</html>
案例 2:判断元素是否在可视区(滚动加载前置逻辑)
利用getBoundingClientRect()判断元素是否进入可视区,是实现 “滚动加载”“懒加载图片” 的核心逻辑。
html
预览
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>判断元素是否在可视区</title>
<style>
body {
height: 3000px;
padding-top: 500px;
}
.lazy-box {
width: 300px;
height: 200px;
margin: 0 auto;
border: 2px dashed #666;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
}
.status {
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%);
background: #ff4400;
color: #fff;
padding: 8px 16px;
border-radius: 4px;
display: none;
}
</style>
</head>
<body>
<div class="lazy-box" id="lazyBox">待检测元素</div>
<div class="status" id="status">元素进入可视区!</div>
<script>
var lazyBox = document.getElementById('lazyBox');
var status = document.getElementById('status');
// 判断元素是否在可视区的函数
function isInViewport(element) {
var rect = element.getBoundingClientRect();
var viewportHeight = window.innerHeight; // 可视区高度
var viewportWidth = window.innerWidth; // 可视区宽度
// 核心逻辑:元素顶部 <= 可视区底部,且元素底部 >= 可视区顶部(垂直方向可见)
// 同时 元素左侧 <= 可视区右侧,且元素右侧 >= 可视区左侧(水平方向可见)
return (
rect.top <= viewportHeight &&
rect.bottom >= 0 &&
rect.left <= viewportWidth &&
rect.right >= 0
);
}
// 检测并更新状态
function checkVisibility() {
if (isInViewport(lazyBox)) {
status.style.display = 'block';
lazyBox.style.backgroundColor = '#90ee90';
lazyBox.textContent = '元素已在可视区';
} else {
status.style.display = 'none';
lazyBox.style.backgroundColor = 'transparent';
lazyBox.textContent = '待检测元素(滚动到可视区试试)';
}
}
// 初始检测
checkVisibility();
// 滚动时实时检测
window.onscroll = checkVisibility;
// 窗口大小改变时检测
window.onresize = checkVisibility;
</script>
</body>
</html>
9. 距离相关属性对比总结
为了更清晰区分offset/client/inner/scroll四类属性的差异,整理对比表格如下:
| 对比维度 | offset 系列 | client 系列 | inner 系列 | scroll 系列 |
|---|---|---|---|---|
| 作用对象 | 任意 DOM 元素 | 任意 DOM 元素 | 仅 window(浏览器窗口) | 任意 DOM 元素(含 window) |
| 尺寸计算范围 | 内容 + padding + border | 内容 + padding | 窗口可视区(含滚动条) | 内容总尺寸(含不可见部分) |
| 位置参考基准 | 最近定位父级(无则 body) | 浏览器可视区(鼠标位置) | 无(仅尺寸,无位置) | 元素自身(滚动距离) |
| 是否受滚动影响 | 不受(位置固定) | 鼠标位置不受,元素尺寸受 | 不受(窗口尺寸固定) | 受(滚动距离动态变化) |
| 是否可读写 | 只读 | 只读 | 只读 | scrollTop/scrollLeft 可写 |
| 典型用途 | 元素定位、拖拽计算 | 鼠标位置、元素可视尺寸 | 窗口尺寸适配 | 滚动距离、滚动加载 |
10. JS 中两种添加事件的方式
在 JavaScript 中,为元素绑定事件主要有两种方式:on+事件名 和事件监听(addEventListener)。两种方式各有特点,适用于不同场景。
10.1 方式一:on+事件名(DOM0 级事件绑定)
这是最基础的事件绑定方式,直接通过元素的事件属性绑定函数。
基本用法
javascript
// 获取元素
var oneDiv = document.getElementById('oneDiv');
// 添加事件关联(同一事件多次绑定,后面的会覆盖前面的)
oneDiv.onclick = function() {
console.log("第一次点击"); // 不会执行,被后面的覆盖
};
oneDiv.onclick = function() {
console.log("第二次点击"); // 仅执行此函数
};
// 取消事件关联(三种方式)
oneDiv.onclick = null;
// 或 oneDiv.onclick = false;
// 或 oneDiv.onclick = function() { return false; };
特点
- 简单直观,兼容性好(支持所有浏览器)
- 同一元素的同一事件只能绑定一个处理函数,多次绑定会覆盖
- 移除事件只需将事件属性设为
null或false - 事件处理函数中的
this指向绑定事件的元素
10.2 方式二:事件监听(addEventListener,DOM2 级事件绑定)【推荐】
这是标准的事件绑定方式,支持为同一元素的同一事件绑定多个处理函数,更灵活。
基本语法
javascript
element.addEventListener(event, function, useCapture);
event:必需,事件名(不含on前缀,如click而非onclick)function:必需,事件触发时执行的函数useCapture:可选,布尔值(false为事件冒泡,true为事件捕获,默认false)
基本用法
javascript
// 定义处理函数
function fun1() {
console.log("函数1执行");
}
function fun2() {
console.log("函数2执行");
}
// 添加事件关联(可绑定多个函数)
oneDiv.addEventListener("click", fun1);
oneDiv.addEventListener("click", fun2);
oneDiv.addEventListener("click", function() {
console.log("匿名函数执行");
});
// 点击元素时,三个函数按绑定顺序依次执行
// 取消事件关联(只能移除具名函数,匿名函数无法移除)
oneDiv.removeEventListener("click", fun1); // 成功移除fun1
特点
- 支持为同一元素的同一事件绑定多个处理函数,按绑定顺序执行
- 可通过
useCapture参数控制事件流(冒泡 / 捕获) - 适用于所有 DOM 元素(不仅限于 HTML 元素)
- 移除事件需使用
removeEventListener,且必须传入与绑定相同的函数引用 - 事件处理函数中的
this指向绑定事件的元素
10.3 兼容性处理(IE 低版本)
IE8 及以下版本不支持 addEventListener 和 removeEventListener,需使用专有方法 attachEvent 和 detachEvent。
javascript
// 标准浏览器
oneDiv.addEventListener("click", fun1);
oneDiv.removeEventListener("click", fun1);
// IE 低版本浏览器(需加 on 前缀)
oneDiv.attachEvent("onclick", fun1);
oneDiv.detachEvent("onclick", fun1);
10.4 两种方式对比
| 对比维度 | on+事件名(DOM0 级) |
addEventListener(DOM2 级) |
|---|---|---|
| 多事件绑定 | 不支持,后绑定的会覆盖前面的 | 支持,可绑定多个函数,按顺序执行 |
| 事件流控制 | 不支持(仅冒泡) | 支持(通过 useCapture 控制冒泡 / 捕获) |
| 适用对象 | 主要用于 HTML 元素 | 适用于所有 DOM 元素 |
| 事件名格式 | 需加 on 前缀(如 onclick) |
不加 on 前缀(如 click) |
| 移除事件 | 直接赋值 null(element.onclick = null) |
需用 removeEventListener,且需函数引用 |
| 兼容性 | 所有浏览器支持 | IE8 及以下不支持,需用 attachEvent |
| 推荐场景 | 简单交互,同一事件只需一个处理函数 | 复杂交互,同一事件需多个处理函数 |
10.5 事件绑定的封装(兼容所有浏览器)
为简化跨浏览器事件绑定,可封装通用函数:
javascript
// 添加事件绑定
function addEvent(obj, eventType, handler) {
if (obj.addEventListener) {
// 标准浏览器
obj.addEventListener(eventType, handler);
} else if (obj.attachEvent) {
// IE 低版本
obj.attachEvent("on" + eventType, handler);
} else {
// 最原始方式(兼容极旧浏览器)
obj["on" + eventType] = handler;
}
}
// 移除事件绑定
function removeEvent(obj, eventType, handler) {
if (obj.removeEventListener) {
// 标准浏览器
obj.removeEventListener(eventType, handler);
} else if (obj.detachEvent) {
// IE 低版本
obj.detachEvent("on" + eventType, handler);
} else {
// 最原始方式
obj["on" + eventType] = null;
}
}
// 使用示例
var box = document.getElementById('box');
function handleClick() {
console.log('点击事件触发');
}
// 添加事件
addEvent(box, 'click', handleClick);
// 移除事件
removeEvent(box, 'click', handleClick);
11. DOM CSS 动态样式
JavaScript 可以动态操作元素的 CSS 样式,包括获取和设置样式。常用方式有 obj.style、getComputedStyle 和 currentStyle。
11.1 obj.style(操作行内样式)
直接通过元素的 style 属性操作样式,既可获取也可设置,但仅作用于元素的行内样式(style 属性中定义的样式)。
基本用法
html
预览
<div id="box" style="width: 100px; height: 100px; background-color: red;"></div>
<script>
var box = document.getElementById('box');
// 获取行内样式(仅能获取行内定义的样式)
console.log(box.style.width); // 输出 "100px"
console.log(box.style.backgroundColor); // 输出 "red"
// 设置样式(会添加到行内样式中)
box.style.width = "200px"; // 宽度变为 200px
box.style.height = "200px"; // 高度变为 200px
box.style.backgroundColor = "blue"; // 背景色变为蓝色
box.style.border = "2px solid black"; // 添加边框
box.style.fontSize = "16px"; // 字体大小变为 16px(注意驼峰命名)
</script>
注意事项
- CSS 属性名在 JS 中需使用驼峰命名法(如
background-color→backgroundColor) - 只能操作行内样式,无法获取
<style>标签或外部 CSS 文件中定义的样式 - 设置样式时必须包含单位(如
width: "100"无效,需写width: "100px")
11.2 getComputedStyle(获取计算后样式,非 IE 浏览器)
用于获取元素的最终计算样式(包括行内样式、内部样式、外部样式),仅能获取不能设置,支持除 IE8 及以下外的所有浏览器。
基本语法
javascript
window.getComputedStyle(元素)[样式属性]
用法示例
html
预览
<style>
#box {
width: 150px;
height: 150px;
color: green;
}
</style>
<div id="box" style="background-color: yellow;"></div>
<script>
var box = document.getElementById('box');
var computedStyle = window.getComputedStyle(box);
// 获取计算后样式(包括 CSS 中定义的样式)
console.log(computedStyle.width); // 输出 "150px"(来自 CSS)
console.log(computedStyle.backgroundColor); // 输出 "rgb(255, 255, 0)"(来自行内)
console.log(computedStyle.color); // 输出 "rgb(0, 128, 0)"(来自 CSS)
</script>
11.3 currentStyle(获取计算后样式,IE 浏览器)
IE 浏览器(IE8 及以下)专用,功能与 getComputedStyle 类似,仅能获取不能设置。
基本语法
javascript
元素.currentStyle[样式属性]
用法示例
javascript
// 仅 IE8 及以下生效
var box = document.getElementById('box');
console.log(box.currentStyle.width); // 获取计算后的宽度
console.log(box.currentStyle.color); // 获取计算后的颜色
11.4 获取 CSS 样式的兼容写法
为兼容所有浏览器,可封装获取样式的通用函数:
javascript
/**
* 获取元素的计算后样式
* @param {Object} obj - 目标元素
* @param {String} attr - 样式属性名(如 "width"、"color")
* @returns {String} 样式属性值
*/
function getCss(obj, attr) {
if (obj.currentStyle) {
// IE 浏览器
return obj.currentStyle[attr];
} else {
// 标准浏览器
return window.getComputedStyle(obj)[attr];
}
}
// 使用示例
var box = document.getElementById('box');
console.log(getCss(box, 'width')); // 获取宽度(兼容所有浏览器)
console.log(getCss(box, 'backgroundColor')); // 获取背景色
11.5 操作类名(className)
通过修改元素的 className 属性,可批量切换样式(推荐用于复杂样式变更)。
html
预览
<style>
.default {
width: 100px;
height: 100px;
background-color: gray;
}
.active {
width: 200px;
height: 200px;
background-color: orange;
border: 2px solid red;
}
</style>
<div id="box" class="default"></div>
<script>
var box = document.getElementById('box');
// 切换类名(批量变更样式)
box.onclick = function() {
if (this.className === 'default') {
this.className = 'active'; // 应用 active 样式
} else {
this.className = 'default'; // 恢复 default 样式
}
};
</script>
12. event 对象使用技巧
event 对象包含事件相关的详细信息(如事件类型、触发元素、鼠标位置等),是处理事件的核心工具。
12.1 常用属性
| 属性 | 作用 |
|---|---|
e.target |
获取触发事件的元素(事件源) |
e.currentTarget |
获取绑定事件的元素(与 this 等效) |
e.type |
获取事件类型(如 click、mouseover) |
e.preventDefault() |
阻止事件的默认行为(如链接跳转、表单提交) |
e.stopPropagation() |
阻止事件冒泡 |
e.dataset |
获取元素上通过 data-* 属性定义的自定义数据 |
-
事件绑定方式:
on+事件名适合简单场景,兼容性好但不支持多事件绑定addEventListener功能强大,支持多事件绑定和事件流控制,推荐使用
-
样式操作:
obj.style用于操作行内样式(可读写)getComputedStyle/currentStyle用于获取计算后样式(只读)className适合批量切换样式
-
event对象:提供事件相关信息,如target(事件源)、dataset(自定义数据)等,是事件处理的关键。
12.2 实用案例
案例 1:通过 e.target 简化事件处理
html
预览
<ul id="list">
<li>项目 1</li>
<li>项目 2</li>
<li>项目 3</li>
</ul>
<script>
// 给父元素绑定事件,通过 e.target 判断子元素(事件委托)
document.getElementById('list').addEventListener('click', function(e) {
// e.target 是触发事件的 li 元素
if (e.target.tagName === 'LI') {
console.log('点击了:' + e.target.textContent);
e.target.style.color = 'red'; // 仅改变被点击的 li 样式
}
});
</script>
案例 2:通过 e.dataset 获取自定义数据
html
预览
<button class="btn" data-id="1001" data-name="商品A">点击查看</button>
<script>
document.querySelector('.btn').addEventListener('click', function(e) {
// 获取 data-* 属性数据
console.log('商品ID:', e.target.dataset.id); // 输出 "1001"
console.log('商品名称:', e.target.dataset.name); // 输出 "商品A"
});
</script>
案例 3:阻止默认行为
html
预览
<a href="https://www.example.com" id="link">示例链接</a>
<script>
document.getElementById('link').addEventListener('click', function(e) {
e.preventDefault(); // 阻止链接跳转
console.log('链接被点击,但未跳转');
});
</script>
13. JS 效果案例
13.1 苏宁易购手机端:密码显示 / 隐藏(小眼睛效果)
html
预览
<style>
.input-group {
position: relative;
width: 300px;
margin: 20px;
}
.password-input {
width: 100%;
padding: 10px 40px 10px 10px;
box-sizing: border-box;
}
.eye-icon {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
cursor: pointer;
}
</style>
<div class="input-group">
<input type="password" class="password-input" id="password" placeholder="请输入密码">
<span class="eye-icon" id="eye">👁️</span>
</div>
<script>
var passwordInput = document.getElementById('password');
var eyeIcon = document.getElementById('eye');
var isVisible = false;
// 点击小眼睛切换密码显示状态
eyeIcon.addEventListener('click', function() {
isVisible = !isVisible;
// 切换 input 类型(password/text)
passwordInput.type = isVisible ? 'text' : 'password';
// 切换图标
eyeIcon.textContent = isVisible ? '👁️🗨️' : '👁️';
});
</script>
13.2 京东官网:Tab 切换效果(排他思想)
html
预览
<style>
.tab-container {
width: 500px;
margin: 20px;
}
.tab-nav {
display: flex;
border-bottom: 1px solid #ccc;
}
.tab-nav li {
padding: 10px 20px;
cursor: pointer;
list-style: none;
}
.tab-nav li.active {
color: #e4393c;
border-bottom: 2px solid #e4393c;
}
.tab-content {
padding: 20px;
}
.tab-panel {
display: none;
}
.tab-panel.active {
display: block;
}
</style>
<div class="tab-container">
<ul class="tab-nav" id="tabNav">
<li class="active">商品介绍</li>
<li>规格参数</li>
<li>售后保障</li>
</ul>
<div class="tab-content">
<div class="tab-panel active">商品介绍内容...</div>
<div class="tab-panel">规格参数内容...</div>
<div class="tab-panel">售后保障内容...</div>
</div>
</div>
<script>
var navItems = document.querySelectorAll('#tabNav li');
var panels = document.querySelectorAll('.tab-panel');
// 为每个导航项绑定点击事件
navItems.forEach(function(item, index) {
item.addEventListener('click', function() {
// 排他思想:先移除所有项的 active 类
navItems.forEach(nav => nav.classList.remove('active'));
panels.forEach(panel => panel.classList.remove('active'));
// 为当前项添加 active 类
this.classList.add('active');
panels[index].classList.add('active');
});
});
</script>
13.3 小米官网:搜索框下拉列表
html
预览
<style>
.search-container {
position: relative;
width: 300px;
margin: 20px;
}
.search-input {
width: 100%;
padding: 8px 10px;
box-sizing: border-box;
}
.search-dropdown {
position: absolute;
top: 100%;
left: 0;
width: 100%;
border: 1px solid #ccc;
border-top: none;
background: #fff;
display: none;
}
.search-dropdown.active {
display: block;
}
.search-dropdown li {
padding: 8px 10px;
list-style: none;
cursor: pointer;
}
.search-dropdown li:hover {
background-color: #f5f5f5;
}
</style>
<div class="search-container">
<input type="text" class="search-input" id="searchInput" placeholder="搜索商品...">
<ul class="search-dropdown" id="searchDropdown">
<li>小米手机</li>
<li>红米手机</li>
<li>笔记本电脑</li>
<li>智能手表</li>
</ul>
</div>
<script>
var searchInput = document.getElementById('searchInput');
var searchDropdown = document.getElementById('searchDropdown');
// 输入框获取焦点时显示下拉列表
searchInput.addEventListener('focus', function() {
searchDropdown.classList.add('active');
});
// 输入框失去焦点时隐藏下拉列表(延迟执行,确保点击列表项有效)
searchInput.addEventListener('blur', function() {
setTimeout(function() {
searchDropdown.classList.remove('active');
}, 200);
});
// 点击列表项填充输入框
document.querySelectorAll('#searchDropdown li').forEach(function(item) {
item.addEventListener('click', function() {
searchInput.value = this.textContent;
searchDropdown.classList.remove('active');
});
});
</script>
更多推荐


所有评论(0)