Trónok harca szereplőinek megjelenítése

Mi a cél?

A Trónok harca szereplőinek megjelenítése egy külső file-ban tárolt adatok alapján.

DEMO (a képre kattintva indul)

Trónok harca szereplőinek megjelenítése

A HTML template

A kikommentelt részeket a JS fogja előállítani, a design elkészítéséig használjuk csak őket sablonnak.

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>game of thrones</title> <link href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,400;0,700;1,400&display=swap" rel="stylesheet"> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"> <link rel="stylesheet" href="style.css"> </head> <body> <main class="container"> <article> <div class="cards"> <!-- <div class="card"> <div class="card__img"> <img src="./assets/aemon.png" alt=""> </div> <div class="card__name">name</div> </div> --> </div> </article> <aside> <div class="aside__header"> Game of thrones </div> <div class="aside__details"> <!-- <div class="aside__details-img"> <img src="../assets/pictures/aemon_targaryen.jpg" alt=""> </aside__details-img> <div class="aside__details-name-arms"> <span>Szereplő neve</span> <img src="../assets/houses/baratheon.png" alt=""> </div> <div class="aside__details-short">Rövid leírás helye</div> --> </div> <div class="aside__search"> <span><i class="fa fa-angle-right"></i></span> <form class="aside__form" action=""> <button type="submit"><i class="fa fa-search"></i></button> <input type="search" placeholder="Search a character" name="search"> </form> </div> </aside> </main> <script src="./js/got.js"></script> </body> </html>

A CSS design

