使用JavaScript筛选电影列表:解决类型筛选及data未定义问题
Let's break down the issues in your code and fix them step by step:
1. The "data is undefined" Error
First off, that error happens because of variable scope. You declared var data inside the if (this.readyState === 4 && this.status === 200) block, which means it only exists inside that block. When you try to log data.movies.categories outside of that block, data hasn't been defined yet (either the request hasn't finished, or it failed).
Also, data.movies is an array—you can't access data.movies.categories directly; you need to loop through individual movie objects instead.
2. Missing Genre Filter Functionality
Your current code doesn't listen for changes on the dropdown menu, so selecting a genre won't trigger any filtering. Plus, some movies have multiple categories (like "action,fantasy"), so a simple == check won't work—we need to check if the selected genre is present in the movie's category list.
Fixed Code Implementation
Here's the revised JavaScript code with all issues resolved:
window.addEventListener("load", setup); // Declare data in a higher scope so it's accessible to the filter function let data; function setup() { var xhr = new XMLHttpRequest(); xhr.open('GET', 'data/data.json', true); xhr.onreadystatechange = function() { if (this.readyState === 4 && this.status === 200) { data = JSON.parse(this.responseText); renderMovies(data.movies); populateGenres(data.categories); // Add event listener for genre selection document.getElementById('selection').addEventListener('change', filterMoviesByGenre); } } xhr.onerror = function() { console.log("Error fetching data"); } xhr.send(); } // Function to populate the genre dropdown function populateGenres(categories) { let genreHtml = '<option>-- Select Genre --</option>'; categories.forEach(category => { genreHtml += `<option>${category}</option>`; }); document.getElementById('selection').innerHTML = genreHtml; } // Function to render movie list function renderMovies(movies) { let output = ''; movies.forEach(movie => { output += ` <ul class="movie-item"> <li>Title: ${movie.title}</li> <li>Category: ${movie.categories}</li> </ul> `; }); document.getElementById('output').innerHTML = output; } // Function to filter movies by selected genre function filterMoviesByGenre() { const selectedGenre = this.value.trim(); const movieItems = document.querySelectorAll('.movie-item'); movieItems.forEach((item, index) => { const movieCategories = data.movies[index].categories.split(',').map(cat => cat.trim()); // Show all movies if no genre is selected, else check if genre matches if (selectedGenre === '-- Select Genre --') { item.classList.remove('hidden'); } else if (!movieCategories.includes(selectedGenre)) { item.classList.add('hidden'); } else { item.classList.remove('hidden'); } }); }
Key Changes Made:
- Scoped
datacorrectly: Declareddataoutside the XHR callback so it's accessible to the filter function. - Split code into reusable functions: Separated rendering movies, populating genres, and filtering into distinct functions for better readability.
- Handled multiple categories: Used
split(',')to turn a movie's category string into an array, then trimmed whitespace to match genre values accurately. - Added dropdown change listener: The
filterMoviesByGenrefunction runs every time the user selects a new genre, showing/hiding movies accordingly. - Fixed the loop issue: Your original loop ran for the length of both
data.moviesanddata.categoriesat the same time, which would cause mismatches (since there are more movies than categories). We now useforEachto loop through each array separately.
CSS Note
Don't forget to add the hidden class in your CSS to actually hide the movie items:
.hidden { display: none; }
内容的提问来源于stack exchange,提问作者Wislow

