💻오늘 배운 내용
insertAdjacentHTML()
element.insertAdjacentHTML(position, text);
요소(element)의 내용을 변경하는 대신 HTML을 문서(document)에 삽입
요소 변경이 아닌 요소 삽입만을 해야 하는 상황
position의 값은 beforebegin, afterbegin, beforeend, afterend만 사용할 수 있다.
trim()
문자열 좌우에서 공백을 제거하는 함수
var a = " 가 나 다 라 마 "
a.trim()
//"가 나 다 라 마"
replace()
문자열에서 변경하려는 문자열이 여러 번 반복될 경우, 첫 번째로 발견한 문자열만 치환
apple, banana, banana' 이렇게 'banana'가 여러 번 반복될 경우
replace('banana', 'tomato')를 실행하면 첫번째로 발견된 'banana'만 치환되어
'apple, tomato, banana'의 결과를 얻게 됨
모든 문자열을 치환하려면
let str = 'apple, banana, orange, banana';
let replaced_str = str.replace(/banana/g, 'tomato');
document.write('변경 전 : ', str, '<br/>');
document.write('변경 후 : ', replaced_str, '<br/>');
파라미터로 들어가는 값이 정규식 임을 알려준다.
그리고, '/' 뒤에는 'g'라는 modifier를 붙였다. (g는 'global match'라는 의미로 사용)
❓발생한 이슈/고민
검색기능을 만드는데 하루종일 걸렸다
- 먼저 페이지 내 요소에대해 검색 기능을 만들고
- 자동완성 기능을 추가
- 띄어쓰기에 상관없이 검색가능하도록 구현 해보았다
💡해결과정
가장 처음 구성해본 검색기능
for (let i = 0; i < movies.length; i++) {
document.querySelectorAll('#movies')[i].innerText = movies[i].title;
}
function searchFilter(data, search) {
// data 값을 하나하나 꺼내와서
return data.map((d) => {
// 만약 해당 데이터가 search 값을 가지고 있다면 리턴한다.
if (d.includes(search)) {
return d;
}
});
}
// search 버튼 클릭 시 호출되는 함수
function search() {
// 폼에 입력된 값
let text = document.getElementById('input').value;
// res [undefined, {id:, name: favorites:}, undefined] 이런식으로 리턴
// 따라서 undefined 값을 제거해줘야하기 때문에 filter 메소드 적용
let res = searchFilter(movies, text).filter((d) => d !== undefined);
// 결과 값 화면 출력
document.getElementById('movies').innerText = res.join(', ');
}
// 클릭 시 search 함수 호출
document.getElementById('btn').addEventListener('click', search);
for문과 map 메소드를 이용하여 야심차게 구성했지만
검색이 동작하지 않는 문제발생검색이 동작하지 않는 문제발생
for문에서 innerText = movies[i].title이 아니라 result의 배열을 사용하도록 수정해야할 것 같다.
또한 생성될 영화 카드는 템플릿 리터럴을 이용하여 한눈에 보이도록 가독성을 높임
for (let i = 0; i < response.results.length; i++) {
document.querySelectorAll('.movieName')[i].innerText =
response.results[i].title;
}
function searchFilter(data, search) {
return data.filter((d) => d.title.includes(search));
}
function search() {
let text = document.getElementById('input').value;
let res = searchFilter(response.results, text);
document.getElementById('movies').innerHTML = '';
res.forEach((movie) => {
let template = `<div class="movie">
<img src="https://image.tmdb.org/t/p/w500${movie.poster_path}" alt="" />
<h2 class="movieName">${movie.title}</h2>
<p class="movieSum">${movie.overview}</p>
<p class="movieRate">평점 ${movie.vote_average}</p>
</div>`;
document.querySelector('#movies').insertAdjacentHTML('beforeend', template);
});
}
document.getElementById('btn').addEventListener('click', search);
기본적인 검색은 가능했지만 버튼이 아니라 엔터를 쳤을때 값이 검색이 실행되도록 하고 싶었다.
3번째 수정
// 초기화 및 이벤트 리스너 설정
function initialize() {
fetchMovies();
document
.getElementById('input')
.addEventListener('keydown', function (event) {
if (event.key === 'Enter') {
search();
}
});
}
initialize();
성공
검색기능 4차 수정 실시간반영 시도
// 검색어 입력 이벤트를 감지하여 실시간으로 검색 실행
function handleSearchInput() {
const searchInput = document.getElementById('input');
searchInput.addEventListener('input', function () {
search();
});
}
검색어 실시간 반영함수 추가 인풋창에 어떤 값이 입력되는것을 감지하면 바로바로 search 함수를 호출하게 했다.
반영은 되지만 기존카드 + 검색된 카드 중복생성되는 문제가 발생
검색 5차 수정
// 검색 버튼 클릭 시 호출되는 함수
function search() {
const searchInput = document.getElementById('input');
const searchText = searchInput.value.trim().toLowerCase();
const moviesContainer = document.getElementById('movies');
// 기존 영화 카드 삭제
moviesContainer.innerHTML = '';
if (searchText === '') {
// 검색어가 없는 경우, 모든 영화를 다시 표시
movies.forEach((movie) => {
const template = `<div class="movie">
<img src="https://image.tmdb.org/t/p/w500${movie.poster_path}" alt="" />
<h2 class="movieName">${movie.title}</h2>
<p class="movieSum">${movie.overview}</p>
<p class="movieRate">평점 ${movie.vote_average}</p>
</div>`;
moviesContainer.insertAdjacentHTML('beforeend', template);
});
return;
}
const filteredMovies = searchFilter(movies, searchText);
filteredMovies
.forEach((movie) => {
const template = `<div class="movie">
<img src="https://image.tmdb.org/t/p/w500${movie.poster_path}" alt="" />
<h2 class="movieName">${movie.title}</h2>
<p class="movieSum">${movie.overview}</p>
<p class="movieRate">평점 ${movie.vote_average}</p>
</div>`;
moviesContainer.insertAdjacentHTML('beforeend', template);
})
.catch((error) => console.error(error));
}
성공!
- 검색 결과를 표시하는 부분에서, 기존에는 fetchMovies() 함수를 호출하여 모든 영화를 표시한 다음, 검색 결과에 맞는 영화 카드만 따로 표시했었는데,
- 이를 수정하여 검색 결과에 따라 필터링된 영화 카드만 표시하도록 변경.
- 이를 위해 searchFilter() 함수를 사용하여 검색어에 맞는 영화를 필터링하고, 필터링된 영화 카드를 반복적으로 생성하여 표시하도록 수정.
이렇게 변경된 코드는 검색어 입력 시 실시간으로 결과를 반영하고, 중복된 카드가 생성되지 않도록 한다.
추가로 띄어쓰기에 상관없게!
function searchFilter(data, search) {
const searchKeywords = search.toLowerCase().split(' ');
return data.filter((movie) => {
const movieTitle = movie.title.toLowerCase().replace(/\\s/g, '');
return searchKeywords.every((keyword) => movieTitle.includes(keyword));
});
}
먼저, searchFilter 함수 내부에서는 검색어를 소문자로 변환하고,
split 메서드를 사용하여 띄어쓰기를 기준으로 검색어를 여러 개의 키워드로 분리한다.
이렇게 분리된 키워드들은 searchKeywords 배열에 저장.
그 후, filter 메서드를 사용하여 데이터 배열을 필터링하는데,
각 영화의 제목을 소문자로 변환하고, replace 메서드를 이용하여 제목 내의 모든 공백을 제거.
이렇게 만들어진 movieTitle은 영화 제목을 공백 없이 이어붙인 문자열.
마지막으로, searchKeywords 배열의 각 키워드를 순회하면서,
every 메서드를 사용하여 모든 키워드가 movieTitle에 포함되는지 확인한다.
모든 키워드가 포함되는 경우에만 true를 반환하고, 그 외의 경우는 false를 반환하여 해당 영화를 필터링.
이렇게 하면 검색어의 키워드들이 영화 제목에 띄어쓰기에 상관없이
키워드에 포함되어 있는 경우에는 해당하는 영화들을 찾을 수 있게 된다.
그렇게 어찌저찌 완성한 코드가
let movies = []; // 영화 데이터를 저장할 배열
function fetchMovies() {
fetch(
'https://api.themoviedb.org/3/movie/upcoming?language=ko&page=1',
options
)
.then((response) => response.json())
.then((response) => {
movies = response.results; // 영화 데이터를 배열에 저장
movies.forEach((movie) => {
let template = `<div class="movie">
<img src="https://image.tmdb.org/t/p/w500${movie.poster_path}" alt="" />
<h2 class="movieName">${movie.title}</h2>
<p class="movieSum">${movie.overview}</p>
<p class="movieRate">평점 ${movie.vote_average}</p>
</div>`;
document
.querySelector('#movies')
.insertAdjacentHTML('beforeend', template);
});
})
.catch((err) => console.error(err));
}
// 검색어를 사용하여 영화를 필터링하는 함수
function searchFilter(data, search) {
const searchKeywords = search.toLowerCase().split(' ');
return data.filter((movie) => {
const movieTitle = movie.title.toLowerCase().replace(/\s/g, '');
return searchKeywords.every((keyword) => movieTitle.includes(keyword));
});
}
// 검색 버튼 클릭 시 호출되는 함수
function search() {
const searchInput = document.getElementById('input');
const searchText = searchInput.value.trim().toLowerCase();
const moviesContainer = document.getElementById('movies');
// 기존 영화 카드 삭제
moviesContainer.innerHTML = '';
if (searchText === '') {
// 검색어가 없는 경우, 모든 영화를 다시 표시
movies.forEach((movie) => {
const template = `<div class="movie">
<img src="https://image.tmdb.org/t/p/w500${movie.poster_path}" alt="" />
<h2 class="movieName">${movie.title}</h2>
<p class="movieSum">${movie.overview}</p>
<p class="movieRate">평점 ${movie.vote_average}</p>
</div>`;
moviesContainer.insertAdjacentHTML('beforeend', template);
});
return;
}
const filteredMovies = searchFilter(movies, searchText);
filteredMovies
.forEach((movie) => {
const template = `<div class="movie">
<img src="https://image.tmdb.org/t/p/w500${movie.poster_path}" alt="" />
<h2 class="movieName">${movie.title}</h2>
<p class="movieSum">${movie.overview}</p>
<p class="movieRate">평점 ${movie.vote_average}</p>
</div>`;
moviesContainer.insertAdjacentHTML('beforeend', template);
})
.catch((error) => console.error(error));
}
// 검색어 입력 이벤트를 감지하여 실시간으로 검색 실행
function handleSearchInput() {
const searchInput = document.getElementById('input');
searchInput.addEventListener('input', function () {
search();
});
}
// 초기화 및 이벤트 리스너 설정
function initialize() {
fetchMovies();
handleSearchInput();
document
.getElementById('input')
.addEventListener('keydown', function (event) {
if (event.key === 'Enter') {
event.preventDefault(); // 기본 동작 방지
search();
}
});
}
initialize();
…이것인데…이게 전체 코든데.. let template만 3번이나 반복되는 것이 상당히 비효율적이라고 느낌
수정한 코드
function fetchMovies() {
fetch(
'https://api.themoviedb.org/3/movie/upcoming?language=ko&page=1',
options
)
.then((response) => response.json())
.then((response) => {
movies = response.results; // 영화 데이터를 배열에 저장
displayMovies(movies); // 영화 카드 표시 함수 호출
})
.catch((err) => console.error(err));
}
// 영화 카드를 표시하는 함수
function displayMovies(movies) {
const moviesContainer = document.getElementById('movies');
movies.forEach((movie) => {
const template = `<div class="movie">
<img src="https://image.tmdb.org/t/p/w500${movie.poster_path}" alt="" />
<h2 class="movieName">${movie.title}</h2>
<p class="movieSum">${movie.overview}</p>
<p class="movieRate">평점 ${movie.vote_average}</p>
</div>`;
moviesContainer.insertAdjacentHTML('beforeend', template);
});
}
// 검색어를 사용하여 영화를 필터링하는 함수
function searchFilter(data, search) {
const searchKeywords = search.toLowerCase().split(' ');
return data.filter((movie) => {
const movieTitle = movie.title.toLowerCase().replace(/\s/g, '');
return searchKeywords.every((keyword) => movieTitle.includes(keyword));
});
}
// 검색 버튼 클릭 시 호출되는 함수
function search() {
const searchInput = document.getElementById('input');
const searchText = searchInput.value.trim().toLowerCase();
const moviesContainer = document.getElementById('movies');
// 기존 영화 카드 삭제
moviesContainer.innerHTML = '';
if (searchText === '') {
// 검색어가 없는 경우, 모든 영화를 다시 표시
displayMovies(movies);
return;
}
const filteredMovies = searchFilter(movies, searchText);
displayMovies(filteredMovies);
}
수정된 코드에서는 displayMovies 함수를 도입하여 영화 카드를 표시하는 부분을 별도의 함수로 분리했다.
그리고 검색 관련 로직도 변경되었으며, displayMovies 함수를 호출하여 영화 카드를 표시한다.
📋레퍼런스
'내일배움캠프 > Today I Learned' 카테고리의 다른 글
[TIL 2023.06.01] 영화 검색 페이지 완성! 제출 전 다듬기 (GOE’s CINEMA) (0) | 2023.06.02 |
---|---|
[TIL 2023.05.31] 순수 자바스크립트로 무한루프 자동 슬라이드 만들기 (GOE's CINEMA) (0) | 2023.06.01 |
[TIL 2023.05.29] 영화 검색 페이지 만들기 (GOE’s CINEMA) (0) | 2023.05.30 |
[TIL 2023.05.26] this의 정의, 활용방법, call, apply, bind (0) | 2023.05.27 |
[TIL 2023.05.25] JavaScript 데이터 타입, 실행컨텍스트 (1) | 2023.05.26 |