You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用数组map方法渲染分页时如何正确移除旧DOM元素?

问题根因
  • 事件重复绑定:你在循环渲染每个卡片的时候都给next按钮绑定了一次点击事件,同时又单独绑了第二个next事件,点一次next会同时触发几十个回调,逻辑完全混乱
  • 删除逻辑错误:第二个监听器里的card是你刚新建的、还没插入到DOM的空元素,调用remove()根本不会影响页面上已经存在的旧卡片
  • 旧卡片清除不彻底:第一个监听器每次只能删除当前循环对应的单个卡片,没法一次性清空所有20条旧卡片
  • 代码冗余:创建卡片的逻辑重复写了两遍,后续维护难度高
修复方案

前端代码修改

  1. 抽离公共的卡片创建方法、旧卡片清除方法
  2. 分页事件只绑定1次,不要放在循环内绑定
  3. 单独存储当前页码,不要直接修改接口返回的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 02:12:02