本文将介绍10个常用的JavaScript网页特效实现,包括轮播图、图片懒加载、无限滚动等,每个案例都提供完整代码和详细讲解。
一、响应式导航栏
1.1 效果展示
- 桌面端显示完整导航
- 移动端显示汉堡菜单
- 平滑的动画过渡
<!-- HTML结构 -->
<nav class="navbar">
<div class="brand">Logo</div>
<button class="toggle-btn">
<span></span>
<span></span>
<span></span>
</button>
<ul class="nav-links">
<li><a href="#home">首页</a></li>
<li><a href="#about">关于</a></li>
<li><a href="#services">服务</a></li>
<li><a href="#contact">联系</a></li>
</ul>
</nav>
/* CSS样式 */
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
background: #fff;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.toggle-btn {
display: none;
}
.nav-links {
display: flex;
gap: 2rem;
}
@media (max-width: 768px) {
.toggle-btn {
display: block;
}
.nav-links {
display: none;
position: absolute;
top: 100%;
left: 0;
width: 100%;
background: #fff;
}
.nav-links.active {
display: flex;
flex-direction: column;
}
}
// JavaScript实现
const toggleBtn = document.querySelector('.toggle-btn');
const navLinks = document.querySelector('.nav-links');
toggleBtn.addEventListener('click', () => {
navLinks.classList.toggle('active');
});
// 点击外部关闭菜单
document.addEventListener('click', (e) => {
if (!e.target.closest('.navbar')) {
navLinks.classList.remove('active');
}
});
二、图片懒加载
2.1 使用Intersection Observer
<img class="lazy"
data-src="image.jpg"
src="placeholder.jpg"
alt="懒加载图片">
// 懒加载实现
function lazyLoad() {
const images = document.querySelectorAll('img.lazy');
const imageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.remove('lazy');
observer.unobserve(img);
}
});
});
images.forEach(img => imageObserver.observe(img));
}
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', lazyLoad);
三、轮播图
3.1 自动轮播+手动控制
<div class="carousel">
<div class="carousel-container">
<div class="carousel-slide">
<img src="slide1.jpg" alt="Slide 1">
</div>
<div class="carousel-slide">
<img src="slide2.jpg" alt="Slide 2">
</div>
<div class="carousel-slide">
<img src="slide3.jpg" alt="Slide 3">
</div>
</div>
<button class="carousel-btn prev"><</button>
<button class="carousel-btn next">></button>
<div class="carousel-dots"></div>
</div>
class Carousel {
constructor(element) {
this.carousel = element;
this.slides = element.querySelectorAll('.carousel-slide');
this.currentIndex = 0;
this.interval = null;
this.initButtons();
this.initDots();
this.startAutoPlay();
}
initButtons() {
const prevBtn = this.carousel.querySelector('.prev');
const nextBtn = this.carousel.querySelector('.next');
prevBtn.addEventListener('click', () => this.slide('prev'));
nextBtn.addEventListener('click', () => this.slide('next'));
}
initDots() {
const dotsContainer = this.carousel.querySelector('.carousel-dots');
this.slides.forEach((_, index) => {
const dot = document.createElement('span');
dot.addEventListener('click', () => this.goToSlide(index));
dotsContainer.appendChild(dot);
});
this.updateDots();
}
slide(direction) {
if (direction === 'next') {
this.currentIndex = (this.currentIndex + 1) % this.slides.length;
} else {
this.currentIndex = (this.currentIndex - 1 + this.slides.length) % this.slides.length;
}
this.updateSlides();
this.updateDots();
}
goToSlide(index) {
this.currentIndex = index;
this.updateSlides();
this.updateDots();
}
updateSlides() {
const offset = -this.currentIndex * 100;
this.carousel.querySelector('.carousel-container').style.transform = `translateX(${offset}%)`;
}
updateDots() {
const dots = this.carousel.querySelectorAll('.carousel-dots span');
dots.forEach((dot, index) => {
dot.classList.toggle('active', index === this.currentIndex);
});
}
startAutoPlay() {
this.interval = setInterval(() => this.slide('next'), 3000);
this.carousel.addEventListener('mouseenter', () => {
clearInterval(this.interval);
});
this.carousel.addEventListener('mouseleave', () => {
this.startAutoPlay();
});
}
}
// 初始化轮播图
new Carousel(document.querySelector('.carousel'));
四、无限滚动
4.1 实现无限加载列表
class InfiniteScroll {
constructor(container, loadMore) {
this.container = container;
this.loadMore = loadMore;
this.page = 1;
this.loading = false;
this.init();
}
init() {
const observer = new IntersectionObserver(
entries => {
if (entries[0].isIntersecting && !this.loading) {
this.loadMore();
}
},
{ threshold: 0.1 }
);
observer.observe(this.container.querySelector('.loading'));
}
async loadMore() {
this.loading = true;
try {
const data = await this.fetchData(this.page);
this.appendItems(data);
this.page++;
} catch (error) {
console.error('加载失败:', error);
}
this.loading = false;
}
async fetchData(page) {
const response = await fetch(`/api/items?page=${page}`);
return response.json();
}
appendItems(items) {
items.forEach(item => {
const element = document.createElement('div');
element.className = 'item';
element.textContent = item.title;
this.container.insertBefore(element, this.container.lastElementChild);
});
}
}
五、拖拽排序
5.1 可拖拽列表
<ul class="sortable-list">
<li draggable="true">项目 1</li>
<li draggable="true">项目 2</li>
<li draggable="true">项目 3</li>
</ul>
class SortableList {
constructor(element) {
this.list = element;
this.items = element.querySelectorAll('li');
this.draggedItem = null;
this.init();
}
init() {
this.items.forEach(item => {
item.addEventListener('dragstart', e => {
this.draggedItem = item;
e.dataTransfer.effectAllowed = 'move';
item.classList.add('dragging');
});
item.addEventListener('dragend', () => {
this.draggedItem.classList.remove('dragging');
this.draggedItem = null;
});
item.addEventListener('dragover', e => {
e.preventDefault();
if (item !== this.draggedItem) {
const rect = item.getBoundingClientRect();
const y = e.clientY - rect.top;
if (y < rect.height / 2) {
item.parentNode.insertBefore(this.draggedItem, item);
} else {
item.parentNode.insertBefore(this.draggedItem, item.nextSibling);
}
}
});
});
}
}
六、表单验证
6.1 实时验证表单
<form class="validated-form">
<div class="form-group">
<input type="text" name="username" required
pattern="[A-Za-z0-9]{3,}"
data-error="用户名至少3个字符">
</div>
<div class="form-group">
<input type="email" name="email" required
data-error="请输入有效的邮箱地址">
</div>
<div class="form-group">
<input type="password" name="password" required
pattern=".{6,}"
data-error="密码至少6个字符">
</div>
<button type="submit">提交</button>
</form>
class FormValidator {
constructor(form) {
this.form = form;
this.inputs = form.querySelectorAll('input');
this.init();
}
init() {
this.inputs.forEach(input => {
input.addEventListener('input', () => this.validateInput(input));
input.addEventListener('blur', () => this.validateInput(input));
});
this.form.addEventListener('submit', e => {
e.preventDefault();
if (this.validateAll()) {
this.submitForm();
}
});
}
validateInput(input) {
const error = input.dataset.error;
const valid = input.checkValidity();
input.classList.toggle('invalid', !valid);
this.showError(input, valid ? '' : error);
}
validateAll() {
let valid = true;
this.inputs.forEach(input => {
if (!input.checkValidity()) {
valid = false;
this.validateInput(input);
}
});
return valid;
}
showError(input, message) {
const errorDiv = input.parentNode.querySelector('.error-message')
|| document.createElement('div');
errorDiv.className = 'error-message';
errorDiv.textContent = message;
if (!input.parentNode.contains(errorDiv)) {
input.parentNode.appendChild(errorDiv);
}
}
async submitForm() {
const data = new FormData(this.form);
try {
const response = await fetch('/api/submit', {
method: 'POST',
body: data
});
if (response.ok) {
alert('提交成功!');
this.form.reset();
}
} catch (error) {
alert('提交失败,请重试');
}
}
}
七、总结
本文介绍的特效和功能都是实际开发中常用的,建议:
- 理解代码原理,不要简单复制
- 根据实际需求进行修改和优化
- 注意性能和用户体验
- 考虑浏览器兼容性
如果你在实现这些特效时遇到问题,欢迎在评论区讨论!