使用数组map方法渲染分页时如何正确移除旧DOM元素?
问题根因
- 事件重复绑定:你在循环渲染每个卡片的时候都给next按钮绑定了一次点击事件,同时又单独绑了第二个next事件,点一次next会同时触发几十个回调,逻辑完全混乱
- 删除逻辑错误:第二个监听器里的
card是你刚新建的、还没插入到DOM的空元素,调用remove()根本不会影响页面上已经存在的旧卡片 - 旧卡片清除不彻底:第一个监听器每次只能删除当前循环对应的单个卡片,没法一次性清空所有20条旧卡片
- 代码冗余:创建卡片的逻辑重复写了两遍,后续维护难度高
修复方案
前端代码修改
- 抽离公共的卡片创建方法、旧卡片清除方法
- 分页事件只绑定1次,不要放在循环内绑定
- 单独存储当前页码,不要直接修改接口返回的next参数
修复后完整前端代码如下:
const paginatedApp = { // 单独存储当前页码 currentPage: 1, pageLimit: 20, paginatedPokemon: async function(){ await this.fetchPokemonData() // 只绑定一次分页事件 this.bindPaginationEvents() }, // 抽离接口请求逻辑 fetchPokemonData: async function() { const res = await fetch(`http://localhost:1337/pokemon?page=${this.currentPage}&limit=${this.pageLimit}`) const data = await res.json() // 先清旧卡片 this.clearOldCards() // 再渲染新卡片 this.createCards(data) return data }, // 抽离清除旧卡片逻辑 clearOldCards: function() { const oldCards = document.querySelectorAll('.card') oldCards.forEach(card => card.remove()) }, // 抽离卡片创建逻辑,只写一次 createSingleCard: function(pokeEl, mainContainer) { let card = document.createElement('div'); let imgHolder = document.createElement('div'); let img = document.createElement('img'); let infoHolder = document.createElement('div'); let infoBoxOne = document.createElement('div'); let infoBoxTwo = document.createElement('div'); let pId = document.createElement('p'); let pName = document.createElement('p'); let boxForType = document.createElement('div'); let id = document.createTextNode('id:'); let name = document.createTextNode('name:'); let type = document.createTextNode('type:'); // Info box 1 let infoPId = document.createElement('p'); let infoPName = document.createElement('p'); let infoPType = document.createElement('p'); infoPId.append(id); infoPName.append(name); infoPType.append(type); infoBoxOne.append(infoPId, infoPName, infoPType); infoBoxTwo.append(pId, pName, boxForType); // 加类名 card.classList.add('card'); imgHolder.classList.add('imgHolder'); infoHolder.classList.add('infoHolder'); boxForType.classList.add('boxForType'); img.classList.add('img'); pId.classList.add('id'); pName.classList.add('name'); infoBoxOne.classList.add('infoBoxOne'); infoBoxTwo.classList.add('infoBoxTwo'); img.src = pokeEl.sprites.other['official-artwork']['front_default']; pId.append(`#${pokeEl.id}`); pName.append(pokeEl.name); // 处理属性 pokeEl.types.map(pokeType => { let pokemonType = pokeType.type.name let pokes = document.createElement('p'); pokes.append(pokemonType) boxForType.append(pokes); }) imgHolder.appendChild(img); card.appendChild(imgHolder) infoHolder.append(infoBoxOne, infoBoxTwo) card.appendChild(infoHolder); mainContainer.appendChild(card); boxForType.children[0].style.border = '1px solid #fff'; if(boxForType.children[1]) { boxForType.children[1].style.border = '1px solid #fff' } }, createCards: function(pokemonObj){ const main = document.querySelector('main'); pokemonObj.result.forEach(pokeEl => { this.createSingleCard(pokeEl, main) }) }, // 分页事件只绑定一次 bindPaginationEvents: function() { const next = document.querySelector('.next'); const previous = document.querySelector('.previous'); next.addEventListener('click', async () => { this.currentPage++ await this.fetchPokemonData() }) previous.addEventListener('click', async () => { if(this.currentPage <= 1) return this.currentPage-- await this.fetchPokemonData() }) } } paginatedApp.paginatedPokemon();
后端代码优化建议
当前后端每次收到请求都会全量拉取251条宝可梦数据再切页,每次请求都会调用200+次第三方API,响应速度慢还容易触发限流。可以把拉取到的全量宝可梦数据做本地缓存,有效期设置几小时即可,不用每次请求都重新拉取。
内容的提问来源于stack exchange,提问作者LJS100
相关产品推荐
相关产品推荐

