Django如何实现初始加载指定数量商品 滚动自动加载更多
Django滚动分页加载商品实现方案
整体实现分为后端分页接口改造、前端交互逻辑两部分,改完即可实现初始加载10条、滚动到底自动追加10条的效果。
后端改造(views.py)
用Django自带的Paginator实现分页,区分初始页面请求和ajax滚动请求,分别返回完整页面和商品卡片片段:
from django.core.paginator import Paginator def All(request): page_size = 10 # 获取当前页码,默认加载第1页 page = int(request.GET.get('page', 1)) # 商品必须加固定排序,避免分页数据错乱 all_products = product.objects.all().order_by('-id') paginator = Paginator(all_products, page_size) current_page_products = paginator.page(page) # 滚动触发的ajax请求,直接返回商品卡片HTML片段 if request.headers.get('x-requested-with') == 'XMLHttpRequest': return render(request, 'product_items.html', {'p': current_page_products}) # 初始页面请求,返回带第一页商品的完整页面,同时传总页数给前端 total_pages = paginator.num_pages return render(request, 'home.html', { 'p': current_page_products, 'total_pages': total_pages })
新建templates/product_items.html文件,只存循环渲染的商品卡片代码,供ajax请求返回用:
{% for item in p %} <div class='card'> <img src="{{item.image}}" alt="商品图片"> <p>{{item.description}}</p> <a href="{{item.buy}}" target='_blank' rel='noopener noreferrer'> <button><span class="price">${{item.price}}</span> buy</button> </a> </div> {% endfor %}
前端改造(home.html)
把原来的商品循环部分替换成以下代码,加滚动监听逻辑,自动触发下一页加载:
<div class="grid" id="productGrid"> {% for item in p %} <div class='card'> <img src="{{item.image}}" alt="商品图片"> <p>{{item.description}}</p> <a href="{{item.buy}}" target='_blank' rel='noopener noreferrer'> <button><span class="price">${{item.price}}</span> buy</button> </a> </div> {% endfor %} </div> <!-- 加载状态提示 --> <div id="loadingTip" style="text-align: center; padding: 20px; display: none;">加载中...</div> <script> // 初始化分页参数 let currentPage = 1 const totalPages = {{ total_pages }} let isLoading = false const productGrid = document.getElementById('productGrid') const loadingTip = document.getElementById('loadingTip') // 监听页面滚动 window.addEventListener('scroll', () => { // 已经加载完所有页、或者正在加载中时不触发新请求 if (currentPage >= totalPages || isLoading) return // 距离页面底部200px时提前触发加载,提升体验 const scrollTop = document.documentElement.scrollTop || document.body.scrollTop const windowHeight = window.innerHeight const docTotalHeight = document.documentElement.scrollHeight if (scrollTop + windowHeight >= docTotalHeight - 200) { loadNextPage() } }) // 加载下一页商品 function loadNextPage() { isLoading = true loadingTip.style.display = 'block' currentPage += 1 // 拉取下一页数据 fetch(`/all/?page=${currentPage}`, { headers: { 'X-Requested-With': 'XMLHttpRequest' } }) .then(res => res.text()) .then(htmlFragment => { // 把新的商品卡片追加到容器末尾 productGrid.insertAdjacentHTML('beforeend', htmlFragment) isLoading = false loadingTip.style.display = 'none' // 加载完最后一页时显示提示 if (currentPage >= totalPages) { loadingTip.textContent = '已经到底啦' loadingTip.style.display = 'block' } }) .catch(err => { // 加载失败时回退状态 currentPage -= 1 isLoading = false loadingTip.style.display = 'none' console.error('商品加载失败:', err) }) } </script>
注意事项
- 确认urls.py中All视图对应的路由地址和js里fetch请求的地址一致,路由不是
/all/的话自行替换即可 - 原模板中循环变量和传入的商品列表参数同名均为
p,易引发作用域混淆,统一改为item更稳妥 - 必须加
isLoading状态锁,避免滚动过程中重复触发请求,保证同一时间只有一个加载请求在执行 - 原代码中
<img></img>为错误写法,img是自闭合单标签,无需额外闭合标签,补充alt属性更符合规范 - 原卡片内p标签写了固定
id="id",循环渲染后会出现重复id,不符合HTML规范,已移除
内容的提问来源于stack exchange,提问作者3an
相关产品推荐
相关产品推荐

