为啥我的点击事件在生成 HTML 时只工作一次,但在 console.logging 时工作正常?

Posted

技术标签:

【中文标题】为啥我的点击事件在生成 HTML 时只工作一次,但在 console.logging 时工作正常?【英文标题】:Why does my click event only work once when generating HTML, but works correctly when console.logging?为什么我的点击事件在生成 HTML 时只工作一次,但在 console.logging 时工作正常? 【发布时间】:2020-05-13 05:43:26 【问题描述】:

它在控制台中以及在生成 html 时按照我打算的方式运行,但它只适用于 HTML 一次,而它始终适用于控制台。我是否需要在这里使用循环,并以这种方式返回 HTML?我试过地图,但它不是一个数组。编辑:忘记了 HTML,只是添加了它。

<!DOCTYPE html>
<html>
<head>
<title>Pokedex</title>
<link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
<h1 id="main-header">Pokedex</h1>
<div id="main-container">

</div>
<script src="index.js" type="text/javascript"></script>
</body>
</html>

Javascript

const container = document.getElementById('main-container');

function getPokemon(callback) 
const xhr = new XMLHttpRequest();
const url = 'https://pokeapi.co/api/v2/pokemon/';

xhr.onload = function() 
    if(xhr.status === 200) 
        const pokemon = JSON.parse(xhr.responseText);
        container.innerHTML+=
        pokemon.results.map((poke, index)=> 
            return `
                <div class='cardFront'>
                    <img src='https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/$(index + 1).toString().png'></img>
                    <h4>$poke.name</h4>
                </div>
            `
        ).join('');

        callback();
    

xhr.open('GET', url, true);
xhr.send();


function cardBack() 
const endPoint = this.lastChild.previousSibling.innerText;
const xhr = new XMLHttpRequest();

xhr.onload = function() 
    if(xhr.status === 200) 
        const details = JSON.parse(xhr.responseText); 
        container.innerHTML+= `
            <div class='backSide'>
                <h4>$details.name</h4>
                <h4>$details.types[0].type.name</h4>
            </div>
        `
    

xhr.open('GET', 'https://pokeapi.co/api/v2/pokemon/' + endPoint, true);
xhr.send();


getPokemon(()=>
const cardFront = document.querySelectorAll('.cardFront');
cardFront.forEach((card)=> 
    card.addEventListener('click', cardBack);
)
);

【问题讨论】:

你的html在哪里? 是动态按钮,你要点击哪里? @User1899289003 这是一张口袋妖怪卡。我已经渲染了前端,然后对不同的端点进行 API 调用,以获取后端的数据。当点击正确的信息时,我只是试图让背面呈现。 【参考方案1】:

在您的点击监听器的回调函数中,您正在使用它的 .innerHTML 属性将新的 html 代码添加到您的主容器 &lt;div&gt; 元素。 这将删除所有现有的事件侦听器 - 因此 click 只会触发一次。

虽然我建议使用创建一个新的&lt;div&gt;

var newDiv = document.createElement("DIV");

并将其附加到主容器

document.getElementById('main-container').appendChild(newDiv);

在您的情况下,更简单的解决方案是替换

container.innerHTML+= `
    <div class='backSide'>
        <h4>$details.name</h4>
        <h4>$details.types[0].type.name</h4>
    </div>
`

通过

container.insertAdjacentHTML("afterend", `
    <div class='backSide'>
        <h4>$details.name</h4>
        <h4>$details.types[0].type.name</h4>
    </div>
`);

【讨论】:

【参考方案2】:

我创建了一个似乎符合所有标准的版本。卡片背面信息的请求只有在卡片被点击一次后才能再次点击卡片返回到前面。我通过在同一个 div 中构建每张卡片的正面和背面并在两侧之间切换一个“隐藏”类来做到这一点。

我通常会使用 fetch() 和 Promise,但使用 xhr 和回调保留您的原始代码。

CodePen 在这里:https://codepen.io/edlucas/pen/xxbepjM

JS

const container = document.getElementById('main-container');
const pokeNames = [];
let loading = false;

function getPokemon(callback) 

    const xhr = new XMLHttpRequest();
    const url = 'https://pokeapi.co/api/v2/pokemon/';

    xhr.onload = function () 
        if (xhr.status === 200) 
            const pokemon = JSON.parse(xhr.responseText);
            container.innerHTML +=
                pokemon.results.map((poke, index) => 
                    const pokeName = poke.name.replace(/\s/g, "");
                    pokeNames.push(poke.name);
                    return `
                                    <div id="poke-$pokeName" class="poke-card">
                                    <div class='cardFront'>
                                            <img src='https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/$(index + 1).toString().png'></img>
                                            <h4>$pokeName</h4>
                                    </div>
                                    </div>
                            `
                ).join('');

            callback();
        
    
    xhr.open('GET', url, true);
    xhr.send();


function addCardBack(pokeName, callback) 
    const xhr = new XMLHttpRequest();

    xhr.onload = function () 
        if (xhr.status === 200) 
            const details = JSON.parse(xhr.responseText);
            const pokeContainer = document.getElementById(`poke-$pokeName`);
            pokeContainer.innerHTML += `
                            <div class='cardBack'>
                                    <h4>$details.name</h4>
                                    <h4>$details.types[0].type.name</h4>
                            </div>
                    `;
            callback(pokeName);
        
        loading = false;
    
    loading = true;
    xhr.open('GET', 'https://pokeapi.co/api/v2/pokemon/' + pokeName, true);
    xhr.send();


function toggleCard(pokeName) 
    const cardBack = document.querySelectorAll(`#poke-$pokeName .cardBack`)[0];

    if (!cardBack) 
        // Prevent a second click from calling this function again before data is loaded
        if (!loading) 
            addCardBack(pokeName, (pokeName) => 
                swapCards(pokeName);
            );
        
     else 
        swapCards(pokeName);
    


function swapCards(pokeName) 
    const cardFront = document.querySelectorAll(`#poke-$pokeName .cardFront`)[0];
    const cardBack = document.querySelectorAll(`#poke-$pokeName .cardBack`)[0];

    if (cardFront.classList.contains('hidden')) 
        cardFront.classList.remove('hidden');
        cardBack.classList.add('hidden');
     else 
        cardFront.classList.add('hidden');
        cardBack.classList.remove('hidden');
    


getPokemon(() => 
    pokeNames.forEach((pokeName) => 
        const card = document.getElementById(`poke-$pokeName`);
        card.addEventListener('click', () => toggleCard(pokeName));
    )
);

CSS

.hidden 
    display: none;


.poke-card 
    width: 100px;
    height: 150px;
    padding: 10px;
    margin-bottom: 20px;
    border: 1px solid blue;

【讨论】:

以上是关于为啥我的点击事件在生成 HTML 时只工作一次,但在 console.logging 时工作正常?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的C#Winform自定义控件,继承Control类,然后重写onpaint事件,设计窗体拖动时只剩下外框!

如何让 HTML5 音频在点击时只播放一次?

为啥我在引导模式中的点击事件触发不止一个

为啥UITableView回到前面时只显示一半的数据

Videojs requestPictureInPicture() 在滚动到顶部和底部时只工作一次

点击事件上的 JQuery 只工作一次