本文详解如何使用原生 javascript、html 和 css 构建一个基于 ccbp api 的单车搜索功能,支持输入关键词触发 fetch 请求、显示 bootstrap 加载 spinner,并动态渲染包含城市、id 和名称的单车卡片。
本文详解如何使用原生 javascript、html 和 css 构建一个基于 ccbp api 的单车搜索功能,支持输入关键词触发 fetch 请求、显示 bootstrap 加载 spinner,并动态渲染包含城市、id 和名称的单车卡片。
要实现一个轻量、可复用的城市单车搜索功能,核心在于三部分协同:用户输入控制、异步数据获取与 DOM 动态更新。以下是一个完整、开箱即用的实现方案。
✅ 基础 HTML 结构(含语义化与 Bootstrap 支持)
确保引入 Bootstrap 5 CSS(用于 form-control、btn、spinner-border 等样式),并构建清晰的 UI 层级:
<p class="container mt-4">
<h1 class="mb-4">? 城市单车搜索</h1>
<p class="mb-3">
<label for="searchInput" class="form-label">请输入单车名称</label>
<input
type="text"
class="form-control"
id="searchInput"
placeholder="例如:BikeShare, MetroBike..."
aria-describedby="searchHelp"
>
<p id="searchHelp" class="form-text">支持模糊匹配,留空可获取全部单车列表</p>
</p>
<button type="button" class="btn btn-primary mb-3" onclick="searchBikes()">
? 开始搜索
</button>
<!-- 加载指示器(默认隐藏) -->
<p class="spinner-container text-center mb-4">
<p class="spinner-border text-primary" role="status">
<span class="visually-hidden">加载中...</span>
</p>
</p>
<!-- 搜索结果容器 -->
<p id="search-results"></p>
</p>
? 提示:aria-describedby 和 visually-hidden 提升了无障碍访问体验;mb-* 类确保间距合理。
? 核心 JavaScript 逻辑(健壮 & 可维护)
searchBikes() 函数封装了完整的搜索流程:清空旧结果 → 显示 Spinner → 发起 Fetch → 解析 JSON → 创建并追加卡片 → 错误处理。
立即学习“Java免费学习笔记(深入)”;
function searchBikes() {
const input = document.getElementById('searchInput');
const query = input.value.trim();
const resultsContainer = document.getElementById('search-results');
const spinner = document.querySelector('.spinner-container');
// 清空结果并显示加载状态
resultsContainer.innerHTML = '';
spinner.style.display = 'block';
// 构造请求 URL(支持空查询,即获取全部数据)
const url = query
? `https://apis.ccbp.in/city-bikes?bike-name=${encodeURIComponent(query)}`
: 'https://apis.ccbp.in/city-bikes';
fetch(url)
.then(res => {
if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`);
return res.json();
})
.then(data => {
spinner.style.display = 'none';
if (!Array.isArray(data) || data.length === 0) {
resultsContainer.innerHTML = '<p class="alert alert-info">未找到匹配的单车信息。</p>';
return;
}
data.forEach(bike => {
const card = document.createElement('p');
card.className = 'card shadow-sm mb-2';
card.innerHTML = `
<p class="card-body p-3">
<h6 class="card-title mb-1 text-primary">${bike.city || '未知城市'}</h6>
<p class="card-text mb-1"><small><strong>ID:</strong>${bike.id || '-'}</small></p>
<p class="card-text mb-0"><strong>名称:</strong>${bike.name || 'N/A'}</p>
</p>
`;
resultsContainer.appendChild(card);
});
})
.catch(err => {
spinner.style.display = 'none';
console.error('[Bike Search Error]', err);
resultsContainer.innerHTML = `
<p class="alert alert-danger">
<strong>⚠️ 请求失败</strong>:${err.message}。请检查网络或稍后重试。
</p>
`;
});
}
// 页面加载完成后自动搜索一次(可选:展示示例数据)
document.addEventListener('DOMContentLoaded', () => {
// 可选:首次加载时自动搜索 "bike" 获取示例
// searchBikes();
});
? 补充 CSS 增强体验(无框架依赖)
即使不使用 Bootstrap,也可通过轻量 CSS 保证基础样式一致性:
#search-results {
margin-top: 1.5rem;
}
.spinner-container {
display: none;
}
.card {
border-radius: 8px;
border-left: 4px solid #0d6efd;
}
.card-body h6 {
font-weight: 600;
font-size: 1.05rem;
}
.alert {
border-radius: 6px;
margin-top: 1rem;
}
⚠️ 注意事项与最佳实践
- 安全性:使用 encodeURIComponent(query) 防止 URL 注入,避免特殊字符破坏请求;
- 空值容错:API 返回字段可能缺失(如 city 或 name),务必添加 || ‘N/A’ 或默认值;
- 用户体验:建议为输入框添加 Enter 键提交支持(监听 keydown 事件);
- 性能优化:如需防抖(debounce),可在 searchBikes 调用前封装节流逻辑,避免高频请求;
- API 限制:该 CCBP 接口为教学用途,请勿用于生产环境;实际项目应对接自有后端或带鉴权的开放 API。
通过以上结构化实现,你将获得一个响应迅速、视觉清晰、错误友好且符合现代 Web 标准的单车搜索模块——既可用于学习 Fetch 与 DOM 操作,也具备直接集成到真实项目的潜力。
文章来自机圈观察员网,发布者:,转载请注明出处:https://www.jqgcy.com/shoujipingce/124201.html