从远程加载HTML片段的常见需求
在开发网页应用时,经常会遇到需要动态加载部分内容的场景。比如一个博客列表页,点击某篇文章标题后,只更新文章区域而不刷新整个页面。这时候,使用 Fetch API 获取 HTML 片段并插入到指定容器中,是一种轻量又高效的实现方式。
相比传统的整个页面跳转,这种方式能让用户感觉更流畅,类似原生 App 的体验。尤其是在单页应用(SPA)还不适合的中小型项目里,这种“局部刷新”显得尤为实用。
基本实现流程
Fetch API 是现代浏览器提供的原生网络请求接口,替代了老旧的 XMLHttpRequest。它基于 Promise,语法更清晰。要获取一段 HTML 并渲染,只需要发起请求、读取响应文本,然后赋值给元素的 innerHTML 即可。
fetch('/partials/article.html')
.then(response => response.text())
.then(html => {
document.getElementById('content-area').innerHTML = html;
})
.catch(err => {
console.error('加载失败:', err);
});上面这段代码会从 /partials/article.html 获取 HTML 内容,并将其插入 id 为 content-area 的元素中。整个过程异步进行,不会阻塞页面其他操作。
处理相对路径和资源引用问题
直接插入 HTML 片段时,如果其中包含 img、link 或 script 标签,它们的相对路径是相对于当前页面的,而不是源文件的位置。例如,article.html 中有 <img src="./images/cover.jpg">,而 article.html 放在 /partials/ 目录下,图片实际路径应为 /partials/images/cover.jpg。但页面在根目录加载时,浏览器会尝试请求 /images/cover.jpg,导致 404。
解决办法之一是在服务器端统一规划静态资源路径,比如把所有图片放在 /assets/ 下。另一个方法是,在插入前对 HTML 字符串做简单替换:
fetch('/partials/article.html')
.then(response => response.text())
.then(html => {
const fixedHtml = html.replace(/src="\.\//g, 'src="/partials/');
document.getElementById('content-area').innerHTML = fixedHtml;
});事件绑定与脚本执行
通过 innerHTML 插入的 script 标签默认不会执行。如果片段中包含需要运行的 JS 代码,比如初始化轮播图或表单验证,就得手动处理。
一种做法是提取 script 内容并动态创建 script 元素:
function executeScripts(container) {
const scripts = container.querySelectorAll('script');
scripts.forEach(script => {
const newScript = document.createElement('script');
newScript.textContent = script.innerHTML;
script.parentNode.replaceChild(newScript, script);
});
}
fetch('/partials/dynamic-form.html')
.then(r => r.text())
.then(html => {
const tempDiv = document.createElement('div');
tempDiv.innerHTML = html;
executeScripts(tempDiv);
document.getElementById('content-area').appendChild(tempDiv);
});这样可以确保内联脚本正常运行。对于外部脚本,也可以用类似方式动态加载。
提升用户体验的小细节
在内容加载期间,用户看到的可能是空白区域。加个简单的 loading 提示会好很多:
const container = document.getElementById('content-area');
container.innerHTML = '<div class="loading">加载中...</div>';
fetch('/partials/content.html')
.then(r => r.text())
.then(html => {
container.innerHTML = html;
});再配合一点 CSS 过渡效果,比如淡入淡出,整个过程看起来就更自然了。