* { margin: 0; padding: 0; } *, *::before, *::after { box-sizing: inherit; } html { box-sizing: border-box; font-family: roboto; } body { min-height: 100vh; display: flex; } .container { display: flex; min-height: 100%; } @media only screen and (max-width: 900px) { .container { flex-direction: column; } } article { flex: 5; background-image: url(./assets/site/mapblue.jpg); background-size: cover; } .cards { display: flex; flex-wrap: wrap; margin: 1rem 1.5rem; } .card { flex: 0 1 calc(12.5% - .5rem); padding: 1rem; } .active { box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.5), 0 6px 20px 0 rgba(0, 0, 0, 0.19); } .card__img img { display: block; border: 0; width: 50px; height: auto; margin: 0 auto; } .card__img img:hover { border-radius: 50%; cursor: pointer; box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.5), 0 6px 20px 0 rgba(0, 0, 0, 0.19); } .card__img img:active { border-radius: 50%; box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.8), 0 6px 20px 0 rgba(0, 0, 0, 0.19); } .card__name { text-transform: uppercase; font-size: .5rem; color: rgb(124, 72, 3); text-align: center; padding: .2rem 0; } .card__name:hover { background-color: #ccc; } .card__name:active { background-color: rgb(176, 176, 176); } aside { flex: 1; padding: 1rem; display: flex; flex-direction: column; background-color: rgb(91, 91, 91); color: white; } .aside__header { text-align: center; font-size: 1.1rem; text-transform: uppercase; padding: 1rem 0; font-family: meme; } @font-face { font-family: meme; src: url(./fonts/meme.ttf); } .aside__details { flex: 1; } .aside__details-img img { max-width: 100%; } .aside__details-name-arms { display: flex; justify-content: space-between; font-size: .9rem; } .aside__details-short { font-size: .7rem; margin-top: .5rem; line-height: 1.1rem; } .aside__search { margin-top: 1rem; } .aside__search form { display: inline-block; } .aside__search .fa-angle-right{ border: 1px solid white; border-radius: 2px; font-weight: bold; padding: .2rem .4rem; line-height: 1.2rem; float: left; margin-right: .5rem; margin-bottom: .5rem; } .aside__search button { float: left; border: none; cursor: pointer; margin-bottom: .5rem; } .aside__search button:hover { background: #ccc; } .aside__search .fa-search { color: black; padding: .2rem; background-color: white; border: 1px solid white; line-height: 1.2rem; } .aside__search input { border: 1px solid white; color: rgb(91, 91, 91);; font-size: 1rem; padding: .2rem .4rem; line-height: 1.2rem; float: left; }

A Javascript logika

A got.json file-ból fogjuk kinyerni az adatokat, ahol egy tömbben találhatóak a szereplők adatai objektumokként, éppúgy mintha egy adatbázisból kérdeztük volna le azokat. Egy részlet a json file-ból.

[ { "name": "Jon Snow", "portrait": "assets/jon.png", "picture": "assets/pictures/jon_snow.jpg", "bio": "Jon is the bastard child of Ned Stark of Winterfell and a unkwnow woman. Soon after his marriage, Ned Stark leaved his home to go to fight on the war of the usurper, and returned home carrying a infant that was supposedly his own", "organization": "nightwatch" },

Először felírjuk a használni kívánt DOM elemeket.

const cards = document.querySelector('.cards'); const asideDetails = document.querySelector('.aside__details'); const searchForm = document.querySelector('.aside__form');

A szereplők bélyegképeinek (.card elem) megjelenítéséhez először végrehajtjuk a lekérdezést a külső file-ból, leválogatjuk az élő személyeket és név szerint sorba rendezzük őket. Ezt követően a JS ezen adatok alapján beírja a DOM-ba a képeket. A fetch() promise lekezelésére itt egy async await függvényt használunk.

async function showImages(url) { const response = await fetch(url); const result = await response.json(); const resultLive = result.filter(item => !item.dead); resultLive.sort(function (a, b) { var nameA = a.name.toUpperCase(); var nameB = b.name.toUpperCase(); if (nameA < nameB) { return -1; } if (nameA > nameB) { return 1; } return 0; }); createCardElements(resultLive); } showImages('../json/got.json')

A képek megrajzolása mellett minden egyes képre eseményfigyelőt is teszünk, mely alapján fog megtörténni a honlap jobb oldalán az aside elem megrajzolása valamint részletes tartalommal való feltöltése.

function createCardElements(elements) { elements.forEach(element => { const card = document.createElement('div'); card.classList.add('card'); card.innerHTML = ` <div class="card__img"> <img src="../${element.portrait}" alt="${element.name}"> </div> <div class="card__name">${element.name}</div> ` cards.append(card); card.addEventListener('click', () => { document.querySelectorAll('.card').forEach(card => card.classList.remove('active')); card.classList.add('active'); createAsideElement(element); }) }); }

A részletes adatok megrajzolásánál figyeljük, hogy van-e képünk vagy címerünk az adott szereplőhöz, mert ha nincs akkor egy placeholder képet jelenítünk meg.

function createAsideElement(element) { if (element) { asideDetails.innerHTML = ` <div class="aside__details-img"> ${(element.picture) ? `<img src="../${element.picture}" alt="${element.name}">` : `<img src="../assets/image-placeholder.svg">` } </aside__details-img> <div class="aside__details-name-arms"> <span>${element.name}</span> ${(element.house || element.organization) ? `<img src="../assets/houses/${element.house ? element.house : element.organization}.png" alt="${element.name}">` : `<img src="../assets/image-placeholder.svg" style="width:50px;">` } </div> <div class="aside__details-short">${element.bio}</div> ` } else { asideDetails.innerHTML = ` <p>Character not found</p> ` } }

Végül a keresést oldjuk meg, ahol teljes név egyezőségre keresünk incasesensitive módon és az eredményt megjelenítjük és kereső mezőt töröljük. A fetch() promise feldolgozását most a .then() módszerrel végezzük.

searchForm.addEventListener('submit', (event) => { event.preventDefault(); const search = document.querySelector('input').value; fetch('../json/got.json') .then(response => response.json()) .then(data => { const searchArray = data.filter(item => item.name.toLocaleUpperCase() === search.toLocaleUpperCase()); if (searchArray) { createAsideElement(searchArray[0]); document.querySelector('input').value = ''; } }) })

A teljes kód itt.