如何整合事件监听器?

How do I consolidate eventlisteners?

我有一个 API 应用程序,它会列出点击模式弹出的名称并显示数据。

还有一个按钮连接到具有更多数据的第二个模式。

问题是第二个模式遍历了所有数据,而不是只显示所选名称的正确数据。有人告诉我原因是因为我有两个事件侦听器,但我应该只有 1 个。

这是我的代码笔,这是我被告知问题所在的地方。

https://codepen.io/drxl/pen/WNjJQXa

function addListItem(pokemon) {
  let pokeUl = document.querySelector('.list-group');
  let listItem = document.createElement('li');
  let button = document.createElement('button');
  let baseStatsButton = document.querySelector('#base-stats-button');
  button.innerText = pokemon.name;
  button.classList.add('btn');
  button.classList.add('btn-primary');
  listItem.classList.add('group-list-item');
  button.setAttribute("data-target", "#my-modal");
  button.setAttribute("data-toggle", "modal");
  listItem.appendChild(button);
  pokeUl.appendChild(listItem);
  button.addEventListener('click', function() {
    showDetails(pokemon);
  });
  baseStatsButton.addEventListener('click', function() {
    showStatModal(pokemon);
  });
}

现在,当您添加 pokemon 时,您每次都将相同的点击事件侦听器添加到统计按钮。这最终会在单击时执行事件处理程序 number-of-pokemon 次。只有一个按钮,所以只需要一个事件监听器

我们能做的就是在函数外建立一个全局的currentPokemon。然后当你点击一个口袋妖怪按钮时,你保存口袋妖怪,当你点击基本统计时,显示当前口袋妖怪的基本统计

所以让我们从 add 函数中移出一些常量并保存点击的 pokemon

const pokeUl = document.querySelector('.list-group');
const baseStatsButton = document.querySelector('#base-stats-button');
let currentPokemon; // define a reusable variable
function addListItem(pokemon) {
  let listItem = document.createElement('li');
  let button = document.createElement('button');
  button.innerText = pokemon.name;
  button.classList.add('btn');
  button.classList.add('btn-primary');
  listItem.classList.add('group-list-item');
  button.setAttribute("data-target", "#my-modal");
  button.setAttribute("data-toggle", "modal");
  listItem.appendChild(button);
  pokeUl.appendChild(listItem);
  button.addEventListener('click', function () {
     showDetails(pokemon);
     currentPokemon = pokemon; // save
  });
};

