whenever I click my “show more” button my modal pops up however when I click my X button on the left corner it doesn’t close like I expect it to. It does respond to clicking outside of the box and the “esc” key.
I believe the issue is happening on modalClose.on() but everything looks fine to me.
Any suggestions as to why this might be happening?
let pokemonRepository = (function() { let pokemonList = []; // API let apiUrl = "https://pokeapi.co/api/v2/pokemon/?limit=150"; let modalContainer = $(".modal"); let modalDialog = $(".modal-dialog"); let modalContent = $(".modal-content"); let modalBody = $(".modal-body"); let modalTitle = $(".modal-title"); let modalHeader = $(".modal-header"); let modalClose = $(".btn-close"); let searchIcon = $(".search-icon"); let listItemArray = $("li"); function add(pokemon) { if ( typeof pokemon === "object" && "name" in pokemon && "detailsUrl" in pokemon ) { pokemonList.push(pokemon); } else { console.error("pokemon is not correct"); } } function getAll() { return pokemonList; } // filters through pokemon names function search(pokemonName) { return pokemonList.filter((pokemon) => pokemon.name === pokemonName); } // Function adds a list of pokemon function addListItem(pokemon) { let pokemonDisplay = $(".list-group-horizontal"); // Creates li element let listItem = $("<li>"); listItem.addClass( "list-group-item text-center col-sm-6 col-md-4 border border-secondary bg-image img-fluid" ); // Creates h1 for Pokemon Name let listTitle = $("<h1>"); listTitle.html(`${pokemon.name}`); listTitle.addClass("display-6"); // Creates div which holds sprites let listImg = $("<div>"); loadDetails(pokemon).then(function() { listImg.append( `<img src=${pokemon.imageUrlFront} alt="${pokemon.name} sprite"/>` ); }); let listButton = $("<button>"); listButton.text("show More"); // Added Bootstrap Utility Class listButton.addClass("mp-2 btn btn-secondary"); listButton.attr("type", "button"); listButton.attr("data-bs-toggle", "modal"); listButton.attr("data-bs-toggle", "#pokemonModal"); listItem.append(listTitle); listItem.append(listImg); listItem.append(listButton); pokemonDisplay.append(listItem); buttonEvent(listButton, pokemon); } function buttonEvent(listButton, pokemon) { listButton.on("click", () => { showDetails(pokemon); }); } function showDetails(pokemon) { loadDetails(pokemon).then(() => { // Clears existing content modalContainer.empty(); modalTitle.addClass("modal-title h5 col-sml-3"); let pokemonType = { fire: "text-danger", grass: "text-success", water: "text-primary", electric: "text-warning", flying: "text-info", poison: "text-secondary", }; pokemon.types.forEach((type) => modalTitle.addClass(pokemonType[type.type.name]) ); modalTitle.html(`${pokemon.name}`); modalBody.html(` Entry: ${pokemon.id}<br> Height: ${pokemon.height}<br> Weight: ${pokemon.weight}<br> Types: ${pokemon.types[0].type.name}`); if (pokemon.types.length === 2) { modalBody.innerHTML += `, ${pokemon.types[1].type.name}`; } modalBody.innerHTML += `<br>Abilities: ${pokemon.abilities[0]}.ability.name}`; if (pokemon.abilities.length === 2) { modalBody.innerHTML += `, ${pokemon.abilities[1]}.ability.name}`; } modalBody.append(`<br> <img src=${pokemon.imageUrlFront} alt="${pokemon.name} front sprite"> <img src=${pokemon.imageUrlBack} alt="${pokemon.name} back sprite"> <br> `); modalDialog.append(modalContent); modalContent.append(modalHeader); modalHeader.append(modalTitle); modalHeader.append(modalClose); modalContent.append(modalBody); modalContainer.append(modalDialog); }); modalContainer.modal("show"); } // Jquery eventlistener modalClose.on("click", () => { modalContainer.removeClass("fade"); modalContainer.show(); listItemArray[0].lastChild.click(); }); searchIcon.on("click", () => { // fetching .d-flex class in form let bodyHeader = $(".d-flex"); // returns the number of child elements if (bodyHeader.lastChild.length === 1) { //creates input element let searchQuery = $("<input>"); searchQuery.attr("placeholder", "Pokemon Name"); searchQuery.attr("type", "search"); searchQuery.attr("aria-label", "search Pokemon Name"); searchQuery.addClass("form-control my-3 ps-2 col-sm"); searchIcon.blur(); searchQuery.focus(); bodyHeader.append(searchQuery); searchQuery.on("keydown", (e) => { if (e.key === "Enter") { e.preventDefault(); searchQuery.value = searchQuery.value.charAt(0).toUpperCase() + searchQuery.value.slice(1); for (let i = 0; i < listItemArray.length; i++) { if ( 902 > listItemArray[i].lastChild.getBoundingClientRect()["top"] && listItemArray[i].lastChild.getBoundingClientRect()["top"] > 42 ) { listItemArray[i].lastChild.click(); } } for (let i = 0; i < listItemArray.length; i++) { if ( listItemArray[i].innerText.split("n")[0] === searchQuery.value ) { setTimeout(function() { listItemArray[i].lastChild.click(); }, 5); } } } }); } }); // Fetches data from API function loadList() { return fetch(apiUrl) .then(function(response) { return response.json(); }) .then(function(json) { json.results.forEach((item) => { let pokemon = { name: item.name.charAt(0).toUpperCase() + item.name.slice(1), detailsUrl: item.url, }; add(pokemon); }); }) .catch(function(error) { console.error(error); }); } function loadDetails(item) { let url = item.detailsUrl; return fetch(url) .then(function(response) { return response.json(); }) .then(function(details) { item.imageUrlFront = details.sprites.front_default; item.imageUrlBack = details.sprites.back_default; item.id = details.id; item.height = details.height; item.weight = details.weight; item.types = details.types; item.abilities = details.abilities; }) .catch(function(error) { console.error(error); }); } return { add: add, getAll: getAll, addListItem: addListItem, search: search, showDetails: showDetails, loadList: loadList, loadDetails: loadDetails, buttonEvent: buttonEvent, }; })(); pokemonRepository.loadList().then(function() { pokemonRepository.getAll().forEach(function(pokemon) { pokemonRepository.addListItem(pokemon); }); });
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="description" content="The Pokédex is a simple encyclopedia of Pokémon and their characteristics." /> <link rel="shortcut icon" href="img/favicon.png" type="image/x-icon" /> <title>Pokédex App</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" /> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.3/font/bootstrap-icons.css" /> <link rel="stylesheet" href="/dist/style.production.css" /> </head> <body> <nav class="navbar navbar-expand-lg sticky-top navbar-light bg-light"> <div class="container-fluid"> <a href="#home" class="navbar-brand"> <img src="img/ball.png" width="30" height="24" alt="" class="d-inline-block align-text-top" /><span class="text-uppercase font-weight-bold text-secondary">Pokèdex</span> </a> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarTogglerDemo01" aria-controls="navbarTogglerDemo01" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarSupportedContent"> <ul class="navbar-nav me-auto mb-2 mb-lg-0"> <li class="nav-item"> <a class="nav-link active" aria-current="page" href="#home">Home</a > </li> <li class="nav-item"> <a class="nav-link" href="#about">About</a> </li> </ul> </li> </ul> <form class="d-flex" role="search"> <input class="form-control me-2" placeholder="Pokemon Name" aria-label="Search" /> <button class="btn btn-outline-secondary" type="submit"> Search </button> </form> </div> </div> </nav> <p class="fw-bold position-absolute top-10 start-50 text-center text-danger"></p> <!-- Pokemon Display --> <div class="container"> <ul class="list-group list-group-horizontal flex-fill row mt-4"></ul> </div> <!-- Display Ends Here --> <div class="modal fade" id="pokemonModal" tabindex="-1" role="dialog" aria-labelledby="pokemonModalLabel" aria-hidden="true"> <div class="modal-dialog pt-5 text-center" role="document"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title col-sm-3" id="pokemonModalLabel"></h5> <button type="button" class="btn-close me-3" data-dismiss="modal" aria-label="Close" aria-hidden="true"></button> </div> <!-- Content is dynamically created using jquery --> <div class="modal-body"></div> </div> </div> </div> <script src="https://code.jquery.com/jquery-3.6.0.js" integrity="sha256-H+K7U5CnXl1h5ywQfKtSj8PCmoN9aaq30gDh27Xc0jk=" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.5/dist/umd/popper.min.js" integrity="sha384-Xe+8cL9oJa6tN/veChSP7q+mnSPaj5Bcu9mPX5F5xIGE0DVittaqT5lorf0EI7Vk" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.min.js" integrity="sha384-ODmDIVzN+pFdexxHEHFBQH3/9/vQ9uori45z4JjnFsRydbmQbmL5t1tQ0culUzyK" crossorigin="anonymous"></script> <script src="/src/js/scripts.js"></script> <script src="/src/js/promise-polyfill.js"></script> <script src="/src/js/fetch-pollyfill.js"></script> </body> </html>
Advertisement
Answer
Because for some reason the modal is being reconstructed I assume all the event handlers bootstrap configs for it are gone. So you want to catch click on close, you need to do that after it is shown. This can be done using the event bootstrap provides.
modalContainer.on('shown.bs.modal', event => { // Jquery eventlistener modalClose.on("click", () => { modalContainer.modal("hide"); }); })
let pokemonRepository = (function() { let pokemonList = []; // API let apiUrl = "https://pokeapi.co/api/v2/pokemon/?limit=150"; let modalContainer = $(".modal"); let modalDialog = $(".modal-dialog"); let modalContent = $(".modal-content"); let modalBody = $(".modal-body"); let modalTitle = $(".modal-title"); let modalHeader = $(".modal-header"); let modalClose = $(".btn-close"); let searchIcon = $(".search-icon"); let listItemArray = $("li"); function add(pokemon) { if ( typeof pokemon === "object" && "name" in pokemon && "detailsUrl" in pokemon ) { pokemonList.push(pokemon); } else { console.error("pokemon is not correct"); } } function getAll() { return pokemonList; } // filters through pokemon names function search(pokemonName) { return pokemonList.filter((pokemon) => pokemon.name === pokemonName); } // Function adds a list of pokemon function addListItem(pokemon) { let pokemonDisplay = $(".list-group-horizontal"); // Creates li element let listItem = $("<li>"); listItem.addClass( "list-group-item text-center col-sm-6 col-md-4 border border-secondary bg-image img-fluid" ); // Creates h1 for Pokemon Name let listTitle = $("<h1>"); listTitle.html(`${pokemon.name}`); listTitle.addClass("display-6"); // Creates div which holds sprites let listImg = $("<div>"); loadDetails(pokemon).then(function() { listImg.append( `<img src=${pokemon.imageUrlFront} alt="${pokemon.name} sprite"/>` ); }); let listButton = $("<button>"); listButton.text("show More"); // Added Bootstrap Utility Class listButton.addClass("mp-2 btn btn-secondary"); listButton.attr("type", "button"); listButton.attr("data-bs-toggle", "modal"); listButton.attr("data-bs-toggle", "#pokemonModal"); listItem.append(listTitle); listItem.append(listImg); listItem.append(listButton); pokemonDisplay.append(listItem); buttonEvent(listButton, pokemon); } function buttonEvent(listButton, pokemon) { listButton.on("click", () => { showDetails(pokemon); }); } function showDetails(pokemon) { loadDetails(pokemon).then(() => { // Clears existing content modalContainer.empty(); modalTitle.addClass("modal-title h5 col-sml-3"); let pokemonType = { fire: "text-danger", grass: "text-success", water: "text-primary", electric: "text-warning", flying: "text-info", poison: "text-secondary", }; pokemon.types.forEach((type) => modalTitle.addClass(pokemonType[type.type.name]) ); modalTitle.html(`${pokemon.name}`); modalBody.html(` Entry: ${pokemon.id}<br> Height: ${pokemon.height}<br> Weight: ${pokemon.weight}<br> Types: ${pokemon.types[0].type.name}`); if (pokemon.types.length === 2) { modalBody.innerHTML += `, ${pokemon.types[1].type.name}`; } modalBody.innerHTML += `<br>Abilities: ${pokemon.abilities[0]}.ability.name}`; if (pokemon.abilities.length === 2) { modalBody.innerHTML += `, ${pokemon.abilities[1]}.ability.name}`; } modalBody.append(`<br> <img src=${pokemon.imageUrlFront} alt="${pokemon.name} front sprite"> <img src=${pokemon.imageUrlBack} alt="${pokemon.name} back sprite"> <br> `); modalDialog.append(modalContent); modalContent.append(modalHeader); modalHeader.append(modalTitle); modalHeader.append(modalClose); modalContent.append(modalBody); modalContainer.append(modalDialog); }); modalContainer.on('shown.bs.modal', event => { // Jquery eventlistener modalClose.on("click", () => { modalContainer.modal("hide"); }); }) modalContainer.modal("show"); } searchIcon.on("click", () => { // fetching .d-flex class in form let bodyHeader = $(".d-flex"); // returns the number of child elements if (bodyHeader.lastChild.length === 1) { //creates input element let searchQuery = $("<input>"); searchQuery.attr("placeholder", "Pokemon Name"); searchQuery.attr("type", "search"); searchQuery.attr("aria-label", "search Pokemon Name"); searchQuery.addClass("form-control my-3 ps-2 col-sm"); searchIcon.blur(); searchQuery.focus(); bodyHeader.append(searchQuery); searchQuery.on("keydown", (e) => { if (e.key === "Enter") { e.preventDefault(); searchQuery.value = searchQuery.value.charAt(0).toUpperCase() + searchQuery.value.slice(1); for (let i = 0; i < listItemArray.length; i++) { if ( 902 > listItemArray[i].lastChild.getBoundingClientRect()["top"] && listItemArray[i].lastChild.getBoundingClientRect()["top"] > 42 ) { listItemArray[i].lastChild.click(); } } for (let i = 0; i < listItemArray.length; i++) { if ( listItemArray[i].innerText.split("n")[0] === searchQuery.value ) { setTimeout(function() { listItemArray[i].lastChild.click(); }, 5); } } } }); } }); // Fetches data from API function loadList() { return fetch(apiUrl) .then(function(response) { return response.json(); }) .then(function(json) { json.results.forEach((item) => { let pokemon = { name: item.name.charAt(0).toUpperCase() + item.name.slice(1), detailsUrl: item.url, }; add(pokemon); }); }) .catch(function(error) { console.error(error); }); } function loadDetails(item) { let url = item.detailsUrl; return fetch(url) .then(function(response) { return response.json(); }) .then(function(details) { item.imageUrlFront = details.sprites.front_default; item.imageUrlBack = details.sprites.back_default; item.id = details.id; item.height = details.height; item.weight = details.weight; item.types = details.types; item.abilities = details.abilities; }) .catch(function(error) { console.error(error); }); } return { add: add, getAll: getAll, addListItem: addListItem, search: search, showDetails: showDetails, loadList: loadList, loadDetails: loadDetails, buttonEvent: buttonEvent, }; })(); pokemonRepository.loadList().then(function() { pokemonRepository.getAll().forEach(function(pokemon) { pokemonRepository.addListItem(pokemon); }); });
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="description" content="The Pokédex is a simple encyclopedia of Pokémon and their characteristics." /> <link rel="shortcut icon" href="img/favicon.png" type="image/x-icon" /> <title>Pokédex App</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" /> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.3/font/bootstrap-icons.css" /> <link rel="stylesheet" href="/dist/style.production.css" /> </head> <body> <nav class="navbar navbar-expand-lg sticky-top navbar-light bg-light"> <div class="container-fluid"> <a href="#home" class="navbar-brand"> <img src="img/ball.png" width="30" height="24" alt="" class="d-inline-block align-text-top" /><span class="text-uppercase font-weight-bold text-secondary">Pokèdex</span> </a> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarTogglerDemo01" aria-controls="navbarTogglerDemo01" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarSupportedContent"> <ul class="navbar-nav me-auto mb-2 mb-lg-0"> <li class="nav-item"> <a class="nav-link active" aria-current="page" href="#home">Home</a > </li> <li class="nav-item"> <a class="nav-link" href="#about">About</a> </li> </ul> </li> </ul> <form class="d-flex" role="search"> <input class="form-control me-2" placeholder="Pokemon Name" aria-label="Search" /> <button class="btn btn-outline-secondary" type="submit"> Search </button> </form> </div> </div> </nav> <p class="fw-bold position-absolute top-10 start-50 text-center text-danger"></p> <!-- Pokemon Display --> <div class="container"> <ul class="list-group list-group-horizontal flex-fill row mt-4"></ul> </div> <!-- Display Ends Here --> <div class="modal fade" id="pokemonModal" tabindex="-1" role="dialog" aria-labelledby="pokemonModalLabel" aria-hidden="true"> <div class="modal-dialog pt-5 text-center" role="document"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title col-sm-3" id="pokemonModalLabel"></h5> <button type="button" class="btn-close me-3" data-dismiss="modal" aria-label="Close" aria-hidden="true"></button> </div> <!-- Content is dynamically created using jquery --> <div class="modal-body"></div> </div> </div> </div> <script src="https://code.jquery.com/jquery-3.6.0.js" integrity="sha256-H+K7U5CnXl1h5ywQfKtSj8PCmoN9aaq30gDh27Xc0jk=" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.5/dist/umd/popper.min.js" integrity="sha384-Xe+8cL9oJa6tN/veChSP7q+mnSPaj5Bcu9mPX5F5xIGE0DVittaqT5lorf0EI7Vk" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.min.js" integrity="sha384-ODmDIVzN+pFdexxHEHFBQH3/9/vQ9uori45z4JjnFsRydbmQbmL5t1tQ0culUzyK" crossorigin="anonymous"></script> <script src="/src/js/scripts.js"></script> <script src="/src/js/promise-polyfill.js"></script> <script src="/src/js/fetch-pollyfill.js"></script> </body> </html>