A Trónok harca szereplőinek megjelenítése egy külső file-ban tárolt adatok alapján.
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>
* {
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 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 = '';
}
})
})