然后在统计中使用它

   function showStatModal() {
      if (!currentPokemon) return; // for some reason we have invoked the modal before clicking
      const item = currentPokemon;

let pokemonRepository = (function () {
   let pokemonList = [];
   let apiUrl = 'https://pokeapi.co/api/v2/pokemon/?limit=150';
   let searchInput = document.querySelector("#searchIn");

   function add(pokemon) {
      pokemonList.push(pokemon);
   }

   function getAll() {
      return pokemonList;
   }

   const pokeUl = document.querySelector('.list-group');
   const baseStatsButton = document.querySelector('#base-stats-button');
   let currentPokemon;
   function addListItem(pokemon) {
      let listItem = document.createElement('li');
      let button = document.createElement('button');
      button.innerText = pokemon.name;
      button.classList.add('btn');
      button.classList.add('btn-primary');
      listItem.classList.add('group-list-item');
      button.setAttribute("data-target", "#my-modal");
      button.setAttribute("data-toggle", "modal");
      listItem.appendChild(button);
      pokeUl.appendChild(listItem);
      button.addEventListener('click', function () {
         showDetails(pokemon);
         currentPokemon = pokemon; 
      });
   }

   baseStatsButton.addEventListener('click', showStatModal);

   function loadList() {
      return fetch(apiUrl).then(function (response) {
         return response.json();
      }).then(function (json) {
         json.results.forEach(function (item) {
            let pokemon = {
               name: item.name,
               detailsUrl: item.url
            };
            add(pokemon);
         });
      }).catch(function (e) {
         console.error(e);
      })
   }

   function loadDetails(item) {
      let url = item.detailsUrl;
      return fetch(url).then(function (response) {
         return response.json();
      }).then(function (details) {
         //Add details to item
         item.imageUrl = details.sprites.front_default;
         item.imageUrlBack = details.sprites.back_default;
         item.height = details.height / 10;
         item.weight = details.weight / 10;
         // pokemon types
         item.types = [];
         for (var i = 0; i < details.types.length; i++) {
            item.types.push(details.types[i].type.name);
         }
         item.types = item.types.join(',  ');
         //pokemon abilities
         item.abilities = [];
         // eslint-disable-next-line no-redeclare
         for (var i = 0; i < details.abilities.length; i++) {
            item.abilities.push(details.abilities[i].ability.name);
         }
         item.abilities = item.abilities.join(',  ');

      }).catch(function (e) {
         console.error(e);
      });
   }

   //loads the stats for 2nd modal

   function loadStats(item) {
      let url = item.detailsUrl;
      return fetch(url).then(function (response) {
         return response.json();
      }).then(function (details) {
         //add details to stats
         item.stats = details.stats.map(({ base_stat, stat: { name } }) =>
            `${name}: ${base_stat}`).join("<br/>")


      }).catch(function (e) {
         console.error(e);
      });
   }


   function showDetails(item) {
      pokemonRepository.loadDetails(item).then(function () {
         // console.log("item:", item);
         showModal(item);
      });
   }

   function showModal(item) {
      pokemonRepository.loadDetails(item).then(function () {
         // eslint-disable-next-line no-undef
         let modalBody = $(".modal-body");
         // eslint-disable-next-line no-undef
         let modalTitle = $(".modal-title");

         //clears previous content in modal
         modalTitle.empty();
         modalBody.empty();

         //create elenebtb for pokemon name
         // eslint-disable-next-line no-undef
         let nameElement = $("<h1>" + item.name + "</h1>");

         //create img element
         // eslint-disable-next-line no-undef
         let imageElementFront = $('<img class="modal-img" style="width:50%">');
         imageElementFront.attr("src", item.imageUrl);
         // eslint-disable-next-line no-undef
         let imageElementBack = $('<img class="modal-img" style="width:50%">');
         imageElementBack.attr("src", item.imageUrlBack);

         //create element for pokemon height 
         // eslint-disable-next-line no-undef
         let heightElement = $("<p>" + "Height: " + item.height + "m</p>");

         //for pokemon weight
         let weightElement = $("<p>" + "Weight: " + item.weight + "kgs</p>");
         //pokemon types
         // eslint-disable-next-line no-undef
         let typesElement = $("<p>" + "Types: " + item.types + "</p>");

         //pokemon abilities 
         // eslint-disable-next-line no-undef
         let typesAbilities = $("<p>" + "Abilities: " + item.abilities + "</p>");


         //eventlistener to for search  bar
         searchInput.addEventListener('input', function () {
            let listPokemon = document.querySelectorAll('.group-list-item');
            let value = searchInput.value.toUpperCase();

            listPokemon.forEach(function (pokemon) {
               if (pokemon.innerText.toUpperCase().indexOf(value) > -1) {
                  pokemon.style.display = '';
               } else {
                  pokemon.style.display = 'none'
               }
            })
         });

         modalTitle.append(nameElement);
         modalBody.append(imageElementFront);
         modalBody.append(imageElementBack);
         modalBody.append(heightElement);
         modalBody.append(weightElement);
         modalBody.append(typesElement);
         modalBody.append(typesAbilities);


         // eslint-disable-next-line no-undef
         $('#my-modal').modal('toggle');
      });
   }



   function loadStatDetails(item) {
      pokemonRepository.loadStats(item).then(function () {
         showStatModal(item);
      });
   }
   function showStatModal() {
      if (!currentPokemon) return; // for some reason we have invoked the modal before clicking
      const item = currentPokemon;
      pokemonRepository.loadStats(item).then(function () {
         // eslint-disable-next-line no-undef
         let StatmodalBody = $(".Statmodal-body");
         // eslint-disable-next-line no-undef
         let StatmodalTitle = $(".Statmodal-title");

         //clears previous content in modal
         StatmodalTitle.empty();
         StatmodalBody.empty();

         //create elenebtb for pokemon name
         // eslint-disable-next-line no-undef
         let nameElement = $("<h1>" + item.name + "</h1>");

         //add stats
         let statsElement = $("<p>" + item.stats + "<p>");

         StatmodalTitle.append(nameElement);
         StatmodalBody.append(statsElement);

         $('#my-Statmodal').modal('show');

      });
   }


   return {
      add: add,
      getAll: getAll,
      addListItem: addListItem,
      loadList: loadList,
      loadDetails: loadDetails,
      showDetails: showDetails,
      loadStats: loadStats,
      loadStatDetails: loadStatDetails,
   };

})();

pokemonRepository.loadList().then(function () {

   pokemonRepository.getAll().forEach(function (pokemon) {
      pokemonRepository.addListItem(pokemon);
   });
});

let link = document.getElementById("back-to-top");
var amountScrolled = 250;

//makes button show
window.addEventListener('scroll', function (e) {
   if (this.window.pageYOffset > amountScrolled) {
      link.classList.add('show');
   } else {
      link.className = 'back-to-top';
   }
});

//scrolls to top
link.addEventListener('click', function (e) {
   e.preventDefault();

   var distance = 0 - window.pageYOffset;
   var increments = distance / (500 / 16);
   function animateScroll() {
      window.scrollBy(0, increments);
      if (window.pageYOffset <= document.body.offsetTop) {
         clearInterval(runAnimation);
      }
   };
   // Loop the animation function
   var runAnimation = setInterval(animateScroll, 16);
});
/*
* Prefixed by https://autoprefixer.github.io
* PostCSS: v7.0.29,
* Autoprefixer: v9.7.6
* Browsers: last 4 version
*/

* {
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
}

body {
  background-color: rgb(3, 136, 180);
}

.navbar {
  background-color: #ffcb05!important;
}

.navbar-brand {
  color: #3d7dca!important;
}

.navbar-logo {
  width: 42px;
  height: 42px;
  margin-left: 1rem;
}

.modal-header {
  border-bottom: 1px solid black;
  -webkit-box-pack: justify;
  -ms-flex-pack: justify;
  justify-content: space-between;
}

.modal-content {
  background-color: #ffcb05!important;
  border: #3d7dca solid 6px!important;
  text-align: center;
}

.modal-close {
  background-color: #ee1515;
  border: white solid 2px;
  color: white;
  padding: 8.5px 16.5px;
  border-radius: 50%;
  font-size: 1.25rem;
}

.modal-close:active {
  border-color: #ee1515;
  background-color: white;
  color: #ee1515;
}

.modal-title h1 {
  margin-bottom: 0;
}

.modal h1 {
  margin-top: 0;
  text-align: left;
  font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
  text-transform: uppercase;
  font-size: 3rem;
  color: #3d7dca;
  margin-left: 2rem;
}

.modal p {
  margin-bottom: 0;
  text-align: left;
  font-weight: 700;
  font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
  font-size: 1.5rem;
  color: #3d7dca;
  text-transform: capitalize;
}

.list-group {
  list-style-type: none;
  text-align: center;
  -webkit-padding-start: 0;
  padding-inline-start: 0;
  margin: 2rem;
  text-transform: capitalize;
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-orient: horizontal;
  -webkit-box-direction: normal;
  -ms-flex-direction: row;
  flex-direction: row;
  -ms-flex-wrap: wrap;
  flex-wrap: wrap;
  -webkit-box-pack: center;
  -ms-flex-pack: center;
  justify-content: center;
}

.list-group li {
  width: auto;
  margin: 1rem;
}

.Statmodal-body p {
  margin-left: 3%;
}

.btn {
  /*  background-color:   #cc1313;*/
  background-color: #db0606;
  border: black solid 2px;
  color: white;
  text-transform: capitalize;
  padding: 24px 72px;
  width: 300px;
}

.btn:hover {
  background-color: white;
  border: black solid 2px;
  color: #ee1515;
}

.btn-outline-success:hover {
  background-color: white;
  color: #ee1515;
  border: black 2px solid;
}

.btn-primary {
  font-size: 1.5rem;
}

.btn-primary:hover {
  color: #ee1515;
  background-color: white;
}

#search-button {
  background-color: #db0606;
  border: black solid 2px;
  color: white;
  padding: 4px 25px;
}

