this is written in JS
i cant seem to make the MovieDetails button work at all.
function searchMovie(query) {
const url = `https://imdb8.p.rapidapi.com/auto-complete?q=${query}`;
fetch(url, options)
.then(response => response.json())
.then(data => {
const list = data.d;
list.map((item) => { //makes a list of each individual movie from the data
const name = item.l; // holds the name of movie
const poster = item.i.imageUrl; // holds the poster, given by the data
const detail = item.id // holds the ttid of the movie
// below is what shows the poster, movie name, etc
const movie =
`
<body>
<div >
<div class = "well text-center">
<li><img src="${poster}">
<h2>${name}</h2>
</li>
<button type = "button" id = "MovieDetails" href="#">Movie Details</button>
<script>
document.getElementById('MovieDetails').addEventListener("click",myFunction);
function myFunction(){
console.log(detail)
}
</script>
</div>
</div>
</body>
`;
document.querySelector('.movies').innerHTML = movie; // returns the first element movies and poster to movie div
//console.log()
});
document.getElementById("errorMessage").innerHTML = "";
})
.catch((error) => {
document.getElementById("errorMessage").innerHTML = error;
});
// we should make a condition here for when a new item is placed here there will be a page refresh
// setTimeout(() => {
// location.reload(); }, 2000);
}
the function above will make an api call and then save the results into list, i can hold specific elements of the list in the three const's and const movie will output the movie poster, name and display it. I want to make a button for each movie that when clicked will output the id of the movie which is held in const details. But i cant figure out how to make it work, i have tried (button onclick = function ..) and (document.getElementById...) but it says that getElementById cant be null.
i know that this seems like a silly problem but i cant seem to figure how to make the button actually output something useful or any other way to make a button be mapped out to each api call.
CodePudding user response:
You're heading in the right direction but there are a couple of pain-points with your code as the other commenters have indicated.
Your template string is adding a brand new
body
element to the page for each movie where there should just be one for the whole document. Nice idea to use a template string though - by far the simplest method to get new HTML on to the page.Adding JS to the page dynamically like that is going to end up causing you all kinds of problems - probably too many to mention here, so I'll just skip to the good part.
First remove the body
element from the template string, and perhaps tidy up the remaining HTML to be a little more semantic. I've used section
here but, really, anything other than having lots of divs
is a step in the right direction.
Second: event delegation. Element events "bubble up" the DOM. Instead of attaching a listener to every button we can add one listener to the movie list containing element, and have that catch and process events from its children.
(Note: in this example, instead of logging the details to the console, I'm adding the details to the HTML, and then allowing the button to toggle the element on/off.)
// Cache the movie list element, and attach a listener to it
const movieList = document.querySelector('.movielist');
movieList.addEventListener('click', handleClick);
// Demo data
const data=[{name:"movie1",poster:"img1",details:"Details for movie1."},{name:"movie2",poster:"img2",details:"Details for movie2."},{name:"movie3",poster:"img3",details:"Details for movie3."}];
// `map` over the data to produce your HTML using a
// template string as you've done in your code (no body element)
// Make sure you `join` up the array that `map` returns into a
// whole string once the iteration is complete.
const html = data.map(obj => {
return `
<section >
<header>${obj.name}</header>
<section >${obj.details}</section>
<button type="button">Movie Details</button>
</section>
`;
}).join('');
// Insert that HTML on to the movie list element
movieList.insertAdjacentHTML('beforeend', html);
// This is the handler for the listener attached to the
// movie list. When that element detects an event from a button
// it finds button's previous element sibling (the section
// with the `.details` class), and, in this case, toggles a show
// class on/off
function handleClick(e) {
if (e.target.matches('button')) {
const details = e.target.previousElementSibling;
details.classList.toggle('show');
}
}
.movie { border: 1px solid #555; padding: 0.5em;}
.movie header { text-transform: uppercase; background-color: #efefef; }
.details { display: none; }
.show { display: block; }
button { margin-top: 0.5em; }
<section ></section>
Additional documentation