tab页面结构
页面结构:
└── 容器div container
├── 标签栏div tab_box
│ ├── 按钮 tab_btn (激活) - “首页”
│ ├── 按钮 tab_btn - “关于”
│ ├── 按钮 tab_btn - “绿豆沙”
│ ├── 按钮 tab_btn - “我的”
│ ├── 按钮 tab_btn - “其他”
│ └── 下划线 line
└── 内容区div content_box
├── 内容div content (active)
│ ├── 图片 bg.jpg
│ ├── 标题 h2 - “首页”
│ └── 段落 p - 诗句内容
├── 内容div content
│ ├── 图片 bg.jpg
│ ├── 标题 h2 - “关于”
│ └── 段落 p - 诗句内容
html, body {
height: 100%;
margin: 0;
}
/*设置内容居中*/
body{
background-color: #deeeff;
display: flex;
justify-content: center;
align-items: center;
}
为什么这样写才行 不能一起写在body里
这样也可以
body{
background-color: #deeeff;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
height: 100%
是“继承父元素高度”
body
的高度如果是100%
,它的意思是:我要占满我的父元素html
的高度。- 但如果你没有写
html { height: 100%; }
,那html
的高度就是默认的“被内容撑开的高度”,比如几十像素。 - 所以
body { height: 100%; }
没有“基准”,就不起作用。
写法 | 结果 |
---|---|
✅ html, body { height: 100%; } | body 才能真正占满全屏 |
❌ 只写 body { height: 100%; } | body 高度 = 内容高度,无法居中 |
改写jQue版本
$(document).ready(function (){
const $tabs = $('.tab_btn');
const $contents = $('.content');
const $line = $('.line');
$tabs.each(function(index) {
$(this).on('click', function(e) {
// 移除所有 tab 的 active 类
$tabs.removeClass('active');
console.log($(this))
// 当前 tab 添加 active 类
$(this).addClass('active');
// 设置滑块 line 的宽度和位置
$line.css({
width: $(this).outerWidth() + 'px',
left: $(this).position().left + 'px'
});
// 隐藏所有内容
$contents.removeClass('active');
// 当前索引对应的内容添加 active 类
$contents.eq(index).addClass('active');
});
});
})
原生 JS | jQuery 对应写法 |
---|---|
document.querySelectorAll() | $('.class') |
element.addEventListener() | $(element).on('click', fn) |
classList.add/remove | addClass() / removeClass() |
offsetWidth | $(element).outerWidth() |
offsetLeft | $(element).position().left |
forEach() | each() 或使用 .on() 自动遍历 |
all_content[index] | $contents.eq(index) |
outerWidth()
是 jQuery 获取宽度的方法,包含 padding 和 border(相当于原生的 offsetWidth
)。
position().left
获取的是元素相对父元素的左边距,对应 offsetLeft
。
.eq(index)
是 jQuery 中选取第 index
个元素的方法。
const tabs = document.querySelectorAll('.tab_btn');
const all_content = document.querySelectorAll('.content');
// 遍历所有的tab元素
tabs.forEach((tab, index)=>{
tab.addEventListener('click',(e)=>{
console.log('你点击了按钮:', e.target); // 被点击的元素
console.log('事件类型:', e.type); // click
console.log('点击位置:', e.clientX, e.clientY); // 鼠标点击的位置
// 删除所有的active元素
tabs.forEach(tab => {
tab.classList.remove('active')
});
// 给当前的tab增加active元素
tab.classList.add('active')
// 获取line元素 计算当前元素距离左边的距离
var line = document.querySelector('.line');
line.style.width = e.target.offsetWidth + "px";
line.style.left = e.target.offsetLeft + "px";
console.log(e.target.offsetWidth)
console.log(e.target.offsetLeft)
all_content.forEach(content => {
content.classList.remove('active')
});
console.log(all_content)
all_content[index].classList.add('active');
})
})
📌使用 .forEach()
:
let tabs = ['首页', '关于', '我的'];
tabs.forEach((tab, index) => {
console.log(index, tab);
});
let tabs = ['首页', '关于', '我的'];
for (let i = 0; i < tabs.length; i++) {
console.log(i, tabs[i]);
}
遍历所有元素不能用 break continue
.addEventListener
是 JavaScript 中用于 注册事件监听器 的方法,它让你可以对某个 HTML 元素添加“监听”,当发生某种事件(如点击、悬停、键盘输入等)时执行指定的函数。
📌 基本语法:
element.addEventListener(event, function, useCapture);
参数说明:
event
(字符串):你要监听的事件类型,比如"click"
、"mouseover"
、"keydown"
等。function
(函数):当事件发生时执行的回调函数。useCapture
(可选,布尔值):默认为false
,一般不用管,涉及事件捕获阶段(进阶话题)。
📌 e常用的属性方法
属性/方法 | 说明 |
---|---|
e.target | 触发事件的元素(比如被点击的按钮) |
e.currentTarget | 当前监听器所在的元素(一般等于 tab ) |
e.type | 事件类型,例如 "click" |
e.clientX/Y | 鼠标点击的坐标(相对于窗口) |
e.pageX/Y | 鼠标点击的坐标(相对于页面) |
e.preventDefault() | 阻止默认行为(如链接跳转) |
e.stopPropagation() | 阻止事件冒泡 |
📌e.target.offsetWidth && e.target.offsetLeft
offsetWidth
表示一个元素的 “实际宽度”,包含了:
- 元素的
content
(内容区) padding
(内边距)border
(边框)
不包含:
margin
(外边距)
.box {
width: 100px;
padding: 10px;
border: 2px solid black;
margin: 20px;
}
<div class="box">Hello</div>
const box = document.querySelector('.box');
console.log(box.offsetWidth); // 👉 124
width
= 100padding
左 + 右 = 10 + 10 = 20border
左 + 右 = 2 + 2 = 4- ✅ 所以
offsetWidth = 100 + 20 + 4 = 124
margin
不包含在内!
其他属性
属性 | 含义 |
---|---|
offsetWidth | 元素的 总宽度(含padding和border) |
clientWidth | 内容 + padding,不含 border |
scrollWidth | 实际滚动区域宽度(内容超出时会更大) |
style.width | 你在 CSS 里写的 width 值(可能是空) |
offsetLeft
表示:这个元素距离“父元素”的左边有多远。
就像你问一个盒子:“你从你爸左边数第几像素开始站的?”
<div class="parent" style="position: relative;">
<div class="child" style="position: absolute; left: 50px;"></div>
</div>
const child = document.querySelector('.child');
console.log(child.offsetLeft); // 👉 50
[parent]
|-----------------------------------------|
| |
| <-- offsetLeft --> [child] |
| |
|-----------------------------------------|
其他属性
属性名 | 含义 |
---|---|
offsetLeft | 元素相对最近的定位父元素左边的距离 |
clientLeft | 元素左边框的宽度(很少用) |
scrollLeft | 元素被滚动出去的水平像素值(用于滚动位置) |
getBoundingClientRect().left | 元素相对于视口的左边距离(常用于精确定位) |
📌.classList
就像是元素身上的「衣服列表」,你可以:
- 👕
add()
:穿上一件新衣服 - ❌
remove()
:脱掉一件衣服 - 🔄
toggle()
:穿上或脱掉(根据当前状态) - 🔍
contains()
:检查是否穿了某件衣服
// 查看当前有哪些类
console.log(box.classList); // DOMTokenList ['box', 'active']
// 添加类
box.classList.add('highlight'); // <div class="box active highlight">
// 移除类
box.classList.remove('active'); // <div class="box highlight">
// 切换类(如果有就移除,没有就添加)
box.classList.toggle('hidden');
// 判断是否有某个类
console.log(box.classList.contains('box')); // true
.classList
常用方法总结:
方法名 | 功能说明 |
---|---|
add('class') | 添加类名 |
remove('class') | 移除类名 |
toggle('class') | 切换类名(有就移除,无就添加) |
contains('class') | 判断是否包含某个类名 |
replace('old', 'new') | 替换一个类名 |
[...element.classList] | 把类名转为数组(方便遍历) |
📌拓展
event事件有哪些
一、鼠标事件(Mouse Events)
事件名 | 说明 |
---|---|
click | 鼠标点击(按下+释放) |
dblclick | 双击 |
mousedown | 鼠标按下 |
mouseup | 鼠标释放 |
mousemove | 鼠标移动 |
mouseenter | 鼠标首次进入元素,不冒泡 |
mouseleave | 鼠标离开元素,不冒泡 |
mouseover | 鼠标进入元素,会冒泡 |
mouseout | 鼠标离开元素,会冒泡 |
contextmenu | 鼠标右键 |
二、键盘事件(Keyboard Events)
事件名 | 说明 |
---|---|
keydown | 按键按下 |
keyup | 按键松开 |
keypress ⚠️ | 已废弃,用 keydown 代替(旧版用于输入字符) |
三、表单事件(Form Events)
事件名 | 说明 |
---|---|
submit | 表单提交 |
reset | 表单重置 |
change | 值发生改变(如 <select> 、复选框) |
input | 输入框内容变化(比 change 更即时) |
focus | 获取焦点(如点击输入框) |
blur | 失去焦点 |
focusin | 类似 focus ,但会冒泡 |
focusout | 类似 blur ,但会冒泡 |
四、剪贴板事件(Clipboard Events)
事件名 | 说明 |
---|---|
copy | 拷贝 |
cut | 剪切 |
paste | 粘贴 |
五、拖放事件(Drag & Drop Events)
事件名 | 说明 |
---|---|
drag | 拖动进行中 |
dragstart | 拖动开始 |
dragend | 拖动结束 |
dragenter | 拖动元素进入目标区域 |
dragleave | 拖动元素离开目标区域 |
dragover | 拖动元素在目标区域上 |
drop | 拖动元素放下 |
六、窗口事件(Window / Document Events)
事件名 | 说明 |
---|---|
load | 页面加载完成(包括图片) |
DOMContentLoaded | DOM 加载完成(不等图片) |
resize | 窗口大小变化 |
scroll | 页面滚动 |
unload | 页面卸载(几乎已废弃) |
beforeunload | 页面即将卸载,常用于弹窗提醒 |
error | 出现错误(可用于图片、JS 错误) |
七、触摸事件(移动端 Touch Events)
事件名 | 说明 |
---|---|
touchstart | 手指触摸屏幕 |
touchmove | 手指滑动 |
touchend | 手指离开 |
touchcancel | 系统取消触摸事件 |
八、媒体事件(用于 <audio>
、<video>
)
事件名 | 说明 |
---|---|
play | 开始播放 |
pause | 暂停 |
ended | 播放结束 |
timeupdate | 播放时间更新 |
volumechange | 音量改变 |
loadeddata | 媒体加载完成 |
error | 媒体加载错误 |
九、自定义事件(Custom Events)
- 你可以自己创建事件:
javascript复制编辑const event = new CustomEvent("myevent", { detail: { key: "value" } });
element.dispatchEvent(event);
十、动画/过渡事件(CSS 动画相关)
事件名 | 说明 |
---|---|
animationstart | 动画开始 |
animationend | 动画结束 |
animationiteration | 动画重复 |
transitionstart | 过渡开始 |
transitionend | 过渡结束 |
源代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
*{
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Poppins', sans-serif;
}
html, body {
height: 100%;
margin: 0;
}
/*设置内容居中*/
body{
background-color: #deeeff;
display: flex;
justify-content: center;
align-items: center;
}
.tab_box{
width: 100%;
display: flex;
justify-content: space-around;
align-items: center;
border-bottom: 2px solid rgb(209, 209, 209);
position: relative;
}
.line{
position: absolute;
top: 54px;
width: 62px;
left: 23px;
height: 5px;
background-color: #7360ff;
border-radius: 10px;
transition: all .3s ease-in-out;
}
.container{
width: 600px;
background-color: white;
padding: 20px;
box-shadow: 0 2px 16px rgba(0, 0, 0, .1);
border-radius: 20px;
}
.tab_box .tab_btn{
font-size: 16px;
font-weight: 600;
background: none; /*清除button样式*/
border: none;
padding: 15px;
cursor: pointer;
}
.content_box {
padding: 20px;
}
.content img{
width: 100%;
height: 250px;
}
.content_box .content h2{
margin-bottom: 10px;
}
.content_box .content {
display: none;
animation: moving .5s ease;
}
@keyframes moving {
from {
transform: translateX(50px);
opacity: 0;
}
to {
transform: translateX(0px);
opacity: 1;
}
}
.content_box .content.active {
display: block;
}
.tab_box .tab_btn.active {
color: #7360ff;
}
</style>
</head>
<body>
<div class="container">
<div class="tab_box">
<button class="tab_btn active">首页</button>
<button class="tab_btn">关于</button>
<button class="tab_btn">绿豆沙</button>
<button class="tab_btn">我的</button>
<button class="tab_btn">其他</button>
<div class="line"></div>
</div>
<div class="content_box">
<div class="content active">
<img src="../images/bg.jpg" alt="">
<h2>首页</h2>
<p>
北国风光,千里冰封,万里雪飘。望长城内外,惟余莽莽;大河上下,顿失滔滔。山舞银蛇,原驰蜡象,欲与天公试比高。须晴日,看红装素裹,分外妖娆。江山如此多娇,引无数英雄竞折腰。惜秦皇汉武,略输文采;唐宗宋祖,稍逊风骚。一代天骄,成吉思汗,只识弯弓射大雕。俱往矣,数风流人物,还看今朝。
</p>
</div>
<div class="content">
<img src="../images/bg.jpg" alt="">
<h2>关于</h2>
<p>
北国风光,千里冰封,万里雪飘。望长城内外,惟余莽莽;大河上下,顿失滔滔。山舞银蛇,原驰蜡象,欲与天公试比高。须晴日,看红装素裹,分外妖娆。江山如此多娇,引无数英雄竞折腰。惜秦皇汉武,略输文采;唐宗宋祖,稍逊风骚。一代天骄,成吉思汗,只识弯弓射大雕。俱往矣,数风流人物,还看今朝。
</p>
</div>
<div class="content">
<img src="../images/bg.jpg" alt="">
<h2>绿豆沙</h2>
<p>
北国风光,千里冰封,万里雪飘。望长城内外,惟余莽莽;大河上下,顿失滔滔。山舞银蛇,原驰蜡象,欲与天公试比高。须晴日,看红装素裹,分外妖娆。江山如此多娇,引无数英雄竞折腰。惜秦皇汉武,略输文采;唐宗宋祖,稍逊风骚。一代天骄,成吉思汗,只识弯弓射大雕。俱往矣,数风流人物,还看今朝。
</p>
</div>
<div class="content">
<img src="../images/bg.jpg" alt="">
<h2>我的</h2>
<p>
北国风光,千里冰封,万里雪飘。望长城内外,惟余莽莽;大河上下,顿失滔滔。山舞银蛇,原驰蜡象,欲与天公试比高。须晴日,看红装素裹,分外妖娆。江山如此多娇,引无数英雄竞折腰。惜秦皇汉武,略输文采;唐宗宋祖,稍逊风骚。一代天骄,成吉思汗,只识弯弓射大雕。俱往矣,数风流人物,还看今朝。
</p>
</div>
<div class="content">
<img src="../images/bg.jpg" alt="">
<h2>其他</h2>
<p>
北国风光,千里冰封,万里雪飘。望长城内外,惟余莽莽;大河上下,顿失滔滔。山舞银蛇,原驰蜡象,欲与天公试比高。须晴日,看红装素裹,分外妖娆。江山如此多娇,引无数英雄竞折腰。惜秦皇汉武,略输文采;唐宗宋祖,稍逊风骚。一代天骄,成吉思汗,只识弯弓射大雕。俱往矣,数风流人物,还看今朝。
</p>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$(document).ready(function (){
const $tabs = $('.tab_btn');
const $contents = $('.content');
const $line = $('.line');
$tabs.each(function(index) {
$(this).on('click', function(e) {
// 移除所有 tab 的 active 类
$tabs.removeClass('active');
console.log($(this))
// 当前 tab 添加 active 类
$(this).addClass('active');
// 设置滑块 line 的宽度和位置
$line.css({
width: $(this).outerWidth() + 'px',
left: $(this).position().left + 'px'
});
// 隐藏所有内容
$contents.removeClass('active');
// 当前索引对应的内容添加 active 类
$contents.eq(index).addClass('active');
});
});
})
</script>
</body>
</html>