.modal-footer {
  justify-content: center;
  border-top: none;
}

.modal-button {
  text-align: center;
  width: auto;
}

.back-to-top {
  background-color: #ee1515;
  color: #FFFFFF;
  opacity: 0;
  transition: opacity .6s ease-in-out;
  z-index: 999;
  position: fixed;
  right: 20px;
  bottom: 20px;
  width: 50px;
  height: 50px;
  box-sizing: border-box;
  border-radius: 0%;
}

a.back-to-top {
  font-weight: 1000;
  letter-spacing: 2px;
  font-size: 14px;
  text-transform: uppercase;
  text-align: center;
  line-height: 1.6;
  padding-left: 2px;
  padding-top: 14px;
  text-decoration: none;
}

.back-to-top:hover,
.back-to-top:focus,
.back-to-top:visited {
  color: #FFFFFF;
}

.back-to-top.show {
  opacity: 1;
}

@media screen and (max-width: 727px) {
  .btn {
    width: 250px;
  }
}

@media screen and (max-width:627px) {
  .btn {
    padding: 10px 10px;
    width: 200px;
    font-size: 1.15rem;
  }
}

@media screen and (max-width:575px) {
  .justify-content-between {
    -webkit-box-pack: center!important;
    -ms-flex-pack: center!important;
    justify-content: center!important;
  }
  .form-inline {
    -webkit-box-pack: center;
    -ms-flex-pack: center;
    justify-content: center;
  }
  #search-button {
    margin-top: .5rem;
    padding: 4px 42px;
  }
  .modal p {
    font-size: 1.5rem!important;
  }
}

