LAB-04
146
labs/lab-04/dishes.js
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
const dishes = [
|
||||
{
|
||||
keyword: "mushroom-soup",
|
||||
name: "Грибной крем-суп",
|
||||
price: 280,
|
||||
category: "soup",
|
||||
count: "300 мл",
|
||||
image: "img/soup/mushroom-soup.jpg"
|
||||
},
|
||||
{
|
||||
keyword: "beet-soup",
|
||||
name: "Традиционный борщ",
|
||||
price: 240,
|
||||
category: "soup",
|
||||
count: "350 мл",
|
||||
image: "img/soup/beet-soup.jpg"
|
||||
},
|
||||
{
|
||||
keyword: "chicken-noodle-soup",
|
||||
name: "Куриный бульон с лапшой",
|
||||
price: 220,
|
||||
category: "soup",
|
||||
count: "350 мл",
|
||||
image: "img/soup/chicken-noodle-soup.jpg"
|
||||
},
|
||||
{
|
||||
keyword: "minestrone-soup",
|
||||
name: "Итальянский суп минестроне",
|
||||
price: 260,
|
||||
category: "soup",
|
||||
count: "350 мл",
|
||||
image: "img/soup/minestrone-soup.jpg"
|
||||
},
|
||||
{
|
||||
keyword: "tomato-soup",
|
||||
name: "Томатный крем-суп",
|
||||
price: 250,
|
||||
category: "soup",
|
||||
count: "300 мл",
|
||||
image: "img/soup/tomato-soup.jpg"
|
||||
},
|
||||
{
|
||||
keyword: "pumpkin-soup",
|
||||
name: "Тыквенный суп с имбирем",
|
||||
price: 270,
|
||||
category: "soup",
|
||||
count: "300 мл",
|
||||
image: "img/soup/pumpkin-soup.jpg"
|
||||
},
|
||||
{
|
||||
keyword: "vegan-bowl-chickpeas",
|
||||
name: "Веганский боул с нутом",
|
||||
price: 390,
|
||||
category: "main-course",
|
||||
count: "350 г",
|
||||
image: "img/main-dishes/vegan-bowl-chickpeas.jpg"
|
||||
},
|
||||
{
|
||||
keyword: "grilled-chicken-vegetables",
|
||||
name: "Куриная грудка с овощами гриль",
|
||||
price: 450,
|
||||
category: "main-course",
|
||||
count: "350 г",
|
||||
image: "img/main-dishes/grilled-chicken-vegetables.jpg"
|
||||
},
|
||||
{
|
||||
keyword: "salmon-quinoa-avocado",
|
||||
name: "Лосось с киноа и авокадо",
|
||||
price: 620,
|
||||
category: "main-course",
|
||||
count: "380 г",
|
||||
image: "img/main-dishes/salmon-quinoa-avocado.jpg"
|
||||
},
|
||||
{
|
||||
keyword: "seafood-pasta",
|
||||
name: "Паста с морепродуктами",
|
||||
price: 550,
|
||||
category: "main-course",
|
||||
count: "330 г",
|
||||
image: "img/main-dishes/seafood-pasta.jpg"
|
||||
},
|
||||
{
|
||||
keyword: "teriyaki-chicken-noodles",
|
||||
name: "Рисовая лапша с курицей терияки",
|
||||
price: 420,
|
||||
category: "main-course",
|
||||
count: "340 г",
|
||||
image: "img/main-dishes/teriyaki-chicken-noodles.jpg"
|
||||
},
|
||||
{
|
||||
keyword: "beef-salad",
|
||||
name: "Теплый салат с говядиной",
|
||||
price: 490,
|
||||
category: "main-course",
|
||||
count: "320 г",
|
||||
image: "img/main-dishes/beef-salad.jpg"
|
||||
},
|
||||
{
|
||||
keyword: "berry-juice",
|
||||
name: "Ягодный морс",
|
||||
price: 120,
|
||||
category: "drink",
|
||||
count: "300 мл",
|
||||
image: "img/drinks/berry-juice.jpg"
|
||||
},
|
||||
{
|
||||
keyword: "water",
|
||||
name: "Минеральная вода",
|
||||
price: 80,
|
||||
category: "drink",
|
||||
count: "500 мл",
|
||||
image: "img/drinks/water.jpg"
|
||||
},
|
||||
{
|
||||
keyword: "spinach-smoothie",
|
||||
name: "Зеленый смузи с шпинатом",
|
||||
price: 180,
|
||||
category: "drink",
|
||||
count: "350 мл",
|
||||
image: "img/drinks/spinach-smoothie.jpg"
|
||||
},
|
||||
{
|
||||
keyword: "ginger-lemonade",
|
||||
name: "Имбирный лимонад",
|
||||
price: 140,
|
||||
category: "drink",
|
||||
count: "300 мл",
|
||||
image: "img/drinks/ginger-lemonade.jpg"
|
||||
},
|
||||
{
|
||||
keyword: "mango-smoothie",
|
||||
name: "Смузи манго-маракуйя",
|
||||
price: 170,
|
||||
category: "drink",
|
||||
count: "350 мл",
|
||||
image: "img/drinks/mango-smoothie.jpg"
|
||||
},
|
||||
{
|
||||
keyword: "orange-juice",
|
||||
name: "Свежевыжатый апельсиновый сок",
|
||||
price: 150,
|
||||
category: "drink",
|
||||
count: "300 мл",
|
||||
image: "img/drinks/orange-juice.jpg"
|
||||
}
|
||||
];
|
||||
BIN
labs/lab-04/img/drinks/berry-juice.jpg
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
labs/lab-04/img/drinks/ginger-lemonade.jpg
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
labs/lab-04/img/drinks/mango-smoothie.jpg
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
labs/lab-04/img/drinks/orange-juice.jpg
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
labs/lab-04/img/drinks/spinach-smoothie.jpg
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
labs/lab-04/img/drinks/water.jpg
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
labs/lab-04/img/main-dishes/beef-salad.jpg
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
labs/lab-04/img/main-dishes/grilled-chicken-vegetables.jpg
Normal file
|
After Width: | Height: | Size: 81 KiB |
BIN
labs/lab-04/img/main-dishes/salmon-quinoa-avocado.jpg
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
labs/lab-04/img/main-dishes/seafood-pasta.jpg
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
labs/lab-04/img/main-dishes/teriyaki-chicken-noodles.jpg
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
labs/lab-04/img/main-dishes/vegan-bowl-chickpeas.jpg
Normal file
|
After Width: | Height: | Size: 80 KiB |
BIN
labs/lab-04/img/main.jpg
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
labs/lab-04/img/soup/beet-soup.jpg
Normal file
|
After Width: | Height: | Size: 108 KiB |
BIN
labs/lab-04/img/soup/chicken-noodle-soup.jpg
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
labs/lab-04/img/soup/minestrone-soup.jpg
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
labs/lab-04/img/soup/mushroom-soup.jpg
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
labs/lab-04/img/soup/pumpkin-soup.jpg
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
labs/lab-04/img/soup/tomato-soup.jpg
Normal file
|
After Width: | Height: | Size: 69 KiB |
101
labs/lab-04/index.html
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="icon" href="https://deev.space/media/favicon.ico" type="image/x-icon">
|
||||
<title>ЭкоЛанч - Доставка здоровых бизнес-ланчей в Москве</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>ЭкоЛанч</h1>
|
||||
<nav>
|
||||
<a href="index.html" id="active">Главная</a>
|
||||
<a href="menu.html">Собрать ланч</a>
|
||||
<a href="delivery.html">Доставка</a>
|
||||
<a href="about.html">О нас</a>
|
||||
<a href="#contacts">Контакты</a>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<section class="about-company">
|
||||
<h2>О компании ЭкоЛанч</h2>
|
||||
<img src="img/main.jpg" alt="Здоровая еда в контейнерах">
|
||||
<p>ЭкоЛанч - это современная компания по доставке здоровых бизнес-ланчей в офисы Москвы. Наша миссия - сделать правильное питание доступным для занятых людей, которые ценят свое время и здоровье.</p>
|
||||
<p>Мы используем только свежие продукты от проверенных поставщиков, а наши повара готовят блюда каждое утро. Все ланчи упакованы в экологичную биоразлагаемую упаковку, потому что мы заботимся не только о вашем здоровье, но и о здоровье нашей планеты.</p>
|
||||
</section>
|
||||
|
||||
<section class="advantages">
|
||||
<h2>Наши преимущества</h2>
|
||||
<ul>
|
||||
<li>Быстрая доставка в течение 60 минут по всей Москве в пределах МКАД</li>
|
||||
<li>Свежие блюда, приготовленные утром из качественных продуктов</li>
|
||||
<li>Сбалансированное меню, разработанное профессиональными диетологами</li>
|
||||
<li>Экологичная упаковка, безопасная для окружающей среды</li>
|
||||
<li>Гибкая система скидок для корпоративных клиентов и постоянных заказчиков</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="popular-dishes">
|
||||
<h2>Самые популярные блюда</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Название блюда</th>
|
||||
<th>Описание</th>
|
||||
<th>Калорийность</th>
|
||||
<th>Цена</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Куриная грудка с овощами гриль</td>
|
||||
<td>Нежная куриная грудка с цукини, баклажанами и болгарским перцем</td>
|
||||
<td>420 ккал</td>
|
||||
<td>450 руб.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Лосось с киноа и авокадо</td>
|
||||
<td>Запеченный лосось с гарниром из киноа и свежим авокадо</td>
|
||||
<td>520 ккал</td>
|
||||
<td>620 руб.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Теплый салат с говядиной</td>
|
||||
<td>Сочная говядина с микс-салатом, томатами черри и кедровыми орешками</td>
|
||||
<td>380 ккал</td>
|
||||
<td>490 руб.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Веганский боул с нутом</td>
|
||||
<td>Запеченный нут, хумус, овощи и тахини соус</td>
|
||||
<td>450 ккал</td>
|
||||
<td>390 руб.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Паста с морепродуктами</td>
|
||||
<td>Паста из твердых сортов пшеницы с креветками, мидиями и томатным соусом</td>
|
||||
<td>480 ккал</td>
|
||||
<td>550 руб.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Рисовая лапша с курицей терияки</td>
|
||||
<td>Рисовая лапша wok с куриным филе в соусе терияки и овощами</td>
|
||||
<td>460 ккал</td>
|
||||
<td>420 руб.</td>
|
||||
</tr>
|
||||
</table>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer id="contacts">
|
||||
<p><b>Контактная информация</b></p>
|
||||
<p>Телефон: <a href="tel:+79993737737">+7 (999) 373-77-37</a></p>
|
||||
<p>Email: <a href="mailto:egor@deev.space">egor@deev.space</a></p>
|
||||
<p>Адрес: г. Москва, ул. Михалковская, д. 7, к. 1, офис 813А</p>
|
||||
<p>Режим работы: Пн-Пт с 8:00 до 20:00</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
114
labs/lab-04/menu.html
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="icon" href="https://deev.space/media/favicon.ico" type="image/x-icon">
|
||||
<title>Собрать ланч - ЭкоЛанч</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>ЭкоЛанч</h1>
|
||||
<nav>
|
||||
<a href="index.html">Главная</a>
|
||||
<a href="menu.html" id="active">Собрать ланч</a>
|
||||
<a href="delivery.html">Доставка</a>
|
||||
<a href="about.html">О нас</a>
|
||||
<a href="#contacts">Контакты</a>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<section>
|
||||
<h2>Супы</h2>
|
||||
<div class="dishes-grid" id="soup-section"></div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Главные блюда</h2>
|
||||
<div class="dishes-grid" id="main-course-section"></div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Напитки</h2>
|
||||
<div class="dishes-grid" id="drink-section"></div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<section class="order-form">
|
||||
<h2>Оформить заказ</h2>
|
||||
<form action="https://httpbin.org/post" method="POST">
|
||||
<div class="form-container">
|
||||
|
||||
<div class="order-section">
|
||||
<h3>Ваш заказ</h3>
|
||||
<div id="order-summary">
|
||||
<p class="empty-order">Ничего не выбрано</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="customer-section">
|
||||
<h3>Данные для доставки</h3>
|
||||
|
||||
<label for="name">Имя</label>
|
||||
<input type="text" name="name" id="name" placeholder="Введите ваше имя" required>
|
||||
|
||||
<label for="email">Email</label>
|
||||
<input type="email" name="email" id="email" placeholder="Введите ваш email" required>
|
||||
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" name="subscribe" id="subscribe" checked>
|
||||
<label for="subscribe">Подписаться на рассылку</label>
|
||||
</div>
|
||||
|
||||
<label for="phone">Телефон</label>
|
||||
<input type="tel" name="phone" id="phone" placeholder="Введите ваш телефон" required>
|
||||
|
||||
<label for="address">Адрес доставки</label>
|
||||
<input type="text" name="address" id="address" placeholder="Введите адрес доставки" required>
|
||||
<small class="form-hint">Доставка осуществляется только по Москве</small>
|
||||
|
||||
<div class="radio-group">
|
||||
<label>Время доставки</label>
|
||||
<div>
|
||||
<input type="radio" name="delivery-time-type" id="asap" value="asap" required>
|
||||
<label for="asap">Как можно скорее</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="radio" name="delivery-time-type" id="specific-time" value="specific-time" required>
|
||||
<label for="specific-time">К определенному времени</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label for="delivery-time">Указать время</label>
|
||||
<input type="time" name="delivery-time" id="delivery-time" min="07:00" max="23:00" step="300">
|
||||
<small class="form-hint">Доступное время доставки с 7:00 до 23:00</small>
|
||||
|
||||
<label for="comment">Комментарий к заказу</label>
|
||||
<textarea name="comment" id="comment" placeholder="Введите комментарий к заказу"></textarea>
|
||||
|
||||
<div class="form-buttons">
|
||||
<button type="reset">Сбросить</button>
|
||||
<button type="submit">Отправить заказ</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<footer id="contacts">
|
||||
<p><b>Контактная информация</b></p>
|
||||
<p>Телефон: <a href="tel:+79993737737">+7 (999) 373-77-37</a></p>
|
||||
<p>Email: <a href="mailto:egor@deev.space">egor@deev.space</a></p>
|
||||
<p>Адрес: г. Москва, ул. Михалковская, д. 7, к. 1, офис 813А</p>
|
||||
<p>Режим работы: Пн-Пт с 8:00 до 20:00</p>
|
||||
</footer>
|
||||
|
||||
<script src="dishes.js"></script>
|
||||
<script src="menu.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
158
labs/lab-04/menu.js
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
const selectedDishes = {
|
||||
soup: null,
|
||||
'main-course': null,
|
||||
drink: null
|
||||
};
|
||||
|
||||
function sortDishes() {
|
||||
dishes.sort(function(a, b) {
|
||||
return a.name.localeCompare(b.name, 'ru');
|
||||
});
|
||||
}
|
||||
|
||||
function displayDishes() {
|
||||
const soupSection = document.getElementById('soup-section');
|
||||
const mainCourseSection = document.getElementById('main-course-section');
|
||||
const drinkSection = document.getElementById('drink-section');
|
||||
|
||||
soupSection.innerHTML = '';
|
||||
mainCourseSection.innerHTML = '';
|
||||
drinkSection.innerHTML = '';
|
||||
|
||||
dishes.forEach(function(dish) {
|
||||
const dishCard = createDishCard(dish);
|
||||
|
||||
if (dish.category === 'soup') {
|
||||
soupSection.insertAdjacentHTML('beforeend', dishCard);
|
||||
} else if (dish.category === 'main-course') {
|
||||
mainCourseSection.insertAdjacentHTML('beforeend', dishCard);
|
||||
} else if (dish.category === 'drink') {
|
||||
drinkSection.insertAdjacentHTML('beforeend', dishCard);
|
||||
}
|
||||
});
|
||||
|
||||
addDishClickHandlers();
|
||||
}
|
||||
|
||||
function createDishCard(dish) {
|
||||
return `
|
||||
<div class="dish-card" data-dish="${dish.keyword}">
|
||||
<img src="${dish.image}" alt="${dish.name}">
|
||||
<p class="dish-price">${dish.price} руб.</p>
|
||||
<p class="dish-name">${dish.name}</p>
|
||||
<p class="dish-weight">${dish.count}</p>
|
||||
<button>Добавить</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function addDishClickHandlers() {
|
||||
const dishCards = document.querySelectorAll('.dish-card');
|
||||
|
||||
dishCards.forEach(function(card) {
|
||||
card.addEventListener('click', function() {
|
||||
const keyword = this.dataset.dish;
|
||||
const dish = dishes.find(function(d) {
|
||||
return d.keyword === keyword;
|
||||
});
|
||||
|
||||
if (dish) {
|
||||
selectDish(dish);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function selectDish(dish) {
|
||||
selectedDishes[dish.category] = dish;
|
||||
updateOrderSummary();
|
||||
}
|
||||
|
||||
function updateOrderSummary() {
|
||||
const orderSummary = document.getElementById('order-summary');
|
||||
const hasSelection = selectedDishes.soup ||
|
||||
selectedDishes['main-course'] ||
|
||||
selectedDishes.drink;
|
||||
|
||||
if (!hasSelection) {
|
||||
orderSummary.innerHTML = '<p class="empty-order">Ничего не выбрано</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
let summaryHTML = '';
|
||||
let totalPrice = 0;
|
||||
|
||||
if (selectedDishes.soup) {
|
||||
summaryHTML += `
|
||||
<div class="order-category">
|
||||
<h3>Суп</h3>
|
||||
<div class="order-item">
|
||||
<span>${selectedDishes.soup.name}</span>
|
||||
<span>${selectedDishes.soup.price} руб.</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
totalPrice += selectedDishes.soup.price;
|
||||
} else {
|
||||
summaryHTML += `
|
||||
<div class="order-category">
|
||||
<h3>Суп</h3>
|
||||
<p class="not-selected">Блюдо не выбрано</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
if (selectedDishes['main-course']) {
|
||||
summaryHTML += `
|
||||
<div class="order-category">
|
||||
<h3>Главное блюдо</h3>
|
||||
<div class="order-item">
|
||||
<span>${selectedDishes['main-course'].name}</span>
|
||||
<span>${selectedDishes['main-course'].price} руб.</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
totalPrice += selectedDishes['main-course'].price;
|
||||
} else {
|
||||
summaryHTML += `
|
||||
<div class="order-category">
|
||||
<h3>Главное блюдо</h3>
|
||||
<p class="not-selected">Блюдо не выбрано</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
if (selectedDishes.drink) {
|
||||
summaryHTML += `
|
||||
<div class="order-category">
|
||||
<h3>Напиток</h3>
|
||||
<div class="order-item">
|
||||
<span>${selectedDishes.drink.name}</span>
|
||||
<span>${selectedDishes.drink.price} руб.</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
totalPrice += selectedDishes.drink.price;
|
||||
} else {
|
||||
summaryHTML += `
|
||||
<div class="order-category">
|
||||
<h3>Напиток</h3>
|
||||
<p class="not-selected">Напиток не выбран</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
summaryHTML += `
|
||||
<div class="order-total">
|
||||
<h3>Стоимость заказа</h3>
|
||||
<p class="total-price">${totalPrice} руб.</p>
|
||||
</div>
|
||||
`;
|
||||
|
||||
orderSummary.innerHTML = summaryHTML;
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
sortDishes();
|
||||
displayDishes();
|
||||
});
|
||||
36
labs/lab-04/other/Вопросы для подготовки к защите ЛР №4.txt
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
JavaScript (основы)
|
||||
1. Что значит “JS это слабо типизированный язык”?
|
||||
|
||||
2. Что значит “JS язык с динамической типизацией”?
|
||||
|
||||
3. Какие типы значений есть в JS?
|
||||
|
||||
4. Чем отличаются строки "", ” и “?
|
||||
|
||||
5. Что такое операнд и оператор?
|
||||
|
||||
6. Какие операторы есть в JS?
|
||||
|
||||
7. Различие var, let и const.
|
||||
|
||||
8. Как проверяются условия в JS?
|
||||
|
||||
9. Какие есть циклы в JS?
|
||||
|
||||
10. Как создаются функции?
|
||||
|
||||
11. Как хранятся данные в массиве?
|
||||
|
||||
12. Какие методы массивов вы знаете?
|
||||
|
||||
13. Как обратиться к элементу массива?
|
||||
|
||||
14. Как можно перебирать элементы массива?
|
||||
|
||||
15. Что такое объект?
|
||||
|
||||
16. Как получить значение из свойства объекта?
|
||||
|
||||
17. Для чего используется объект Math?
|
||||
|
||||
18. Приведите примеры свойств и методов в Math.
|
||||
83
labs/lab-04/other/Задание - Лабораторная работа 4.md
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
# Лабораторная работа №4. Отображение и сортировка блюд, добавление в корзину
|
||||
|
||||
---
|
||||
Отобразите блюда на странице с помощью JavaScript. Создайте скрипт, позволяющий выбрать блюдо и добавить его в раздел формы "Ваш заказ". Реализуйте подсчет итоговой стоимости. Отсортируйте блюда в алфавитном порядке.
|
||||
|
||||
## Порядок выполнения
|
||||
|
||||
Примерный макет раздела "Ваш заказ":
|
||||
|
||||
Изначально на странице не должно быть элементов с карточками блюд. Необходимо написать скрипт, который будет перебирать массив с блюдами, динамически создавать соответствующие им HTML-элементы, и при помощи DOM добавлять каждый из них в нужную секцию на странице.
|
||||
1. Создайте массив объектов, в котором будут храниться все существующие блюда. У каждого объекта должны быть следующие свойства:
|
||||
- keyword (хранит название блюда на латинице, должно быть уникальным для каждого блюда)
|
||||
- name (название блюда)
|
||||
- price (цена)
|
||||
- category (категория на английском, например, "soup")
|
||||
- count (вес/объем)
|
||||
- image (путь к изображению, например "soups/gazpacho")
|
||||
|
||||
Пример объекта:
|
||||
Массив должен храниться в отдельном js-файле.
|
||||
|
||||
2. Реализуйте отображение блюд на страницу с помощью JavaScript.
|
||||
Необходимо написать скрипт, который будет перебирать массив с блюдами и выводить каждое из них в нужную секцию. Секции должны выглядеть также, как при статичном выводе из прошлых лабораторных.
|
||||
* У каждого блока с блюдом должен быть data-атрибут "data-dish", в котором будет храниться название блюда на латинице.
|
||||
|
||||
Скрипт отображения должен храниться в отдельном js-файле.
|
||||
|
||||
3. Создайте скрипт, позволяющий выбрать блюдо и добавить его в форму "Сделать заказ".
|
||||
|
||||
- При клике на карточку с блюдом, его название и цена должны появляться в разделе формы "Ваш заказ". Блюдо должно отображаться в своей категории.
|
||||
Используйте data-атрибут, чтобы найти блюдо в массиве.
|
||||
Например, пользователь выбрал суп "Гаспачо". Вот как должен выглядеть раздел "Ваш заказ"
|
||||
- Если затем пользователь выбрал другой суп, он должен отобразиться вместо "Гаспачо"
|
||||
- Если блюдо из одной категории добавлено, а из другой нет, в пустой категории должно быть выведено сообщение: "Блюдо не выбрано" (или "Напиток не выбран" для раздела с напитками).
|
||||
Например, пользователь выбрал суп, но не выбрал основное блюдо и напиток
|
||||
- Если еще ничего не было выбрано, в блоке должен отображаться текст "Ничего не выбрано" (названия категорий скрыты)
|
||||
- Вот как выглядит блок, если были выбраны блюда из каждой категории
|
||||
|
||||
4. Реализуйте подсчет итоговой стоимости для всех выбранных позиций меню.
|
||||
|
||||
- После блоков с выбранными блюдами должен располагаться блок "Стоимость заказа".
|
||||
В блоке отображается итоговая стоимость всех блюд. Например, если был выбран только напиток, отобразится его цена
|
||||
- Так выглядит блок, если выбраны блюда из каждой категории
|
||||
- Если блюда не были выбраны, блок "Стоимость заказа" скрыт также, как и блоки с категориями.
|
||||
|
||||
5. Отсортируйте блюда каждой категории в алфавитном порядке.
|
||||
|
||||
- Используйте метод sort() перед отображением блюд на страницу. Блюда должны быть отсортированы в алфавитном порядке.
|
||||
|
||||
Данные о выбранных блюдах можно не отправлять при отправке формы (остальные данные формы должны отправляться).
|
||||
|
||||
### Материалы для изучения
|
||||
|
||||
#### HTML
|
||||
[Атрибуты data-* [Doka]](https://doka.guide/html/data-attributes/)
|
||||
[Скрытые поля [MDN]](https://developer.mozilla.org/ru/docs/Web/HTML/Element/input/hidden)
|
||||
#### JS
|
||||
[Современный учебник JavaScript [JS.RU]](https://learn.javascript.ru/)
|
||||
[JavaScript [MDN]](https://developer.mozilla.org/ru/docs/Web/JavaScript)
|
||||
[JavaScript [Doka]](https://doka.guide/js/)
|
||||
[Переменные const, let и var [Doka]](https://doka.guide/js/var-let/)
|
||||
[if...else [Doka]](https://doka.guide/js/if-else/)
|
||||
[forEach() [Doka]](https://doka.guide/js/array-foreach/)
|
||||
[Число [Doka]](https://doka.guide/js/number/)
|
||||
[Строка [Doka]](https://doka.guide/js/string/)
|
||||
[Булев тип [Doka]](https://doka.guide/js/boolean/)
|
||||
[undefined [Doka]](https://doka.guide/js/undefined/)
|
||||
[console.log() [Doka]](https://doka.guide/js/console-log/)
|
||||
[textContent [Doka]](https://doka.guide/js/element-textcontent/)
|
||||
[Массив [Doka]](https://doka.guide/js/arrays/)
|
||||
[Объект [Doka]](https://doka.guide/js/object/)
|
||||
[Введение в DOM [MDN]](https://developer.mozilla.org/ru/docs/Web/API/Document_Object_Model/Introduction)
|
||||
[DOM [Doka]](https://doka.guide/js/dom/)
|
||||
[Using the Document Object Model [MDN]](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Using_the_Document_Object_Model)
|
||||
[querySelector() [Doka]](https://doka.guide/js/query-selector/)
|
||||
[querySelectorAll() [Doka]](https://doka.guide/js/query-selector-all/)
|
||||
[dataset [Doka]](https://doka.guide/js/element-dataset/)
|
||||
[addEventListener() [Doka]](https://doka.guide/js/element-addeventlistener/)
|
||||
[HTMLElement: dataset property [MDN]](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset)
|
||||
[Style display Property [W3Schools]](https://www.w3schools.com/jsref/prop_style_display.asp)
|
||||
[Element.insertAdjacentHTML() [MDN]](https://developer.mozilla.org/ru/docs/Web/API/Element/insertAdjacentHTML)
|
||||
[Браузерное окружение, спецификации [JS.RU]](https://learn.javascript.ru/browser-environment)
|
||||
[sort() [Doka]](https://doka.guide/js/array-sort/)
|
||||
BIN
labs/lab-04/other/Результат запроса.png
Normal file
|
After Width: | Height: | Size: 103 KiB |
504
labs/lab-04/styles.css
Normal file
|
|
@ -0,0 +1,504 @@
|
|||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: 'Montserrat', sans-serif;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
header {
|
||||
background-color: #2d5016;
|
||||
color: white;
|
||||
padding: 20px 40px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0 0 15px 0;
|
||||
font-size: 36px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
nav {
|
||||
margin: 0;
|
||||
padding: 10px 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
nav a {
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
font-family: 'Montserrat', sans-serif;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
nav a:hover {
|
||||
color: #a8d08d;
|
||||
}
|
||||
|
||||
nav a#active {
|
||||
color: tomato;
|
||||
}
|
||||
|
||||
main {
|
||||
margin: 0 auto;
|
||||
padding: 40px 60px;
|
||||
max-width: 1200px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
section {
|
||||
margin: 0 0 50px 0;
|
||||
padding: 30px;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 28px;
|
||||
color: #2d5016;
|
||||
margin: 0 0 20px 0;
|
||||
}
|
||||
|
||||
p {
|
||||
font-family: 'Montserrat', sans-serif;
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
line-height: 1.6;
|
||||
margin: 0 0 15px 0;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
object-fit: cover;
|
||||
margin: 0 0 20px 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 20px 0;
|
||||
padding: 0 0 0 25px;
|
||||
}
|
||||
|
||||
ul li {
|
||||
font-family: 'Montserrat', sans-serif;
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
margin: 0 0 12px 0;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
table th {
|
||||
background-color: #2d5016;
|
||||
color: white;
|
||||
padding: 15px;
|
||||
text-align: left;
|
||||
border: 1px solid #2d5016;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
table td {
|
||||
padding: 12px 15px;
|
||||
border: 1px solid #ddd;
|
||||
text-align: left;
|
||||
font-size: 15px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
table tr:nth-child(even) {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
.dishes-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.dish-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 30px 40px;
|
||||
border-radius: 35px;
|
||||
cursor: pointer;
|
||||
filter: drop-shadow(17px 19px 24px rgba(0, 0, 0, 0.13));
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.dish-card:hover {
|
||||
border: 2px solid tomato;
|
||||
}
|
||||
|
||||
.dish-card:hover button {
|
||||
background-color: tomato;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.dish-card img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
border-radius: 35px;
|
||||
margin: 0 0 15px 0;
|
||||
}
|
||||
|
||||
.dish-price {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
.dish-name {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
.dish-weight {
|
||||
color: #888;
|
||||
margin: 0 0 10px 0;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.dish-card button {
|
||||
background-color: #f1eee9;
|
||||
border: none;
|
||||
padding: 10px 30px;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
font-family: 'Montserrat', sans-serif;
|
||||
font-size: 16px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.order-form {
|
||||
margin: 0 auto;
|
||||
padding: 40px 60px;
|
||||
max-width: 1200px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.order-form h2 {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
#order-summary {
|
||||
background-color: #f9f9f9;
|
||||
padding: 30px;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.empty-order {
|
||||
text-align: center;
|
||||
color: #888;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.order-category {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.order-category h3 {
|
||||
font-size: 20px;
|
||||
color: #2d5016;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
.order-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 10px 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.order-item span:first-child {
|
||||
font-weight: 600;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
max-width: 70%;
|
||||
}
|
||||
|
||||
.order-item span:last-child {
|
||||
color: #2d5016;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.not-selected {
|
||||
color: #888;
|
||||
font-style: italic;
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.order-total {
|
||||
margin-top: 30px;
|
||||
padding-top: 20px;
|
||||
border-top: 2px solid #2d5016;
|
||||
}
|
||||
|
||||
.order-total h3 {
|
||||
font-size: 22px;
|
||||
color: #2d5016;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
.total-price {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
color: #2d5016;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.form-container {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 40px;
|
||||
}
|
||||
|
||||
.order-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.order-section h3 {
|
||||
font-size: 22px;
|
||||
color: #2d5016;
|
||||
margin: 0 0 20px 0;
|
||||
}
|
||||
|
||||
.customer-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.customer-section h3 {
|
||||
font-size: 22px;
|
||||
color: #2d5016;
|
||||
margin: 0 0 20px 0;
|
||||
}
|
||||
|
||||
.order-form label {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
margin: 0 0 8px 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.order-form select,
|
||||
.order-form input[type="text"],
|
||||
.order-form input[type="email"],
|
||||
.order-form input[type="tel"],
|
||||
.order-form input[type="time"],
|
||||
.order-form textarea {
|
||||
width: 100%;
|
||||
padding: 12px 15px;
|
||||
margin: 0 0 20px 0;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
font-family: 'Montserrat', sans-serif;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.order-form select {
|
||||
cursor: pointer;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.order-form textarea {
|
||||
height: 100px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.checkbox-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0 0 20px 0;
|
||||
}
|
||||
|
||||
.checkbox-group input[type="checkbox"] {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin: 0 10px 0 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.checkbox-group label {
|
||||
margin: 0;
|
||||
font-weight: 400;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.radio-group {
|
||||
margin: 0 0 20px 0;
|
||||
}
|
||||
|
||||
.radio-group > label {
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.radio-group > div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0 0 8px 0;
|
||||
}
|
||||
|
||||
.radio-group input[type="radio"] {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin: 0 10px 0 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.radio-group input[type="radio"] + label {
|
||||
margin: 0;
|
||||
font-weight: 400;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.form-buttons {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.form-buttons button {
|
||||
flex: 1;
|
||||
padding: 15px 30px;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
font-family: 'Montserrat', sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.form-buttons button[type="reset"] {
|
||||
background-color: #f1eee9;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.form-buttons button[type="reset"]:hover {
|
||||
background-color: #e0ddd8;
|
||||
}
|
||||
|
||||
.form-buttons button[type="submit"] {
|
||||
background-color: #2d5016;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.form-buttons button[type="submit"]:hover {
|
||||
background-color: #3d6020;
|
||||
}
|
||||
|
||||
.form-hint {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
margin: -15px 0 20px 0;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
footer {
|
||||
background-color: black;
|
||||
color: white;
|
||||
padding: 30px 60px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
footer p {
|
||||
color: white;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
footer a {
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
font-family: 'Montserrat', sans-serif;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
footer a:hover {
|
||||
color: #a8d08d;
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
.dishes-grid {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
nav {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.about-company img {
|
||||
width: 500px;
|
||||
}
|
||||
|
||||
.form-container {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
nav {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
nav a {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
section h2 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.dishes-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.about-company img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table th,
|
||||
table td {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.form-container {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.form-buttons {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
table th,
|
||||
table td {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
}
|
||||