@media screen and (max-width:500px) {
  .modal p {
    font-size: 1.5rem!important;
  }
}

@media screen and (max-width: 493px) {
  .justify-content-between {
    -webkit-box-pack: center!important;
    -ms-flex-pack: center!important;
    justify-content: center!important;
  }
}

@media screen and (max-width:450px) {
  .modal-header {
    -webkit-box-align: center!important;
    -ms-flex-align: center!important;
    align-items: center!important;
  }
  .modal-title h1 {
    font-size: 1.75rem;
  }
  button {
    font-size: .85rem;
  }
  .modal p {
    font-size: 1rem!important;
  }
}
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>PokemonAPI</title>
  <link href="img/Poke_Ball.png" rel="shortcut icon" type="image/x-icon" />
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.3/css/all.css" integrity="sha384-SZXxX4whJ79/gErwcOYf+zWLeJdY/qpuqC4cAa9rOGUstPomtqpuNWT9wdPEn2fk" crossorigin="anonymous">
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
  <link href="css/styles.css" rel="stylesheet">
</head>

<body>

  <!--Nav-->
  <nav class="navbar navbar-light bg-light justify-content-between">
    <a class="navbar-brand">PokemonAPI<img class="navbar-logo" src="img/Poke_Ball.png"></a>
    <form class="form-inline">
      <input id="searchIn" class="form-control mr-sm-2" type="search" placeholder="Search for a Pokemon" aria-label="Search for Pokemon">
      <button id="search-button" type="submit">Search</button>
    </form>
  </nav>

  <!--list of pokemon-->
  <ul class="list-group"></ul>



  <div class="modal fade" id="my-modal" aria-hidden="true" tabindex="-1" role="dialog" aria-labelledby="pokemonModalLabel">
    <div class="modal-dialog modal-dialog-centered" role="document">
      <div class="modal-content">
        <div class="modal-header">
          <h5 class="modal-title" aria-labelledby="pokemonModalLabel">Modal 1 title</h5>
          <button type="button" class="modal-close" data-bs-dismiss="modal" aria-label="Close">X</button>
        </div>
        <div class="modal-body">
        </div>
        <div class="modal-footer">
          <button id="base-stats-button" class="btn modal-button" data-bs-target="#my-Statmodal" data-bs-toggle="modal" data-bs-dismiss="modal">Base Stats</button>
        </div>
      </div>
    </div>
  </div>
  <div class="modal fade" id="my-Statmodal" aria-hidden="true" aria-labelledby="pokemonStatModalLabel" tabindex="-1" role="dialog">
    <div class="modal-dialog modal-dialog-centered" role="document">
      <div class="modal-content">
        <div class="modal-header">
          <h5 class="Statmodal-title" id="exampleModalToggleLabel2" aria-labelledby="pokemonStatModalLabel">Modal 2 title</h5>
          <button type="button" class="modal-close" data-bs-dismiss="modal" aria-label="Close">X</button>
        </div>
        <div class="Statmodal-body">

        </div>
        <div class="modal-footer">
          <button class="btn modal-button" data-bs-target="#my-modal" data-bs-toggle="modal" data-bs-dismiss="modal">Back to Pokemon</button>
        </div>
      </div>
    </div>
  </div>












  <!--Top of Page Button-->
  <a href="#" id="back-to-top" class="back-to-top" style="display: inline;"><i class="fas fa-arrow-up fa-2x"></i></a>

  <!-- jQuery first, then Popper.js, then Bootstrap JS -->
  <script src="https://code.jquery.com/jquery-3.5.1.js" integrity="sha256-QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc=" crossorigin="anonymous"></script>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
  <!--PolyFills-->
  <script src="js/promise-polyfill.js"></script>
  <script src="js/fetch-polyfill.js"></script>

  <!--JS-->
  <script src="js/scripts.js"></script>
</body>

</html>