77 changed files with 47358 additions and 92 deletions
@ -0,0 +1,40 @@ |
|||||
|
# 01 - Úvod do HTML |
||||
|
|
||||
|
## 🎯 Co se naučíte |
||||
|
|
||||
|
V této lekci se seznámíte se základy HTML a strukturou webové stránky. |
||||
|
|
||||
|
## 📚 Témata lekce |
||||
|
|
||||
|
- **Základní struktura HTML dokumentu** (`<!DOCTYPE html>`, `<html>`, `<head>`, `<body>`) |
||||
|
- **Nadpisy** (`<h1>` až `<h6>`) |
||||
|
- **Odstavce** (`<p>`) |
||||
|
- **Odkazy** (`<a href="">`) |
||||
|
- **Obrázky** (`<img src="" alt="">`) |
||||
|
- **Komentáře v HTML** (`<!-- komentář -->`) |
||||
|
- **Zalomení řádku** (`<br>`) |
||||
|
|
||||
|
## 📂 Soubory v projektu |
||||
|
|
||||
|
- `index.html` - hlavní HTML soubor s ukázkami základních HTML tagů |
||||
|
- `about.html` - další stránka pro demonstraci odkazů |
||||
|
- `profily/` - složka s příklady vnořené struktury souborů |
||||
|
|
||||
|
## 🚀 Jak s lekcí pracovat |
||||
|
|
||||
|
1. Otevřete `index.html` v prohlížeči (dvojklik nebo přes Live Server) |
||||
|
2. Prohlédněte si HTML kód v editoru |
||||
|
3. Zkuste upravit texty, přidat nové nadpisy nebo odstavce |
||||
|
4. Vyzkoušejte vytvořit vlastní odkazy na nové stránky |
||||
|
|
||||
|
## 💡 Tip pro studenty |
||||
|
|
||||
|
- Používejte komentáře (`<!-- -->`) pro poznámky v kódu |
||||
|
- Zkratka pro zakomentování: `CTRL+K CTRL+C` (odkomentování: `CTRL+K CTRL+U`) |
||||
|
- Každý tag, který otevřete, musíte i zavřít (např. `<p>...</p>`) |
||||
|
- Atribut `alt` u obrázků je povinný (popisuje obrázek) |
||||
|
|
||||
|
## 🔗 Související lekce |
||||
|
|
||||
|
- **Další:** 02 - pokračování v HTML |
||||
|
- **Související:** 03_css_uvod - stylování HTML elementů |
||||
@ -0,0 +1,124 @@ |
|||||
|
# 02 - HTML Pokročilé Elementy |
||||
|
|
||||
|
## 🎯 Co se naučíte |
||||
|
|
||||
|
V této lekci se naučíte pracovat s pokročilejšími HTML elementy a Emmet zkratkami. |
||||
|
|
||||
|
## 📚 Témata lekce |
||||
|
|
||||
|
- **Tabulky** (`<table>`, `<tr>`, `<td>`, `<th>`) |
||||
|
- Struktura tabulek |
||||
|
- Řádky (`<tr>`) a buňky (`<td>`) |
||||
|
- Nadpisy tabulek (`<th>`) |
||||
|
- Stylování pomocí inline CSS |
||||
|
- **Seznamy** (`<ul>`, `<ol>`, `<dl>`) |
||||
|
- Nečíslované seznamy (`<ul>`) |
||||
|
- Číslované seznamy (`<ol>`) |
||||
|
- Definiční seznamy (`<dl>`, `<dt>`, `<dd>`) |
||||
|
- **Emmet zkratky** - rychlé psaní HTML |
||||
|
- **ALT + klikání** - multi-cursor editing |
||||
|
|
||||
|
## 📂 Soubory v projektu |
||||
|
|
||||
|
- `index.html` - hlavní soubor s ukázkami |
||||
|
- `formular.html` - příklad formuláře |
||||
|
|
||||
|
## 💻 Ukázky z lekce |
||||
|
|
||||
|
### Tabulka: |
||||
|
```html |
||||
|
<table style="border: 2px black solid;"> |
||||
|
<tr> |
||||
|
<th>Jméno</th> |
||||
|
<th>Částka</th> |
||||
|
<th>Měna</th> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td>Martin</td> |
||||
|
<td>50</td> |
||||
|
<td>$</td> |
||||
|
</tr> |
||||
|
</table> |
||||
|
``` |
||||
|
|
||||
|
### Seznamy: |
||||
|
```html |
||||
|
<!-- Nečíslovaný seznam --> |
||||
|
<ul> |
||||
|
<li>První položka</li> |
||||
|
<li>Druhá položka</li> |
||||
|
</ul> |
||||
|
|
||||
|
<!-- Číslovaný seznam --> |
||||
|
<ol> |
||||
|
<li>První krok</li> |
||||
|
<li>Druhý krok</li> |
||||
|
</ol> |
||||
|
|
||||
|
<!-- Definiční seznam --> |
||||
|
<dl> |
||||
|
<dt>HTML</dt> |
||||
|
<dd>- HyperText Markup Language</dd> |
||||
|
<dt>CSS</dt> |
||||
|
<dd>- Cascading Style Sheets</dd> |
||||
|
</dl> |
||||
|
``` |
||||
|
|
||||
|
### Emmet zkratky: |
||||
|
``` |
||||
|
ul>li*5>a → vytvoří <ul> s 5 <li>, každý obsahuje <a> |
||||
|
table>tr*3>td*4 → vytvoří tabulku 3 řádky × 4 sloupce |
||||
|
p>a → vytvoří <p> s <a> uvnitř |
||||
|
html:5 nebo ! → vytvoří HTML5 šablonu |
||||
|
``` |
||||
|
|
||||
|
## 🚀 Jak s lekcí pracovat |
||||
|
|
||||
|
1. Otevřete `index.html` v prohlížeči |
||||
|
2. Prohlédněte si ukázky tabulek a seznamů |
||||
|
3. Zkuste vytvořit vlastní tabulku s více řádky a sloupci |
||||
|
4. Vyzkoušejte Emmet zkratky v editoru |
||||
|
5. Použijte **ALT + klikání** pro psaní na více místech najednou |
||||
|
|
||||
|
## 💡 Tip pro studenty |
||||
|
|
||||
|
### Struktura tabulky: |
||||
|
``` |
||||
|
<table> ← kontejner tabulky |
||||
|
<tr> ← řádek (Table Row) |
||||
|
<th> ← hlavička (Table Header) |
||||
|
<td> ← buňka (Table Data) |
||||
|
``` |
||||
|
|
||||
|
### Emmet zkratky - příklady: |
||||
|
- **`html:5`** nebo **`!`** - vytvoří HTML5 šablonu |
||||
|
- **`ul>li*5`** - vytvoří `<ul>` s 5 `<li>` elementy |
||||
|
- **`div.class#id`** - vytvoří `<div class="class" id="id">` |
||||
|
- **`table>tr*3>td*4`** - tabulka 3×4 |
||||
|
- **`h1+p+p`** - `<h1>` a dva `<p>` za sebou |
||||
|
|
||||
|
### Multi-cursor (ALT + klikání): |
||||
|
- Držte **ALT** a klikejte na různá místa |
||||
|
- Můžete psát text na více místech najednou |
||||
|
- Užitečné pro opakující se text |
||||
|
|
||||
|
### Inline CSS: |
||||
|
```html |
||||
|
<!-- Inline CSS přímo v elementu --> |
||||
|
<h1 style="color: lime; background-color: purple;">Nadpis</h1> |
||||
|
|
||||
|
<!-- Lepší je CSS v samostatném souboru (naučíte se v lekci 03) --> |
||||
|
``` |
||||
|
|
||||
|
## ⚠️ Důležité poznámky |
||||
|
|
||||
|
- Tabulky se dnes používají **pouze pro tabulková data**, ne pro layout |
||||
|
- Pro layout použijte **CSS Grid** nebo **Flexbox** (naučíte se později) |
||||
|
- **Emmet** je součástí většiny editorů (VS Code, Sublime, Atom) |
||||
|
- Inline CSS je OK pro testování, ale pro produkci použijte externí CSS |
||||
|
|
||||
|
## 🔗 Související lekce |
||||
|
|
||||
|
- **Předchozí:** 01_uvod - základy HTML |
||||
|
- **Další:** 03_css_uvod - stylování pomocí CSS |
||||
|
- **Emmet dokumentace:** [Emmet Cheat Sheet](https://docs.emmet.io/cheat-sheet/) |
||||
@ -0,0 +1,53 @@ |
|||||
|
# 03 - Úvod do CSS |
||||
|
|
||||
|
## 🎯 Co se naučíte |
||||
|
|
||||
|
V této lekci se naučíte základy CSS (Cascading Style Sheets) - stylování webových stránek. |
||||
|
|
||||
|
## 📚 Témata lekce |
||||
|
|
||||
|
- **Připojení CSS** k HTML (external, internal, inline) |
||||
|
- **CSS selektory** (element, třída `.class`, ID `#id`) |
||||
|
- **Barvy** (`color`, `background-color`) |
||||
|
- **Text styling** (velikost, barva, zarovnání) |
||||
|
- **Kombinace tříd** (více tříd na jednom elementu) |
||||
|
- **Stylování seznamů a tabulek** |
||||
|
|
||||
|
## 📂 Soubory v projektu |
||||
|
|
||||
|
- `index.html` - HTML dokument s různými elementy |
||||
|
- `style.css` - CSS soubor s definicemi stylů |
||||
|
|
||||
|
## 🎨 Ukázky v lekci |
||||
|
|
||||
|
```css |
||||
|
/* Stylování třídou */ |
||||
|
.fancyText { color: blue; } |
||||
|
|
||||
|
/* Stylování ID */ |
||||
|
#uniqueElement { font-size: 20px; } |
||||
|
|
||||
|
/* Kombinace tříd */ |
||||
|
.fancyText.fancyBG { ... } |
||||
|
``` |
||||
|
|
||||
|
## 🚀 Jak s lekcí pracovat |
||||
|
|
||||
|
1. Otevřete `index.html` v prohlížeči |
||||
|
2. Prozkoumejte soubor `style.css` - zde jsou definovány styly |
||||
|
3. V HTML najděte atributy `class=""` a `style=""` - propojení s CSS |
||||
|
4. Zkuste změnit barvy, velikosti nebo přidat nové třídy |
||||
|
5. Vyzkoušejte kombinovat více tříd na jednom elementu |
||||
|
|
||||
|
## 💡 Tip pro studenty |
||||
|
|
||||
|
- **Inline CSS** (`style=""` přímo v HTML) - použijte jen výjimečně |
||||
|
- **External CSS** (samostatný soubor) - nejlepší pro větší projekty |
||||
|
- **Třída** (`.class`) - pro opakující se styly (můžete použít vícekrát) |
||||
|
- **ID** (`#id`) - pro unikátní element (použijte jen jednou na stránce) |
||||
|
- Element může mít více tříd najednou: `class="fancyText fancyBG"` |
||||
|
|
||||
|
## 🔗 Související lekce |
||||
|
|
||||
|
- **Předchozí:** 01_uvod, 02 - základy HTML |
||||
|
- **Další:** 04_layout - pokročilejší layout techniky |
||||
@ -0,0 +1,45 @@ |
|||||
|
# 04 - CSS Layout |
||||
|
|
||||
|
## 🎯 Co se naučíte |
||||
|
|
||||
|
V této lekci se naučíte vytvářet layout (rozvržení) webové stránky pomocí CSS. |
||||
|
|
||||
|
## 📚 Témata lekce |
||||
|
|
||||
|
- **Box Model** (margin, padding, border) |
||||
|
- **Display property** (`block`, `inline`, `inline-block`) |
||||
|
- **Float** (obtékání elementů) |
||||
|
- **Clear** (ukončení obtékání) |
||||
|
- **Width a Height** (šířka a výška elementů) |
||||
|
- **Základní layouty** (dvousloupcový, třísloupcový) |
||||
|
|
||||
|
## 📂 Soubory v projektu |
||||
|
|
||||
|
- `index.html` - HTML struktura s layoutem |
||||
|
- `style.css` nebo `css/` složka - CSS styly pro layout |
||||
|
|
||||
|
## 🚀 Jak s lekcí pracovat |
||||
|
|
||||
|
1. Otevřete `index.html` v prohlížeči |
||||
|
2. Prozkoumejte CSS - zaměřte se na vlastnosti `float`, `clear`, `width` |
||||
|
3. Použijte Developer Tools (F12) a vyzkoušejte Box Model vizualizaci |
||||
|
4. Zkuste změnit šířky sloupců a sledujte, jak se mění layout |
||||
|
|
||||
|
## 💡 Tip pro studenty |
||||
|
|
||||
|
- **Box Model**: `margin` (vnější odsazení) ≠ `padding` (vnitřní odsazení) |
||||
|
- **Float** je starší technika - dnes se používá spíše Flexbox nebo Grid |
||||
|
- Nezapomeňte používat `clear: both;` po float elementech |
||||
|
- Developer Tools (F12) jsou váš nejlepší přítel pro debugování layoutu |
||||
|
|
||||
|
## ⚠️ Důležité poznámky |
||||
|
|
||||
|
- Float-based layout je starší přístup |
||||
|
- V moderních projektech se používá **Flexbox** (lekce 10) nebo **Grid** (lekce 12) |
||||
|
- Tato lekce je důležitá pro pochopení starších projektů |
||||
|
|
||||
|
## 🔗 Související lekce |
||||
|
|
||||
|
- **Předchozí:** 03_css_uvod - základy CSS |
||||
|
- **Další:** 10_flex - moderní flexbox layout |
||||
|
- **Související:** 12_grid - CSS Grid systém |
||||
@ -0,0 +1,71 @@ |
|||||
|
# 05 - Úvod do JavaScriptu |
||||
|
|
||||
|
## 🎯 Co se naučíte |
||||
|
|
||||
|
V této lekci se seznámíte se základy programování v JavaScriptu. |
||||
|
|
||||
|
## 📚 Témata lekce |
||||
|
|
||||
|
- **Připojení JavaScriptu** k HTML (inline, external) |
||||
|
- **Console.log** - výpis do konzole (F12) |
||||
|
- **Proměnné** (`let`, `const`, `var`) |
||||
|
- **Datové typy**: |
||||
|
- Čísla (number) |
||||
|
- Texty (string) |
||||
|
- Pole (array) |
||||
|
- Objekty (object) |
||||
|
- Datum (Date) |
||||
|
- **Podmínky** (`if`, `else`, `&&`, `||`) |
||||
|
- **Cykly** (`for`) |
||||
|
- **Funkce** (definice a volání) |
||||
|
|
||||
|
## 📂 Soubory v projektu |
||||
|
|
||||
|
- `index.html` - HTML soubor s připojením JS |
||||
|
- `app.js` - JavaScript soubor s ukázkami základů |
||||
|
|
||||
|
## 💻 Ukázky v lekci |
||||
|
|
||||
|
```javascript |
||||
|
// Proměnné |
||||
|
let x = 5; |
||||
|
const jmeno = "Jan"; |
||||
|
|
||||
|
// Podmínky |
||||
|
if (x > 3) { |
||||
|
console.log("x je větší než 3"); |
||||
|
} |
||||
|
|
||||
|
// Funkce |
||||
|
function pozdrav() { |
||||
|
console.log("Ahoj!"); |
||||
|
} |
||||
|
pozdrav(); // volání funkce |
||||
|
|
||||
|
// Cyklus |
||||
|
for (let i = 0; i < 5; i++) { |
||||
|
console.log(i); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## 🚀 Jak s lekcí pracovat |
||||
|
|
||||
|
1. Otevřete `index.html` v prohlížeči |
||||
|
2. Otevřete **Developer Console** (F12 → záložka Console) |
||||
|
3. Prohlédněte si soubor `app.js` v editoru |
||||
|
4. Zkuste měnit hodnoty proměnných a sledujte výstup v konzoli |
||||
|
5. Vytvořte vlastní funkce a zavolejte je |
||||
|
6. Experimentujte s podmínkami a cykly |
||||
|
|
||||
|
## 💡 Tip pro studenty |
||||
|
|
||||
|
- **Console.log()** je váš nejlepší přítel při debugování |
||||
|
- **let** vs **const**: použijte `const` pro hodnoty, které se nemění |
||||
|
- **var** se dnes už moc nepoužívá - používejte `let` nebo `const` |
||||
|
- Nezapomeňte volat funkci pomocí závorek: `nazevFunkce()` |
||||
|
- Pro objekty používáme tečkovou notaci: `osobnost.jmeno` |
||||
|
|
||||
|
## 🔗 Související lekce |
||||
|
|
||||
|
- **Další:** 06_js_html - propojení JavaScriptu s HTML elementy |
||||
|
- **Související:** 07_js_css_dom - manipulace s CSS přes JavaScript |
||||
@ -0,0 +1,62 @@ |
|||||
|
# 06 - JavaScript a HTML |
||||
|
|
||||
|
## 🎯 Co se naučíte |
||||
|
|
||||
|
V této lekci se naučíte propojit JavaScript s HTML elementy a vytvořit interaktivní kalkulačku. |
||||
|
|
||||
|
## 📚 Témata lekce |
||||
|
|
||||
|
- **getElementById()** - získání elementu podle ID |
||||
|
- **innerHTML** - změna obsahu elementu |
||||
|
- **innerText** - změna textu elementu |
||||
|
- **onclick** - obsluha kliknutí na tlačítko |
||||
|
- **Input elementy** - čtení hodnot z formulářových polí |
||||
|
- **Number()** - převod textu na číslo |
||||
|
- **Matematické operace** v JavaScriptu |
||||
|
|
||||
|
## 📂 Soubory v projektu |
||||
|
|
||||
|
- `index.html` - HTML s inputy a tlačítky |
||||
|
- `js/app.js` - JavaScript s funkcí pro výpočet |
||||
|
|
||||
|
## 💻 Ukázka z lekce |
||||
|
|
||||
|
```html |
||||
|
<!-- HTML --> |
||||
|
<input type="number" id="num1" placeholder="Číslo 1"> |
||||
|
<button onclick="vypocitat('+')">SEČÍST</button> |
||||
|
<span id="vysledek"></span> |
||||
|
``` |
||||
|
|
||||
|
```javascript |
||||
|
// JavaScript |
||||
|
function vypocitat(operator) { |
||||
|
let cislo1 = document.getElementById("num1").value; |
||||
|
let cislo2 = document.getElementById("num2").value; |
||||
|
// výpočet a zobrazení výsledku |
||||
|
document.getElementById("vysledek").innerHTML = vysledek; |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## 🚀 Jak s lekcí pracovat |
||||
|
|
||||
|
1. Otevřete `index.html` v prohlížeči |
||||
|
2. Vyzkoušejte kalkulačku - zadejte čísla a klikněte na tlačítka |
||||
|
3. Otevřete `js/app.js` a prozkoumejte funkci `vypocitat()` |
||||
|
4. Sledujte, jak se hodnoty čtou z inputů pomocí `getElementById()` |
||||
|
5. Zkuste přidat další operace (mocnina, odmocnina) |
||||
|
6. Použijte Developer Console (F12) pro debugování |
||||
|
|
||||
|
## 💡 Tip pro studenty |
||||
|
|
||||
|
- **getElementById()** vrací element, ne hodnotu - musíte použít `.value` |
||||
|
- **onclick** můžete použít přímo v HTML: `onclick="nazevFunkce()"` |
||||
|
- Hodnoty z inputů jsou vždy **string** - použijte `Number()` pro převod |
||||
|
- **innerHTML** vs **innerText**: innerHTML umožňuje HTML tagy, innerText jen text |
||||
|
- Používejte **console.log()** pro kontrolu hodnot při vývoji |
||||
|
|
||||
|
## 🔗 Související lekce |
||||
|
|
||||
|
- **Předchozí:** 05_js_intro - základy JavaScriptu |
||||
|
- **Další:** 07_js_css_dom - manipulace s CSS přes JavaScript |
||||
|
- **Související:** 08_js_form - práce s formuláři |
||||
@ -0,0 +1,85 @@ |
|||||
|
# 07 - JavaScript, CSS a DOM |
||||
|
|
||||
|
## 🎯 Co se naučíte |
||||
|
|
||||
|
V této lekci se naučíte měnit CSS styly pomocí JavaScriptu a pracovat s DOM (Document Object Model). |
||||
|
|
||||
|
## 📚 Témata lekce |
||||
|
|
||||
|
- **DOM (Document Object Model)** - struktura HTML v JavaScriptu |
||||
|
- **style.property** - změna CSS vlastností přes JS |
||||
|
- **onchange** event - reakce na změnu hodnoty |
||||
|
- **Select element** - práce s rozbalovacím seznamem |
||||
|
- **Input type="color"** - barevný picker |
||||
|
- **Dynamická změna stylů** (barva textu, barva pozadí, font) |
||||
|
|
||||
|
## 📂 Soubory v projektu |
||||
|
|
||||
|
- `index.html` - HTML s select elementy a color pickery |
||||
|
- `css/style.css` - základní CSS styly |
||||
|
- `js/index.js` - JavaScript pro změnu barev |
||||
|
- `js/changeFont.js` - JavaScript pro změnu fontu |
||||
|
|
||||
|
## 💻 Ukázka z lekce |
||||
|
|
||||
|
```html |
||||
|
<!-- HTML --> |
||||
|
<p id="ltext">Lorem ipsum...</p> |
||||
|
<select id="scolor" onchange="changeSColor()"> |
||||
|
<option value="black">černá</option> |
||||
|
<option value="red">červená</option> |
||||
|
</select> |
||||
|
<input type="color" id="tcolor" onchange="dynColor()"> |
||||
|
``` |
||||
|
|
||||
|
```javascript |
||||
|
// JavaScript |
||||
|
function changeSColor() { |
||||
|
let element = document.getElementById("ltext"); |
||||
|
let barva = document.getElementById("scolor").value; |
||||
|
element.style.color = barva; |
||||
|
} |
||||
|
|
||||
|
function dynColor() { |
||||
|
let element = document.getElementById("ltext"); |
||||
|
let barva = document.getElementById("tcolor").value; |
||||
|
element.style.color = barva; |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## 🚀 Jak s lekcí pracovat |
||||
|
|
||||
|
1. Otevřete `index.html` v prohlížeči |
||||
|
2. Vyzkoušejte měnit barvu textu pomocí selectu a color pickeru |
||||
|
3. Změňte font pomocí selectu |
||||
|
4. Prozkoumejte JS soubory - jak se elementy mění pomocí `.style` |
||||
|
5. Zkuste přidat další styly (velikost textu, tučnost, kurzíva) |
||||
|
6. Experimentujte s různými CSS vlastnostmi |
||||
|
|
||||
|
## 💡 Tip pro studenty |
||||
|
|
||||
|
- **element.style.property** - property musí být v camelCase: |
||||
|
- CSS: `background-color` → JS: `backgroundColor` |
||||
|
- CSS: `font-size` → JS: `fontSize` |
||||
|
- **onchange** se spouští při změně hodnoty (select, input) |
||||
|
- **input type="color"** vrací hodnotu v HEX formátu (#000000) |
||||
|
- Používejte **Developer Tools** (F12) → Elements → Styles pro inspekci CSS |
||||
|
|
||||
|
## 🎨 CSS vlastnosti, které můžete měnit přes JS |
||||
|
|
||||
|
```javascript |
||||
|
element.style.color = "red"; |
||||
|
element.style.backgroundColor = "blue"; |
||||
|
element.style.fontSize = "20px"; |
||||
|
element.style.fontFamily = "Arial"; |
||||
|
element.style.fontWeight = "bold"; |
||||
|
element.style.textAlign = "center"; |
||||
|
element.style.padding = "10px"; |
||||
|
element.style.margin = "5px"; |
||||
|
``` |
||||
|
|
||||
|
## 🔗 Související lekce |
||||
|
|
||||
|
- **Předchozí:** 06_js_html - propojení JS a HTML |
||||
|
- **Další:** 08_js_form - validace formulářů s JavaScriptem |
||||
|
- **Související:** 05_js_intro - základy JavaScriptu |
||||
@ -0,0 +1,94 @@ |
|||||
|
# 08 - JavaScript a Formuláře |
||||
|
|
||||
|
## 🎯 Co se naučíte |
||||
|
|
||||
|
V této lekci se naučíte pracovat s formuláři v JavaScriptu - validace, odeslání, manipulace s daty. |
||||
|
|
||||
|
## 📚 Témata lekce |
||||
|
|
||||
|
- **Formulářové elementy** (input, textarea, select, checkbox, radio) |
||||
|
- **Validace formulářů** s JavaScriptem |
||||
|
- **event.preventDefault()** - zabránění odeslání formuláře |
||||
|
- **Čtení hodnot** z různých typů inputů |
||||
|
- **Kontrola povinných polí** |
||||
|
- **Regex** (základy) - kontrola formátu emailu, telefonu |
||||
|
- **Visual feedback** - zobrazení chyb uživateli |
||||
|
|
||||
|
## 📂 Soubory v projektu |
||||
|
|
||||
|
- `index.html` - HTML formulář |
||||
|
- `js/` - JavaScript soubory pro validaci |
||||
|
|
||||
|
## 💻 Ukázka z lekce |
||||
|
|
||||
|
```html |
||||
|
<!-- HTML --> |
||||
|
<form id="myForm" onsubmit="return validateForm()"> |
||||
|
<input type="text" id="jmeno" required> |
||||
|
<input type="email" id="email" required> |
||||
|
<button type="submit">Odeslat</button> |
||||
|
</form> |
||||
|
<p id="error"></p> |
||||
|
``` |
||||
|
|
||||
|
```javascript |
||||
|
// JavaScript |
||||
|
function validateForm() { |
||||
|
let jmeno = document.getElementById("jmeno").value; |
||||
|
let email = document.getElementById("email").value; |
||||
|
|
||||
|
if (jmeno === "") { |
||||
|
document.getElementById("error").innerHTML = "Jméno je povinné!"; |
||||
|
return false; // zabrání odeslání |
||||
|
} |
||||
|
|
||||
|
// Regex pro email |
||||
|
let emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; |
||||
|
if (!emailRegex.test(email)) { |
||||
|
document.getElementById("error").innerHTML = "Neplatný email!"; |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
return true; // formulář se odešle |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## 🚀 Jak s lekcí pracovat |
||||
|
|
||||
|
1. Otevřete `index.html` v prohlížeči |
||||
|
2. Vyzkoušejte odeslat formulář s prázdnými poli |
||||
|
3. Sledujte validační zprávy |
||||
|
4. Prozkoumejte JS kód - jak funguje validace |
||||
|
5. Zkuste přidat další validace (délka hesla, shoda hesel) |
||||
|
6. Použijte console.log() pro debugování |
||||
|
|
||||
|
## 💡 Tip pro studenty |
||||
|
|
||||
|
- **return false** v `onsubmit` zabrání odeslání formuláře |
||||
|
- **event.preventDefault()** je modernější způsob zabránění odeslání |
||||
|
- **required** atribut v HTML je první úroveň validace (HTML5) |
||||
|
- **JavaScript validace** je důležitá pro pokročilejší kontroly |
||||
|
- **Regex** (regulární výrazy) jsou mocný nástroj pro validaci formátů |
||||
|
- Vždy validujte i na **serveru** - JavaScript validace lze obejít |
||||
|
|
||||
|
## 🔍 Užitečné Regex vzory |
||||
|
|
||||
|
```javascript |
||||
|
// Email |
||||
|
/^[^\s@]+@[^\s@]+\.[^\s@]+$/ |
||||
|
|
||||
|
// Telefon (CZ formát) |
||||
|
/^(\+420)?[0-9]{9}$/ |
||||
|
|
||||
|
// Heslo (min 8 znaků, velké, malé, číslo) |
||||
|
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/ |
||||
|
|
||||
|
// Poštovní směrovací číslo |
||||
|
/^[0-9]{3}\s?[0-9]{2}$/ |
||||
|
``` |
||||
|
|
||||
|
## 🔗 Související lekce |
||||
|
|
||||
|
- **Předchozí:** 07_js_css_dom - manipulace s CSS |
||||
|
- **Související:** AI/ai_03_form_validation - pokročilá validace s addEventListener |
||||
|
- **Další:** 11_json - práce s JSON daty |
||||
@ -0,0 +1,100 @@ |
|||||
|
# 09 - Media Queries a Responzivní Design |
||||
|
|
||||
|
## 🎯 Co se naučíte |
||||
|
|
||||
|
V této lekci se naučíte vytvářet responzivní webové stránky pomocí media queries. |
||||
|
|
||||
|
## 📚 Témata lekce |
||||
|
|
||||
|
- **Media Queries** - CSS pravidla pro různé velikosti obrazovek |
||||
|
- **Viewport meta tag** - důležitý pro responzivitu |
||||
|
- **Breakpointy** - body zlomu pro různá zařízení |
||||
|
- **Mobile-first přístup** vs Desktop-first |
||||
|
- **Responzivní obrázky** - přizpůsobení velikosti |
||||
|
- **Responzivní layout** - změna rozvržení podle velikosti |
||||
|
|
||||
|
## 📂 Soubory v projektu |
||||
|
|
||||
|
- `index.html` - HTML s viewport meta tagem |
||||
|
- `style.css` - CSS s media queries |
||||
|
- `img/` - složka s obrázky |
||||
|
|
||||
|
## 💻 Ukázka z lekce |
||||
|
|
||||
|
```html |
||||
|
<!-- HTML - DŮLEŽITÝ viewport meta tag! --> |
||||
|
<head> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
|
</head> |
||||
|
``` |
||||
|
|
||||
|
```css |
||||
|
/* CSS - Media Queries */ |
||||
|
|
||||
|
/* Desktop styly (výchozí) */ |
||||
|
.col-6 { |
||||
|
width: 50%; |
||||
|
float: left; |
||||
|
} |
||||
|
|
||||
|
/* Tablet (do 768px) */ |
||||
|
@media (max-width: 768px) { |
||||
|
.col-6 { |
||||
|
width: 100%; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* Mobil (do 480px) */ |
||||
|
@media (max-width: 480px) { |
||||
|
.col-6 { |
||||
|
width: 100%; |
||||
|
} |
||||
|
body { |
||||
|
font-size: 14px; |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## 🚀 Jak s lekcí pracovat |
||||
|
|
||||
|
1. Otevřete `index.html` v prohlížeči |
||||
|
2. Otevřete **Developer Tools** (F12) a zapněte **Device Toolbar** (Ctrl+Shift+M) |
||||
|
3. Změňte velikost okna nebo vyberte různá zařízení (iPhone, iPad, Desktop) |
||||
|
4. Sledujte, jak se mění layout při různých velikostech |
||||
|
5. Prozkoumejte `style.css` - najděte `@media` pravidla |
||||
|
6. Zkuste přidat vlastní breakpointy a změny stylů |
||||
|
|
||||
|
## 💡 Tip pro studenty |
||||
|
|
||||
|
- **VŽDY použijte viewport meta tag** - bez něj responzivita na mobilu nebude fungovat! |
||||
|
- **Breakpointy** (běžné hodnoty): |
||||
|
- Mobil: `max-width: 480px` |
||||
|
- Tablet: `max-width: 768px` |
||||
|
- Desktop: `min-width: 769px` nebo bez media query |
||||
|
- **Mobile-first** přístup (doporučený): |
||||
|
- Výchozí styly pro mobil |
||||
|
- Media queries s `min-width` pro větší obrazovky |
||||
|
- **Desktop-first** přístup: |
||||
|
- Výchozí styly pro desktop |
||||
|
- Media queries s `max-width` pro menší obrazovky |
||||
|
- Testujte na **reálných zařízeních**, ne jen v prohlížeči |
||||
|
|
||||
|
## 📱 Testování responzivity |
||||
|
|
||||
|
**V prohlížeči:** |
||||
|
1. F12 → Device Toolbar (Ctrl+Shift+M) |
||||
|
2. Vyberte zařízení nebo nastavte vlastní rozměry |
||||
|
3. Otočte zařízení (landscape/portrait) |
||||
|
|
||||
|
**Běžné rozlišení:** |
||||
|
- iPhone: 375px × 667px |
||||
|
- iPad: 768px × 1024px |
||||
|
- Desktop: 1920px × 1080px |
||||
|
|
||||
|
## 🔗 Související lekce |
||||
|
|
||||
|
- **Předchozí:** 08_js_form - JavaScript a formuláře |
||||
|
- **Další:** 10_flex - Flexbox layout |
||||
|
- **Související:** |
||||
|
- 12_grid - CSS Grid (další layout systém) |
||||
|
- AI/ai_01_grid_responsive - praktický projekt s responzivitou |
||||
@ -0,0 +1,129 @@ |
|||||
|
# 10 - Flexbox Layout |
||||
|
|
||||
|
## 🎯 Co se naučíte |
||||
|
|
||||
|
V této lekci se naučíte vytvářet moderní layouty pomocí CSS Flexbox. |
||||
|
|
||||
|
## 📚 Témata lekce |
||||
|
|
||||
|
- **Flexbox základy** - `display: flex` |
||||
|
- **Flex container** vlastnosti: |
||||
|
- `flex-direction` (row, column) |
||||
|
- `justify-content` (zarovnání na hlavní ose) |
||||
|
- `align-items` (zarovnání na vedlejší ose) |
||||
|
- `flex-wrap` (zalamování položek) |
||||
|
- `gap` (mezery mezi položkami) |
||||
|
- **Flex items** vlastnosti: |
||||
|
- `flex-grow`, `flex-shrink`, `flex-basis` |
||||
|
- `align-self` (individuální zarovnání) |
||||
|
- **Praktické použití** - navigace, karty, layout |
||||
|
|
||||
|
## 📂 Soubory v projektu |
||||
|
|
||||
|
- `index.html` - HTML struktura s flexbox layoutem |
||||
|
- `style.css` - CSS s flexbox vlastnostmi |
||||
|
|
||||
|
## 💻 Ukázka z lekce |
||||
|
|
||||
|
```css |
||||
|
/* Flex container */ |
||||
|
.wrapper { |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
flex-wrap: wrap; |
||||
|
gap: 20px; |
||||
|
} |
||||
|
|
||||
|
/* Flex items */ |
||||
|
.main { |
||||
|
flex: 2; /* flex-grow: 2 */ |
||||
|
} |
||||
|
|
||||
|
.aside { |
||||
|
flex: 1; /* flex-grow: 1 */ |
||||
|
} |
||||
|
|
||||
|
/* Navigace s flexbox */ |
||||
|
.navigace { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## 🚀 Jak s lekcí pracovat |
||||
|
|
||||
|
1. Otevřete `index.html` v prohlížeči |
||||
|
2. Otevřete **Developer Tools** (F12) a vyberte flexbox element |
||||
|
3. V Chrome DevTools najdete **Flexbox** badge - klikněte pro vizualizaci |
||||
|
4. Prozkoumejte `style.css` - jak funguje `display: flex` |
||||
|
5. Zkuste měnit `flex-direction`, `justify-content`, `align-items` |
||||
|
6. Změňte velikost okna a sledujte, jak se flex items přizpůsobují |
||||
|
|
||||
|
## 💡 Tip pro studenty |
||||
|
|
||||
|
### Kdy použít Flexbox: |
||||
|
- ✅ Navigační menu (horizontální nebo vertikální) |
||||
|
- ✅ Karty/produkty vedle sebe |
||||
|
- ✅ Centrování elementů (horizontálně i vertikálně) |
||||
|
- ✅ Jednosměrné layouty (řádek nebo sloupec) |
||||
|
|
||||
|
### Kdy raději Grid: |
||||
|
- ❌ Komplexní dvourozměrné layouty (řádky + sloupce) |
||||
|
- ❌ Galerie obrázků |
||||
|
- ❌ Celostránkové layouty s header, sidebar, main, footer |
||||
|
|
||||
|
### Hlavní koncept: |
||||
|
- **Flex container** = rodič s `display: flex` |
||||
|
- **Flex items** = přímé potomci flex containeru |
||||
|
|
||||
|
### Důležité vlastnosti: |
||||
|
```css |
||||
|
/* Na kontejneru */ |
||||
|
display: flex; |
||||
|
justify-content: center; /* horizontální zarovnání */ |
||||
|
align-items: center; /* vertikální zarovnání */ |
||||
|
gap: 20px; /* mezery mezi položkami */ |
||||
|
|
||||
|
/* Na položkách */ |
||||
|
flex: 1; /* roztáhne položku */ |
||||
|
``` |
||||
|
|
||||
|
## 🎯 Praktické příklady |
||||
|
|
||||
|
**Centrování:** |
||||
|
```css |
||||
|
.container { |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
height: 100vh; |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
**Horizontální navigace:** |
||||
|
```css |
||||
|
nav { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
**Responzivní karty:** |
||||
|
```css |
||||
|
.card-container { |
||||
|
display: flex; |
||||
|
flex-wrap: wrap; |
||||
|
gap: 20px; |
||||
|
} |
||||
|
.card { |
||||
|
flex: 1 1 300px; /* min šířka 300px */ |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## 🔗 Související lekce |
||||
|
|
||||
|
- **Předchozí:** 09_mediaquerry - responzivní design |
||||
|
- **Další:** 11_json - práce s JSON |
||||
|
- **Porovnání:** 12_grid - CSS Grid (dvourozměrný layout) |
||||
|
- **Praktický projekt:** AI/ai_04_prep_bootstrap - kombinace Flex a Grid |
||||
@ -0,0 +1,117 @@ |
|||||
|
# 11 - JSON a Práce s Daty |
||||
|
|
||||
|
## 🎯 Co se naučíte |
||||
|
|
||||
|
V této lekci se naučíte pracovat s JSON formátem a načítat data do JavaScriptu. |
||||
|
|
||||
|
## 📚 Témata lekce |
||||
|
|
||||
|
- **JSON formát** (JavaScript Object Notation) |
||||
|
- **JSON.parse()** - převod JSON stringu na objekt |
||||
|
- **JSON.stringify()** - převod objektu na JSON string |
||||
|
- **Načítání dat z JSON souborů** |
||||
|
- **Práce s JSON daty** v JavaScriptu |
||||
|
- **Výpis dat na stránku** pomocí DOM manipulace |
||||
|
- **Pole objektů** - procházení cyklem |
||||
|
|
||||
|
## 📂 Soubory v projektu |
||||
|
|
||||
|
- `index.html` - HTML s elementy pro zobrazení dat |
||||
|
- `js/app.js` - JavaScript pro práce s JSON |
||||
|
- `data/` (možná) - složka s JSON soubory |
||||
|
|
||||
|
## 💻 Ukázka z lekce |
||||
|
|
||||
|
```javascript |
||||
|
// JSON objekt v JavaScriptu |
||||
|
const osoba = { |
||||
|
"jmeno": "Jan", |
||||
|
"prijmeni": "Novák", |
||||
|
"vek": 20, |
||||
|
"mesto": "Praha" |
||||
|
}; |
||||
|
|
||||
|
// Výpis dat |
||||
|
console.log(osoba.jmeno); // Jan |
||||
|
|
||||
|
// JSON string |
||||
|
const jsonString = '{"jmeno":"Jan","vek":20}'; |
||||
|
|
||||
|
// Převod stringu na objekt |
||||
|
const obj = JSON.parse(jsonString); |
||||
|
console.log(obj.jmeno); // Jan |
||||
|
|
||||
|
// Převod objektu na string |
||||
|
const str = JSON.stringify(osoba); |
||||
|
console.log(str); // {"jmeno":"Jan",...} |
||||
|
|
||||
|
// Pole objektů |
||||
|
const osoby = [ |
||||
|
{"jmeno": "Jan", "vek": 20}, |
||||
|
{"jmeno": "Petra", "vek": 25}, |
||||
|
{"jmeno": "Karel", "vek": 30} |
||||
|
]; |
||||
|
|
||||
|
// Procházení pole |
||||
|
osoby.forEach(function(osoba) { |
||||
|
console.log(osoba.jmeno + " má " + osoba.vek + " let"); |
||||
|
}); |
||||
|
|
||||
|
// Výpis na stránku |
||||
|
let html = ""; |
||||
|
osoby.forEach(function(osoba) { |
||||
|
html += "<p>" + osoba.jmeno + "</p>"; |
||||
|
}); |
||||
|
document.getElementById("demo").innerHTML = html; |
||||
|
``` |
||||
|
|
||||
|
## 🚀 Jak s lekcí pracovat |
||||
|
|
||||
|
1. Otevřete `index.html` v prohlížeči |
||||
|
2. Otevřete **Console** (F12) a sledujte výpis JSON dat |
||||
|
3. Prozkoumejte `js/app.js` - jak se pracuje s JSON |
||||
|
4. Zkuste vytvořit vlastní JSON objekt |
||||
|
5. Vyzkoušejte převody mezi objektem a stringem |
||||
|
6. Vytvořte pole objektů a vypište je na stránku |
||||
|
|
||||
|
## 💡 Tip pro studenty |
||||
|
|
||||
|
### JSON syntaxe: |
||||
|
- **Klíče musí být v uvozovkách**: `{"jmeno": "Jan"}` ✅ |
||||
|
- **Hodnoty**: string (v uvozovkách), číslo, boolean, null, objekt, pole |
||||
|
- **Čárka** mezi položkami, ale ne za poslední |
||||
|
- **Žádné komentáře** v JSON! |
||||
|
|
||||
|
### Běžné chyby: |
||||
|
```javascript |
||||
|
// ❌ ŠPATNĚ - klíče bez uvozovek |
||||
|
{jmeno: "Jan"} |
||||
|
|
||||
|
// ✅ SPRÁVNĚ |
||||
|
{"jmeno": "Jan"} |
||||
|
|
||||
|
// ❌ ŠPATNĚ - čárka za posledním |
||||
|
{"jmeno": "Jan", "vek": 20,} |
||||
|
|
||||
|
// ✅ SPRÁVNĚ |
||||
|
{"jmeno": "Jan", "vek": 20} |
||||
|
``` |
||||
|
|
||||
|
### JSON vs JavaScript objekt: |
||||
|
- **JSON** = textový formát (string), univerzální |
||||
|
- **JS objekt** = datová struktura v paměti |
||||
|
- **JSON.parse()** = převod z textu na objekt |
||||
|
- **JSON.stringify()** = převod z objektu na text |
||||
|
|
||||
|
### Praktické použití: |
||||
|
- 💾 Ukládání dat do localStorage |
||||
|
- 🌐 Komunikace s API (fetch) |
||||
|
- 📤 Odesílání dat na server |
||||
|
- 📥 Načítání konfigurace |
||||
|
|
||||
|
## 🔗 Související lekce |
||||
|
|
||||
|
- **Předchozí:** 10_flex - Flexbox layout |
||||
|
- **Další:** 12_grid - CSS Grid |
||||
|
- **Pokročilé:** 20_ajax - AJAX a Fetch API (načítání JSON ze serveru) |
||||
|
- **Praktické:** Práce s API a reálnými JSON daty |
||||
@ -0,0 +1,150 @@ |
|||||
|
# 12 - CSS Grid Layout |
||||
|
|
||||
|
## 🎯 Co se naučíte |
||||
|
|
||||
|
V této lekci se naučíte vytvářet komplexní dvourozměrné layouty pomocí CSS Grid. |
||||
|
|
||||
|
## 📚 Témata lekce |
||||
|
|
||||
|
- **CSS Grid základy** - `display: grid` |
||||
|
- **Grid container** vlastnosti: |
||||
|
- `grid-template-columns` (definice sloupců) |
||||
|
- `grid-template-rows` (definice řádků) |
||||
|
- `gap`, `column-gap`, `row-gap` (mezery) |
||||
|
- `grid-template-areas` (pojmenované oblasti) |
||||
|
- **Grid items** vlastnosti: |
||||
|
- `grid-column`, `grid-row` (umístění položky) |
||||
|
- `grid-area` (pojmenovaná oblast) |
||||
|
- **Responzivní Grid** - kombinace s media queries |
||||
|
- **Galerie** - praktické použití Gridu |
||||
|
|
||||
|
## 📂 Soubory v projektu |
||||
|
|
||||
|
- `index.html` - HTML s grid strukturou |
||||
|
- `style.css` - CSS s grid definicemi |
||||
|
|
||||
|
## 💻 Ukázka z lekce |
||||
|
|
||||
|
```css |
||||
|
/* Grid container - galerie */ |
||||
|
.grid-container { |
||||
|
display: grid; |
||||
|
grid-template-columns: repeat(3, 1fr); /* 3 stejně široké sloupce */ |
||||
|
gap: 20px; /* mezery mezi položkami */ |
||||
|
} |
||||
|
|
||||
|
/* Grid items */ |
||||
|
.grid-item { |
||||
|
background-color: lightblue; |
||||
|
padding: 20px; |
||||
|
} |
||||
|
|
||||
|
/* První položka zabere 2 sloupce */ |
||||
|
.grid-item:first-child { |
||||
|
grid-column: span 2; |
||||
|
} |
||||
|
|
||||
|
/* Responzivní Grid */ |
||||
|
@media (max-width: 768px) { |
||||
|
.grid-container { |
||||
|
grid-template-columns: repeat(2, 1fr); /* 2 sloupce na tabletu */ |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@media (max-width: 480px) { |
||||
|
.grid-container { |
||||
|
grid-template-columns: 1fr; /* 1 sloupec na mobilu */ |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## 🚀 Jak s lekcí pracovat |
||||
|
|
||||
|
1. Otevřete `index.html` v prohlížeči |
||||
|
2. Otevřete **Developer Tools** (F12) a vyberte grid element |
||||
|
3. V Chrome DevTools najdete **Grid** badge - klikněte pro vizualizaci mřížky |
||||
|
4. Prozkoumejte `style.css` - jak funguje `display: grid` |
||||
|
5. Zkuste měnit počet sloupců: `grid-template-columns: repeat(4, 1fr)` |
||||
|
6. Experimentujte s `gap`, `grid-column`, `grid-row` |
||||
|
7. Změňte velikost okna a sledujte responzivitu |
||||
|
|
||||
|
## 💡 Tip pro studenty |
||||
|
|
||||
|
### Kdy použít Grid: |
||||
|
- ✅ Komplexní dvourozměrné layouty (řádky + sloupce) |
||||
|
- ✅ Galerie obrázků |
||||
|
- ✅ Celostránkové layouty (header, sidebar, main, footer) |
||||
|
- ✅ Dashboardy, karty v mřížce |
||||
|
- ✅ Kalendáře |
||||
|
|
||||
|
### Kdy raději Flexbox: |
||||
|
- ❌ Jednosměrné layouty (jen řádek nebo jen sloupec) |
||||
|
- ❌ Navigační menu |
||||
|
- ❌ Jednoduchá centrování |
||||
|
|
||||
|
### Hlavní jednotky: |
||||
|
- **fr** (fraction) = podíl dostupného prostoru |
||||
|
- `1fr 2fr` = první sloupec 1/3, druhý 2/3 |
||||
|
- **px** = pevná velikost |
||||
|
- **%** = procenta z containeru |
||||
|
- **auto** = automatická velikost podle obsahu |
||||
|
|
||||
|
### Grid layout pattern: |
||||
|
```css |
||||
|
/* Kontejner */ |
||||
|
.grid-container { |
||||
|
display: grid; /* MUSÍ BÝT! */ |
||||
|
grid-template-columns: repeat(3, 1fr); |
||||
|
gap: 20px; |
||||
|
} |
||||
|
|
||||
|
/* Položky se umístí automaticky */ |
||||
|
.grid-item { |
||||
|
/* žádné speciální vlastnosti potřeba */ |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## 🎯 Praktické příklady |
||||
|
|
||||
|
**Galerie 3 sloupce:** |
||||
|
```css |
||||
|
.galerie { |
||||
|
display: grid; |
||||
|
grid-template-columns: repeat(3, 1fr); |
||||
|
gap: 10px; |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
**Responzivní galerie (bez media queries!):** |
||||
|
```css |
||||
|
.galerie { |
||||
|
display: grid; |
||||
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); |
||||
|
gap: 20px; |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
**Layout stránky:** |
||||
|
```css |
||||
|
.layout { |
||||
|
display: grid; |
||||
|
grid-template-columns: 250px 1fr; /* sidebar + main */ |
||||
|
grid-template-rows: 80px 1fr 60px; /* header + content + footer */ |
||||
|
gap: 10px; |
||||
|
height: 100vh; |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## ⚠️ Důležité poznámky |
||||
|
|
||||
|
1. **display: grid musí být na KONTEJNERU**, ne na položkách! |
||||
|
2. **Grid položky** = pouze přímé děti grid containeru |
||||
|
3. Použijte **Chrome DevTools Grid inspector** pro debugování |
||||
|
4. **repeat()** funkce šetří psaní: `repeat(3, 1fr)` = `1fr 1fr 1fr` |
||||
|
|
||||
|
## 🔗 Související lekce |
||||
|
|
||||
|
- **Předchozí:** 11_json - práce s JSON |
||||
|
- **Další:** 13_bootstrap_intro - úvod do Bootstrapu |
||||
|
- **Porovnání:** 10_flex - Flexbox (jednosměrný layout) |
||||
|
- **Praktický projekt:** AI/ai_01_grid_responsive - kompletní Grid projekt s responzivitou |
||||
@ -0,0 +1,119 @@ |
|||||
|
# 13 - Úvod do Bootstrapu |
||||
|
|
||||
|
## 🎯 Co se naučíte |
||||
|
|
||||
|
V této lekci se seznámíte s Bootstrap frameworkem a jeho základními komponentami. |
||||
|
|
||||
|
## 📚 Témata lekce |
||||
|
|
||||
|
- **Co je Bootstrap** - CSS framework pro rychlý vývoj |
||||
|
- **Připojení Bootstrapu** k projektu (lokální soubory) |
||||
|
- **Container** - základní obal pro obsah (`.container`) |
||||
|
- **Grid systém** - 12sloupcová mřížka |
||||
|
- `.row` - řádek |
||||
|
- `.col` - sloupce (automatická šířka) |
||||
|
- `.col-6` - sloupec se šířkou 6/12 |
||||
|
- **Responzivní třídy** - `col-md-6`, `col-lg-3` |
||||
|
- **Typography** - `display-1`, `display-5` |
||||
|
- **Utility třídy**: |
||||
|
- Okraje: `m-2`, `mx-2`, `p-2` |
||||
|
- Borders: `border`, `border-danger`, `rounded` |
||||
|
- Další: `<mark>`, `<code>`, `<kbd>` |
||||
|
|
||||
|
## 📂 Soubory v projektu |
||||
|
|
||||
|
- `index.html` - HTML s Bootstrap třídami |
||||
|
- `css/bootstrap.css` - Bootstrap CSS (lokální) |
||||
|
- `js/bootstrap.js` - Bootstrap JavaScript (lokální) |
||||
|
|
||||
|
## 💻 Ukázka z lekce |
||||
|
|
||||
|
```html |
||||
|
<!-- Container --> |
||||
|
<div class="container"> |
||||
|
<h1 class="display-1">Hello, Bootstrap!</h1> |
||||
|
|
||||
|
<!-- Grid systém --> |
||||
|
<div class="row"> |
||||
|
<div class="col-md-6 col-lg-3"> |
||||
|
<div class="border border-primary rounded m-2 p-2"> |
||||
|
Obsah sloupce |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="col-md-6 col-lg-3"> |
||||
|
Další sloupec |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
``` |
||||
|
|
||||
|
## 🚀 Jak s lekcí pracovat |
||||
|
|
||||
|
1. Otevřete `index.html` v prohlížeči |
||||
|
2. Prozkoumejte Bootstrap třídy v HTML |
||||
|
3. Změňte velikost okna - sledujte responzivní chování |
||||
|
4. Vyzkoušejte Developer Tools (F12) - Device Toolbar |
||||
|
5. Experimentujte s různými utility třídami |
||||
|
6. Změňte `col-md-6` na `col-lg-4` a sledujte změny |
||||
|
|
||||
|
## 💡 Tip pro studenty |
||||
|
|
||||
|
### Bootstrap Grid System: |
||||
|
- **12 sloupců celkem** v každém řádku |
||||
|
- **col-6** = 6/12 = 50% šířky |
||||
|
- **col-4** = 4/12 = 33.33% šířky |
||||
|
- **col** = automatická šířka (rovnoměrně rozdělí prostor) |
||||
|
|
||||
|
### Responzivní breakpointy: |
||||
|
- **xs** (extra small) = < 576px (mobil) - není třeba psát, je výchozí |
||||
|
- **sm** (small) = ≥ 576px |
||||
|
- **md** (medium) = ≥ 768px (tablet) |
||||
|
- **lg** (large) = ≥ 992px (desktop) |
||||
|
- **xl** (extra large) = ≥ 1200px |
||||
|
|
||||
|
### Příklad responzivity: |
||||
|
```html |
||||
|
<div class="col-12 col-md-6 col-lg-3"> |
||||
|
<!-- Mobil: 100% šířky (12/12) --> |
||||
|
<!-- Tablet: 50% šířky (6/12) --> |
||||
|
<!-- Desktop: 25% šířky (3/12) --> |
||||
|
</div> |
||||
|
``` |
||||
|
|
||||
|
### Container vs Container-fluid: |
||||
|
- **container** = fixní šířka, centrovaný |
||||
|
- **container-fluid** = 100% šířky |
||||
|
|
||||
|
### Důležité: |
||||
|
- VŽDY používejte **row** pro řádky |
||||
|
- Sloupce (**col**) musí být uvnitř **row** |
||||
|
- Celkový součet sloupců by měl být **12** (nebo méně) |
||||
|
|
||||
|
## 🎨 Utility třídy: |
||||
|
|
||||
|
```html |
||||
|
<!-- Okraje (margin) --> |
||||
|
m-2 = margin všude |
||||
|
mx-2 = margin vlevo a vpravo |
||||
|
my-3 = margin nahoře a dole |
||||
|
mt-4 = margin nahoře |
||||
|
|
||||
|
<!-- Padding --> |
||||
|
p-2 = padding všude |
||||
|
px-3 = padding vlevo a vpravo |
||||
|
|
||||
|
<!-- Borders --> |
||||
|
border = přidá border |
||||
|
border-primary = modrý border |
||||
|
border-3 = tlustší border |
||||
|
rounded = zaoblené rohy |
||||
|
``` |
||||
|
|
||||
|
## 🔗 Související lekce |
||||
|
|
||||
|
- **Předchozí:** 12_grid - CSS Grid (pochopení grid systému) |
||||
|
- **Důležité přípravné projekty:** |
||||
|
- AI/ai_01_grid_responsive - CSS Grid praxe |
||||
|
- AI/ai_04_prep_bootstrap - komplexní příprava |
||||
|
- **Další:** 14_bootstrap_layout - pokročilejší Bootstrap layouty |
||||
|
- **Související:** 15_bootstrap_components - Bootstrap komponenty |
||||
@ -0,0 +1,162 @@ |
|||||
|
# 14 - Bootstrap Layout |
||||
|
|
||||
|
## 🎯 Co se naučíte |
||||
|
|
||||
|
V této lekci se naučíte vytvářet kompletní layout stránky s pomocí Bootstrapu. |
||||
|
|
||||
|
## 📚 Témata lekce |
||||
|
|
||||
|
- **Header s navigací** - `nav`, `nav-pills`, `nav-item` |
||||
|
- **Flexbox utility třídy** - `d-flex`, `justify-content-center`, `align-items-center` |
||||
|
- **Main content** - hlavní obsah stránky |
||||
|
- **Jumbotron** - velká úvodní sekce (hero section) |
||||
|
- **Tabulky** - `table`, `table-hover`, `table-dark`, `table-info` |
||||
|
- **Footer** - patička stránky s `mt-auto` |
||||
|
- **Sticky footer** - přilepený footer (flexbox pattern) |
||||
|
- **Buttons** - `btn`, `btn-primary` |
||||
|
|
||||
|
## 📂 Soubory v projektu |
||||
|
|
||||
|
- `index.html` - kompletní layout s header, main, footer |
||||
|
- `css/bootstrap.css` - Bootstrap CSS |
||||
|
- `js/bootstrap.js` - Bootstrap JS |
||||
|
|
||||
|
## 💻 Ukázka z lekce |
||||
|
|
||||
|
```html |
||||
|
<body class="d-flex flex-column min-vh-100"> |
||||
|
<!-- Header --> |
||||
|
<header class="d-flex flex-wrap justify-content-center py-3 border-bottom"> |
||||
|
<a href="#" class="me-auto fs-4">Název Stránky</a> |
||||
|
<ul class="nav nav-pills"> |
||||
|
<li class="nav-item"> |
||||
|
<a href="#" class="nav-link active">Home</a> |
||||
|
</li> |
||||
|
<li class="nav-item"> |
||||
|
<a href="#" class="nav-link">About</a> |
||||
|
</li> |
||||
|
</ul> |
||||
|
</header> |
||||
|
|
||||
|
<!-- Main --> |
||||
|
<main class="container"> |
||||
|
<!-- Jumbotron --> |
||||
|
<div class="p-5 bg-secondary text-white rounded"> |
||||
|
<h1 class="display-3">Hello world!</h1> |
||||
|
<p>Lorem ipsum...</p> |
||||
|
<a href="#" class="btn btn-primary">Více info</a> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Tabulka --> |
||||
|
<table class="table table-hover"> |
||||
|
<tr class="table-dark"> |
||||
|
<td>Jméno</td> |
||||
|
<td>Email</td> |
||||
|
</tr> |
||||
|
<tr class="table-info"> |
||||
|
<td>Jan</td> |
||||
|
<td>jan@email.cz</td> |
||||
|
</tr> |
||||
|
</table> |
||||
|
</main> |
||||
|
|
||||
|
<!-- Footer --> |
||||
|
<footer class="footer mt-auto"> |
||||
|
<div class="nav justify-content-center py-2"> |
||||
|
<span>© 2025</span> |
||||
|
</div> |
||||
|
</footer> |
||||
|
</body> |
||||
|
``` |
||||
|
|
||||
|
## 🚀 Jak s lekcí pracovat |
||||
|
|
||||
|
1. Otevřete `index.html` v prohlížeči |
||||
|
2. Prozkoumejte strukturu: header → main → footer |
||||
|
3. Změňte velikost okna - sledujte responzivní navigaci |
||||
|
4. Vyzkoušejte hover efekt na tabulce |
||||
|
5. Experimentujte s barevnými třídami tabulek |
||||
|
6. Zkuste přidat další navigační položky |
||||
|
|
||||
|
## 💡 Tip pro studenty |
||||
|
|
||||
|
### Sticky Footer pattern: |
||||
|
```html |
||||
|
<body class="d-flex flex-column min-vh-100"> |
||||
|
<header>...</header> |
||||
|
<main class="container">...</main> |
||||
|
<footer class="footer mt-auto">...</footer> |
||||
|
</body> |
||||
|
``` |
||||
|
- **d-flex flex-column** = flexbox ve vertikálním směru |
||||
|
- **min-vh-100** = minimální výška 100% viewportu |
||||
|
- **mt-auto** = margin-top: auto (footer se přilepí dolů) |
||||
|
|
||||
|
### Flexbox utility třídy: |
||||
|
```html |
||||
|
<!-- Display --> |
||||
|
d-flex = display: flex |
||||
|
d-inline-flex = display: inline-flex |
||||
|
|
||||
|
<!-- Direction --> |
||||
|
flex-row = řádkový (výchozí) |
||||
|
flex-column = sloupcový |
||||
|
|
||||
|
<!-- Justify content (horizontální zarovnání) --> |
||||
|
justify-content-start = vlevo |
||||
|
justify-content-center = na střed |
||||
|
justify-content-end = vpravo |
||||
|
justify-content-between = mezi |
||||
|
|
||||
|
<!-- Align items (vertikální zarovnání) --> |
||||
|
align-items-start = nahoře |
||||
|
align-items-center = na střed |
||||
|
align-items-end = dole |
||||
|
``` |
||||
|
|
||||
|
### Navigace: |
||||
|
```html |
||||
|
<ul class="nav nav-pills"> |
||||
|
<li class="nav-item"> |
||||
|
<a href="#" class="nav-link active">Aktivní odkaz</a> |
||||
|
</li> |
||||
|
<li class="nav-item"> |
||||
|
<a href="#" class="nav-link disabled">Neaktivní odkaz</a> |
||||
|
</li> |
||||
|
</ul> |
||||
|
``` |
||||
|
|
||||
|
### Tabulky: |
||||
|
```html |
||||
|
<!-- Základní tabulka --> |
||||
|
<table class="table">...</table> |
||||
|
|
||||
|
<!-- S hover efektem --> |
||||
|
<table class="table table-hover">...</table> |
||||
|
|
||||
|
<!-- Barevné řádky --> |
||||
|
<tr class="table-primary">...</tr> <!-- modrá --> |
||||
|
<tr class="table-success">...</tr> <!-- zelená --> |
||||
|
<tr class="table-danger">...</tr> <!-- červená --> |
||||
|
<tr class="table-warning">...</tr> <!-- žlutá --> |
||||
|
<tr class="table-info">...</tr> <!-- světle modrá --> |
||||
|
<tr class="table-dark">...</tr> <!-- tmavá --> |
||||
|
``` |
||||
|
|
||||
|
### Buttons: |
||||
|
```html |
||||
|
<button class="btn btn-primary">Primary</button> |
||||
|
<button class="btn btn-secondary">Secondary</button> |
||||
|
<button class="btn btn-success">Success</button> |
||||
|
<button class="btn btn-danger">Danger</button> |
||||
|
<button class="btn btn-warning">Warning</button> |
||||
|
<button class="btn btn-outline-primary">Outline</button> |
||||
|
``` |
||||
|
|
||||
|
## 🔗 Související lekce |
||||
|
|
||||
|
- **Předchozí:** 13_bootstrap_intro - úvod do Bootstrapu |
||||
|
- **Další:** 15_bootstrap_components - Bootstrap komponenty |
||||
|
- **Související:** |
||||
|
- 10_flex - Flexbox (pochopení flexbox utility tříd) |
||||
|
- AI/ai_02_semantic_portfolio - sémantická struktura stránky |
||||
@ -0,0 +1,165 @@ |
|||||
|
# 15 - Bootstrap Komponenty |
||||
|
|
||||
|
## 🎯 Co se naučíte |
||||
|
|
||||
|
V této lekci se naučíte používat základní Bootstrap komponenty pro vytváření interaktivních webů. |
||||
|
|
||||
|
## 📚 Témata lekce |
||||
|
|
||||
|
- **Cards** - kartičky pro obsah |
||||
|
- **Buttons** - tlačítka různých stylů |
||||
|
- **Badges** - odznaky pro zvýraznění |
||||
|
- **Alerts** - upozornění a zprávy |
||||
|
- **Progress bars** - indikátory průběhu |
||||
|
- **Breadcrumbs** - navigační drobečková navigace |
||||
|
- **Pagination** - stránkování |
||||
|
- **List groups** - stylované seznamy |
||||
|
- **Forms** - formulářové komponenty |
||||
|
|
||||
|
## 📂 Soubory v projektu |
||||
|
|
||||
|
- `index.html` - ukázky Bootstrap komponent |
||||
|
- `css/bootstrap.css` - Bootstrap CSS |
||||
|
- `js/bootstrap.js` - Bootstrap JS |
||||
|
|
||||
|
## 💻 Ukázky komponent |
||||
|
|
||||
|
### Cards (Kartičky) |
||||
|
```html |
||||
|
<div class="card" style="width: 18rem;"> |
||||
|
<img src="..." class="card-img-top" alt="..."> |
||||
|
<div class="card-body"> |
||||
|
<h5 class="card-title">Nadpis karty</h5> |
||||
|
<p class="card-text">Text karty...</p> |
||||
|
<a href="#" class="btn btn-primary">Tlačítko</a> |
||||
|
</div> |
||||
|
</div> |
||||
|
``` |
||||
|
|
||||
|
### Alerts |
||||
|
```html |
||||
|
<div class="alert alert-primary" role="alert"> |
||||
|
Primární alert |
||||
|
</div> |
||||
|
<div class="alert alert-success" role="alert"> |
||||
|
Úspěšná operace! |
||||
|
</div> |
||||
|
<div class="alert alert-danger" role="alert"> |
||||
|
Chyba! |
||||
|
</div> |
||||
|
``` |
||||
|
|
||||
|
### Badges |
||||
|
```html |
||||
|
<h1>Nadpis <span class="badge bg-secondary">Nový</span></h1> |
||||
|
<button class="btn btn-primary"> |
||||
|
Notifikace <span class="badge bg-danger">4</span> |
||||
|
</button> |
||||
|
``` |
||||
|
|
||||
|
### Progress Bar |
||||
|
```html |
||||
|
<div class="progress"> |
||||
|
<div class="progress-bar" role="progressbar" |
||||
|
style="width: 75%" aria-valuenow="75" |
||||
|
aria-valuemin="0" aria-valuemax="100">75%</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Barevné --> |
||||
|
<div class="progress"> |
||||
|
<div class="progress-bar bg-success" style="width: 50%">50%</div> |
||||
|
</div> |
||||
|
``` |
||||
|
|
||||
|
## 🚀 Jak s lekcí pracovat |
||||
|
|
||||
|
1. Otevřete `index.html` v prohlížeči |
||||
|
2. Prozkoumejte různé komponenty |
||||
|
3. Vyzkoušejte změnit barvy komponent (primary → success) |
||||
|
4. Experimentujte s kombinacemi komponent |
||||
|
5. Zkuste vytvořit vlastní kartu s obrázkem a tlačítkem |
||||
|
6. Prozkoumejte Bootstrap dokumentaci pro více variant |
||||
|
|
||||
|
## 💡 Tip pro studenty |
||||
|
|
||||
|
### Card layout: |
||||
|
```html |
||||
|
<!-- Grid s kartami --> |
||||
|
<div class="row"> |
||||
|
<div class="col-md-4"> |
||||
|
<div class="card"> |
||||
|
<div class="card-body"> |
||||
|
<h5 class="card-title">Karta 1</h5> |
||||
|
<p class="card-text">Obsah...</p> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="col-md-4"> |
||||
|
<div class="card">...</div> |
||||
|
</div> |
||||
|
<div class="col-md-4"> |
||||
|
<div class="card">...</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
``` |
||||
|
|
||||
|
### Bootstrap barvy (color variants): |
||||
|
Většina komponent podporuje barevné varianty: |
||||
|
- **primary** = modrá (hlavní) |
||||
|
- **secondary** = šedá |
||||
|
- **success** = zelená (úspěch) |
||||
|
- **danger** = červená (chyba) |
||||
|
- **warning** = žlutá (varování) |
||||
|
- **info** = světle modrá (info) |
||||
|
- **light** = světlá |
||||
|
- **dark** = tmavá |
||||
|
|
||||
|
### List Groups: |
||||
|
```html |
||||
|
<ul class="list-group"> |
||||
|
<li class="list-group-item">První položka</li> |
||||
|
<li class="list-group-item active">Aktivní položka</li> |
||||
|
<li class="list-group-item">Třetí položka</li> |
||||
|
</ul> |
||||
|
``` |
||||
|
|
||||
|
### Breadcrumbs: |
||||
|
```html |
||||
|
<nav aria-label="breadcrumb"> |
||||
|
<ol class="breadcrumb"> |
||||
|
<li class="breadcrumb-item"><a href="#">Home</a></li> |
||||
|
<li class="breadcrumb-item"><a href="#">Produkty</a></li> |
||||
|
<li class="breadcrumb-item active">Notebook</li> |
||||
|
</ol> |
||||
|
</nav> |
||||
|
``` |
||||
|
|
||||
|
### Pagination: |
||||
|
```html |
||||
|
<nav> |
||||
|
<ul class="pagination"> |
||||
|
<li class="page-item"><a class="page-link" href="#">Předchozí</a></li> |
||||
|
<li class="page-item"><a class="page-link" href="#">1</a></li> |
||||
|
<li class="page-item active"><a class="page-link" href="#">2</a></li> |
||||
|
<li class="page-item"><a class="page-link" href="#">3</a></li> |
||||
|
<li class="page-item"><a class="page-link" href="#">Další</a></li> |
||||
|
</ul> |
||||
|
</nav> |
||||
|
``` |
||||
|
|
||||
|
## 🎨 Praktické využití |
||||
|
|
||||
|
**E-shop produkty** = Cards |
||||
|
**Úspěšné odeslání formuláře** = Alert success |
||||
|
**Počet položek v košíku** = Badge |
||||
|
**Upload souboru** = Progress bar |
||||
|
**Navigace kategoriemi** = Breadcrumbs |
||||
|
**Seznam článků** = Pagination |
||||
|
|
||||
|
## 🔗 Související lekce |
||||
|
|
||||
|
- **Předchozí:** 14_bootstrap_layout - Bootstrap layout |
||||
|
- **Další:** |
||||
|
- 16_carousel - Bootstrap carousel (slider) |
||||
|
- 17_modal - Bootstrap modal (okna) |
||||
|
- **Dokumentace:** [Bootstrap Components](https://getbootstrap.com/docs/5.3/components/) |
||||
@ -0,0 +1,171 @@ |
|||||
|
# 16 - Bootstrap Carousel |
||||
|
|
||||
|
## 🎯 Co se naučíte |
||||
|
|
||||
|
V této lekci se naučíte vytvářet carousel (slider/slideshow) pomocí Bootstrapu. |
||||
|
|
||||
|
## 📚 Témata lekce |
||||
|
|
||||
|
- **Carousel** - posuvný slider obrázků |
||||
|
- **Carousel indicators** - indikátory snímků (tečky) |
||||
|
- **Carousel controls** - ovládací šipky |
||||
|
- **Carousel captions** - popisky k obrázkům |
||||
|
- **Auto-sliding** - automatické přepínání |
||||
|
- **JavaScript carousel API** - ovládání přes JS |
||||
|
- **Data attributes** - `data-bs-ride`, `data-bs-slide-to` |
||||
|
|
||||
|
## 📂 Soubory v projektu |
||||
|
|
||||
|
- `index.html` - HTML s carousel komponentou |
||||
|
- `css/bootstrap.css` - Bootstrap CSS |
||||
|
- `js/bootstrap.js` - Bootstrap JS (DŮLEŽITÝ pro carousel!) |
||||
|
- `images/` - obrázky pro carousel |
||||
|
|
||||
|
## 💻 Základní Carousel |
||||
|
|
||||
|
```html |
||||
|
<div id="carouselExample" class="carousel slide" data-bs-ride="carousel"> |
||||
|
<!-- Indikátory (tečky) --> |
||||
|
<div class="carousel-indicators"> |
||||
|
<button type="button" data-bs-target="#carouselExample" |
||||
|
data-bs-slide-to="0" class="active"></button> |
||||
|
<button type="button" data-bs-target="#carouselExample" |
||||
|
data-bs-slide-to="1"></button> |
||||
|
<button type="button" data-bs-target="#carouselExample" |
||||
|
data-bs-slide-to="2"></button> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Snímky --> |
||||
|
<div class="carousel-inner"> |
||||
|
<div class="carousel-item active"> |
||||
|
<img src="slide1.jpg" class="d-block w-100" alt="Slide 1"> |
||||
|
<div class="carousel-caption"> |
||||
|
<h5>První snímek</h5> |
||||
|
<p>Popis prvního snímku</p> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="carousel-item"> |
||||
|
<img src="slide2.jpg" class="d-block w-100" alt="Slide 2"> |
||||
|
</div> |
||||
|
<div class="carousel-item"> |
||||
|
<img src="slide3.jpg" class="d-block w-100" alt="Slide 3"> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Ovládací šipky --> |
||||
|
<button class="carousel-control-prev" type="button" |
||||
|
data-bs-target="#carouselExample" data-bs-slide="prev"> |
||||
|
<span class="carousel-control-prev-icon"></span> |
||||
|
</button> |
||||
|
<button class="carousel-control-next" type="button" |
||||
|
data-bs-target="#carouselExample" data-bs-slide="next"> |
||||
|
<span class="carousel-control-next-icon"></span> |
||||
|
</button> |
||||
|
</div> |
||||
|
``` |
||||
|
|
||||
|
## 🚀 Jak s lekcí pracovat |
||||
|
|
||||
|
1. Otevřete `index.html` v prohlížeči |
||||
|
2. Sledujte automatické přepínání snímků |
||||
|
3. Vyzkoušejte kliknout na šipky (prev/next) |
||||
|
4. Klikněte na indikátory (tečky) pro přímý přechod |
||||
|
5. Prozkoumejte HTML - všimněte si `data-bs-*` atributů |
||||
|
6. Zkuste přidat další snímek (carousel-item) |
||||
|
7. Změňte caption texty |
||||
|
|
||||
|
## 💡 Tip pro studenty |
||||
|
|
||||
|
### Důležité třídy: |
||||
|
- **carousel slide** - hlavní wrapper |
||||
|
- **carousel-inner** - kontejner pro snímky |
||||
|
- **carousel-item** - jednotlivý snímek |
||||
|
- **carousel-item active** - aktivní (viditelný) snímek |
||||
|
- **d-block w-100** - obrázek na celou šířku |
||||
|
|
||||
|
### Data attributes: |
||||
|
```html |
||||
|
<!-- Automatické spuštění --> |
||||
|
data-bs-ride="carousel" |
||||
|
|
||||
|
<!-- Přechod na konkrétní snímek --> |
||||
|
data-bs-slide-to="0" <!-- první snímek (indexováno od 0) --> |
||||
|
|
||||
|
<!-- Přechod vpřed/vzad --> |
||||
|
data-bs-slide="prev" <!-- předchozí --> |
||||
|
data-bs-slide="next" <!-- další --> |
||||
|
|
||||
|
<!-- Interval mezi snímky (v ms) --> |
||||
|
data-bs-interval="5000" <!-- 5 sekund --> |
||||
|
``` |
||||
|
|
||||
|
### Carousel varianty: |
||||
|
|
||||
|
**Bez automatického přepínání:** |
||||
|
```html |
||||
|
<div class="carousel slide"> <!-- bez data-bs-ride --> |
||||
|
``` |
||||
|
|
||||
|
**Fade efekt místo slide:** |
||||
|
```html |
||||
|
<div class="carousel slide carousel-fade" data-bs-ride="carousel"> |
||||
|
``` |
||||
|
|
||||
|
**Vlastní interval:** |
||||
|
```html |
||||
|
<div class="carousel-item" data-bs-interval="2000"> |
||||
|
<!-- tento snímek se zobrazí 2 sekundy --> |
||||
|
</div> |
||||
|
``` |
||||
|
|
||||
|
### JavaScript API: |
||||
|
|
||||
|
```javascript |
||||
|
// Získání carousel instance |
||||
|
var myCarousel = document.querySelector('#myCarousel') |
||||
|
var carousel = new bootstrap.Carousel(myCarousel) |
||||
|
|
||||
|
// Ovládání |
||||
|
carousel.next() // další snímek |
||||
|
carousel.prev() // předchozí snímek |
||||
|
carousel.to(2) // přechod na snímek 2 |
||||
|
carousel.cycle() // spustit automatické přepínání |
||||
|
carousel.pause() // pozastavit |
||||
|
``` |
||||
|
|
||||
|
### Responzivní carousel: |
||||
|
```html |
||||
|
<!-- Různé výšky pro různá zařízení --> |
||||
|
<style> |
||||
|
.carousel-item img { |
||||
|
height: 400px; |
||||
|
object-fit: cover; |
||||
|
} |
||||
|
|
||||
|
@media (max-width: 768px) { |
||||
|
.carousel-item img { |
||||
|
height: 250px; |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
|
``` |
||||
|
|
||||
|
## ⚠️ Důležité poznámky |
||||
|
|
||||
|
1. **První carousel-item musí mít třídu `active`** |
||||
|
2. **Všechny data-bs-target musí odpovídat ID carouselu** |
||||
|
3. **Bootstrap.js musí být připojen** - carousel funguje na JavaScriptu |
||||
|
4. **Obrázky by měly mít stejné rozměry** pro hezký vzhled |
||||
|
|
||||
|
## 🎯 Praktické využití |
||||
|
|
||||
|
- **Hlavní stránka** - hero slider s produkty |
||||
|
- **Galerie** - slideshow fotek |
||||
|
- **Testimonials** - recenze zákazníků |
||||
|
- **Portfolio** - ukázky projektů |
||||
|
|
||||
|
## 🔗 Související lekce |
||||
|
|
||||
|
- **Předchozí:** 15_bootstrap_components - Bootstrap komponenty |
||||
|
- **Další:** 17_modal - Bootstrap modal |
||||
|
- **Dokumentace:** [Bootstrap Carousel](https://getbootstrap.com/docs/5.3/components/carousel/) |
||||
@ -0,0 +1,224 @@ |
|||||
|
# 17 - Bootstrap Modal |
||||
|
|
||||
|
## 🎯 Co se naučíte |
||||
|
|
||||
|
V této lekci se naučíte vytvářet modální okna (pop-up okna) pomocí Bootstrapu. |
||||
|
|
||||
|
## 📚 Témata lekce |
||||
|
|
||||
|
- **Modal** - modální okno (overlay) |
||||
|
- **Modal struktura** - header, body, footer |
||||
|
- **Data attributes** - `data-bs-toggle`, `data-bs-target` |
||||
|
- **Modal velikosti** - malé, velké, extra velké |
||||
|
- **Scrollable modal** - pro dlouhý obsah |
||||
|
- **Centered modal** - centrované na obrazovce |
||||
|
- **JavaScript Modal API** - otevírání/zavírání přes JS |
||||
|
- **Modal events** - události (show, hide) |
||||
|
|
||||
|
## 📂 Soubory v projektu |
||||
|
|
||||
|
- `index.html` - HTML s modal komponentou |
||||
|
- `css/bootstrap.css` - Bootstrap CSS |
||||
|
- `js/bootstrap.js` - Bootstrap JS (DŮLEŽITÝ pro modal!) |
||||
|
|
||||
|
## 💻 Základní Modal |
||||
|
|
||||
|
```html |
||||
|
<!-- Tlačítko pro otevření modalu --> |
||||
|
<button type="button" class="btn btn-primary" |
||||
|
data-bs-toggle="modal" data-bs-target="#exampleModal"> |
||||
|
Otevřít modal |
||||
|
</button> |
||||
|
|
||||
|
<!-- Modal struktura --> |
||||
|
<div class="modal fade" id="exampleModal" tabindex="-1"> |
||||
|
<div class="modal-dialog"> |
||||
|
<div class="modal-content"> |
||||
|
<!-- Header --> |
||||
|
<div class="modal-header"> |
||||
|
<h5 class="modal-title">Nadpis modalu</h5> |
||||
|
<button type="button" class="btn-close" |
||||
|
data-bs-dismiss="modal"></button> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Body --> |
||||
|
<div class="modal-body"> |
||||
|
<p>Obsah modálního okna...</p> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Footer --> |
||||
|
<div class="modal-footer"> |
||||
|
<button type="button" class="btn btn-secondary" |
||||
|
data-bs-dismiss="modal">Zavřít</button> |
||||
|
<button type="button" class="btn btn-primary">Uložit</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
``` |
||||
|
|
||||
|
## 🚀 Jak s lekcí pracovat |
||||
|
|
||||
|
1. Otevřete `index.html` v prohlížeči |
||||
|
2. Klikněte na tlačítko pro otevření modalu |
||||
|
3. Vyzkoušejte zavření pomocí: |
||||
|
- Křížku (×) |
||||
|
- Tlačítka "Zavřít" |
||||
|
- Kliknutí mimo modal |
||||
|
- Klávesy Esc |
||||
|
4. Prozkoumejte HTML strukturu modalu |
||||
|
5. Zkuste změnit velikost modalu |
||||
|
6. Přidejte formulář do modal-body |
||||
|
|
||||
|
## 💡 Tip pro studenty |
||||
|
|
||||
|
### Data attributes: |
||||
|
|
||||
|
**Otevření modalu:** |
||||
|
```html |
||||
|
<button data-bs-toggle="modal" data-bs-target="#myModal"> |
||||
|
Otevřít |
||||
|
</button> |
||||
|
``` |
||||
|
- **data-bs-toggle="modal"** - zapne modal funkcionalitu |
||||
|
- **data-bs-target="#myModal"** - ID modalu, který se otevře |
||||
|
|
||||
|
**Zavření modalu:** |
||||
|
```html |
||||
|
<button data-bs-dismiss="modal">Zavřít</button> |
||||
|
``` |
||||
|
|
||||
|
### Velikosti modalu: |
||||
|
|
||||
|
```html |
||||
|
<!-- Malý modal --> |
||||
|
<div class="modal-dialog modal-sm"> |
||||
|
|
||||
|
<!-- Normální (výchozí) --> |
||||
|
<div class="modal-dialog"> |
||||
|
|
||||
|
<!-- Velký --> |
||||
|
<div class="modal-dialog modal-lg"> |
||||
|
|
||||
|
<!-- Extra velký --> |
||||
|
<div class="modal-dialog modal-xl"> |
||||
|
|
||||
|
<!-- Celá obrazovka --> |
||||
|
<div class="modal-dialog modal-fullscreen"> |
||||
|
``` |
||||
|
|
||||
|
### Centrovaný modal: |
||||
|
|
||||
|
```html |
||||
|
<div class="modal-dialog modal-dialog-centered"> |
||||
|
<!-- vertikálně centrovaný --> |
||||
|
</div> |
||||
|
``` |
||||
|
|
||||
|
### Scrollable modal (dlouhý obsah): |
||||
|
|
||||
|
```html |
||||
|
<div class="modal-dialog modal-dialog-scrollable"> |
||||
|
<div class="modal-content"> |
||||
|
<div class="modal-body"> |
||||
|
<!-- dlouhý obsah zde --> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
``` |
||||
|
|
||||
|
### JavaScript API: |
||||
|
|
||||
|
```javascript |
||||
|
// Otevření modalu přes JavaScript |
||||
|
var myModal = new bootstrap.Modal(document.getElementById('myModal')) |
||||
|
myModal.show() |
||||
|
|
||||
|
// Zavření |
||||
|
myModal.hide() |
||||
|
|
||||
|
// Přepnutí (toggle) |
||||
|
myModal.toggle() |
||||
|
``` |
||||
|
|
||||
|
### Modal events: |
||||
|
|
||||
|
```javascript |
||||
|
var modalEl = document.getElementById('myModal') |
||||
|
|
||||
|
// Před otevřením |
||||
|
modalEl.addEventListener('show.bs.modal', function () { |
||||
|
console.log('Modal se otevírá') |
||||
|
}) |
||||
|
|
||||
|
// Po otevření |
||||
|
modalEl.addEventListener('shown.bs.modal', function () { |
||||
|
console.log('Modal je otevřený') |
||||
|
}) |
||||
|
|
||||
|
// Před zavřením |
||||
|
modalEl.addEventListener('hide.bs.modal', function () { |
||||
|
console.log('Modal se zavírá') |
||||
|
}) |
||||
|
|
||||
|
// Po zavření |
||||
|
modalEl.addEventListener('hidden.bs.modal', function () { |
||||
|
console.log('Modal je zavřený') |
||||
|
}) |
||||
|
``` |
||||
|
|
||||
|
## 🎯 Praktické příklady |
||||
|
|
||||
|
### Modal s formulářem: |
||||
|
|
||||
|
```html |
||||
|
<div class="modal-body"> |
||||
|
<form> |
||||
|
<div class="mb-3"> |
||||
|
<label for="email" class="form-label">Email</label> |
||||
|
<input type="email" class="form-control" id="email"> |
||||
|
</div> |
||||
|
<div class="mb-3"> |
||||
|
<label for="password" class="form-label">Heslo</label> |
||||
|
<input type="password" class="form-control" id="password"> |
||||
|
</div> |
||||
|
</form> |
||||
|
</div> |
||||
|
``` |
||||
|
|
||||
|
### Potvrzovací dialog: |
||||
|
|
||||
|
```html |
||||
|
<div class="modal-body"> |
||||
|
<p>Opravdu chcete smazat tento záznam?</p> |
||||
|
</div> |
||||
|
<div class="modal-footer"> |
||||
|
<button class="btn btn-secondary" data-bs-dismiss="modal">Ne</button> |
||||
|
<button class="btn btn-danger" onclick="deleteRecord()">Ano, smazat</button> |
||||
|
</div> |
||||
|
``` |
||||
|
|
||||
|
## 🎨 Praktické využití |
||||
|
|
||||
|
- **Přihlášení/Registrace** - formulář v modalu |
||||
|
- **Potvrzení akce** - "Opravdu smazat?" |
||||
|
- **Detaily produktu** - zobrazení více informací |
||||
|
- **Galerie obrázků** - lightbox efekt |
||||
|
- **Video přehrávač** - YouTube/Vimeo embed |
||||
|
- **Notifikace** - důležité zprávy |
||||
|
|
||||
|
## ⚠️ Důležité poznámky |
||||
|
|
||||
|
1. **Modal musí mít jedinečné ID** |
||||
|
2. **Bootstrap.js musí být připojen** - modal funguje na JavaScriptu |
||||
|
3. **data-bs-target musí odpovídat ID modalu** |
||||
|
4. **Vnořené modaly se nedoporučují** (modal v modalu) |
||||
|
5. **Backdrop** (tmavé pozadí) se dá vypnout: `data-bs-backdrop="false"` |
||||
|
|
||||
|
## 🔗 Související lekce |
||||
|
|
||||
|
- **Předchozí:** 16_carousel - Bootstrap carousel |
||||
|
- **Související:** |
||||
|
- 15_bootstrap_components - ostatní komponenty |
||||
|
- AI/ai_04_prep_bootstrap - vlastní modal implementace |
||||
|
- **Dokumentace:** [Bootstrap Modal](https://getbootstrap.com/docs/5.3/components/modal/) |
||||
@ -0,0 +1,181 @@ |
|||||
|
# 18 - Bootstrap Formuláře a DOM Manipulace |
||||
|
|
||||
|
## 🎯 Co se naučíte |
||||
|
|
||||
|
V této lekci se naučíte vytvářet dynamické formuláře s Bootstrapem a DOM manipulací. |
||||
|
|
||||
|
## 📚 Témata lekce |
||||
|
|
||||
|
- **Bootstrap formuláře** - `form-control`, `form-label`, `mb-3` |
||||
|
- **addEventListener** - moderní přístup k event handlingu |
||||
|
- **event.preventDefault()** - zabránění default chování formuláře |
||||
|
- **DOM manipulace** - vytváření elementů pomocí JS |
||||
|
- **createElement()** - vytvoření nového elementu |
||||
|
- **appendChild()** - přidání elementu do DOM |
||||
|
- **Template literals** - backticks pro HTML šablony |
||||
|
- **Bootstrap Cards** - kartičky pro obsah |
||||
|
- **Real-time výpis** - návštěvní kniha |
||||
|
|
||||
|
## 📂 Soubory v projektu |
||||
|
|
||||
|
- `index.html` - kompletní aplikace s formulářem a výpisem |
||||
|
- `css/bootstrap.css` - Bootstrap CSS |
||||
|
|
||||
|
## 💻 Ukázka z lekce |
||||
|
|
||||
|
### Formulář s Bootstrap třídami: |
||||
|
```html |
||||
|
<form id="guestbookForm"> |
||||
|
<div class="mb-3"> |
||||
|
<label for="name" class="form-label">Jméno:</label> |
||||
|
<input type="text" class="form-control" id="name" required> |
||||
|
</div> |
||||
|
<div class="mb-3"> |
||||
|
<label for="message" class="form-label">Zpráva:</label> |
||||
|
<textarea class="form-control" id="message" required></textarea> |
||||
|
</div> |
||||
|
<button type="submit" class="btn btn-danger w-100">ODESLAT</button> |
||||
|
</form> |
||||
|
|
||||
|
<div id="entries"></div> |
||||
|
``` |
||||
|
|
||||
|
### JavaScript - Template Literals (rychlejší): |
||||
|
```javascript |
||||
|
form.addEventListener("submit", function(e) { |
||||
|
e.preventDefault(); |
||||
|
|
||||
|
const name = document.getElementById("name").value; |
||||
|
const message = document.getElementById("message").value; |
||||
|
|
||||
|
// Template literal s backticks |
||||
|
const htmldiv = ` |
||||
|
<div class="alert alert-info"> |
||||
|
<strong>${name}</strong>: ${message} |
||||
|
</div> |
||||
|
`; |
||||
|
|
||||
|
entriesDiv.innerHTML += htmldiv; |
||||
|
form.reset(); |
||||
|
}); |
||||
|
``` |
||||
|
|
||||
|
### JavaScript - createElement (strukturovanější): |
||||
|
```javascript |
||||
|
form.addEventListener("submit", function(e) { |
||||
|
e.preventDefault(); |
||||
|
|
||||
|
const name = document.getElementById("name").value; |
||||
|
const message = document.getElementById("message").value; |
||||
|
|
||||
|
// Vytvoření elementů |
||||
|
const div = document.createElement("div"); |
||||
|
div.className = "alert alert-danger"; |
||||
|
|
||||
|
const strong = document.createElement("strong"); |
||||
|
strong.textContent = name; |
||||
|
|
||||
|
const text = document.createElement("span"); |
||||
|
text.textContent = ": " + message; |
||||
|
|
||||
|
// Složení dohromady |
||||
|
div.appendChild(strong); |
||||
|
div.appendChild(text); |
||||
|
|
||||
|
// Vložení do stránky |
||||
|
entriesDiv.appendChild(div); |
||||
|
|
||||
|
form.reset(); |
||||
|
}); |
||||
|
``` |
||||
|
|
||||
|
## 🚀 Jak s lekcí pracovat |
||||
|
|
||||
|
1. Otevřete `index.html` v prohlížeči |
||||
|
2. Vyplňte jméno a zprávu do formuláře |
||||
|
3. Odešlete formulář - zpráva se objeví pod formulářem |
||||
|
4. Přidejte další zprávy - všechny se zobrazí |
||||
|
5. Prozkoumejte JavaScript kód - dvě různé metody |
||||
|
6. Vyzkoušejte Developer Tools (F12) - sledujte DOM změny |
||||
|
|
||||
|
## 💡 Tip pro studenty |
||||
|
|
||||
|
### addEventListener vs onclick: |
||||
|
```javascript |
||||
|
// ✅ MODERNÍ přístup (doporučený) |
||||
|
form.addEventListener("submit", function(e) { |
||||
|
e.preventDefault(); |
||||
|
// ... |
||||
|
}); |
||||
|
|
||||
|
// ❌ STARŠÍ přístup (méně flexibilní) |
||||
|
<form onsubmit="return handleSubmit()"> |
||||
|
``` |
||||
|
|
||||
|
### event.preventDefault(): |
||||
|
```javascript |
||||
|
form.addEventListener("submit", function(e) { |
||||
|
e.preventDefault(); // Zastaví odeslání a reload stránky |
||||
|
// Můžeme zpracovat data vlastním způsobem |
||||
|
}); |
||||
|
``` |
||||
|
|
||||
|
### Template Literals (backticks): |
||||
|
```javascript |
||||
|
// Starý způsob (konkatenace) |
||||
|
const html = "<div>" + name + ": " + message + "</div>"; |
||||
|
|
||||
|
// ✅ Nový způsob (template literal) |
||||
|
const html = `<div>${name}: ${message}</div>`; |
||||
|
``` |
||||
|
- Používá **backticks** (AltGr + ý) |
||||
|
- **${proměnná}** - vložení hodnoty proměnné |
||||
|
- Podporuje **víceřádkové stringy** |
||||
|
|
||||
|
### innerHTML vs appendChild: |
||||
|
|
||||
|
**innerHTML** - rychlé, ale přepisuje vše: |
||||
|
```javascript |
||||
|
div.innerHTML = `<p>Nový obsah</p>`; |
||||
|
div.innerHTML += `<p>Přidaný obsah</p>`; |
||||
|
``` |
||||
|
|
||||
|
**appendChild** - přidává element, lepší pro DOM manipulaci: |
||||
|
```javascript |
||||
|
const p = document.createElement("p"); |
||||
|
p.textContent = "Nový obsah"; |
||||
|
div.appendChild(p); |
||||
|
``` |
||||
|
|
||||
|
### Bootstrap Form Classes: |
||||
|
```html |
||||
|
<!-- Form group --> |
||||
|
<div class="mb-3"> <!-- margin-bottom: 1rem --> |
||||
|
<label class="form-label">Název</label> |
||||
|
<input class="form-control" type="text"> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Input velikosti --> |
||||
|
<input class="form-control form-control-sm"> <!-- malý --> |
||||
|
<input class="form-control"> <!-- normální --> |
||||
|
<input class="form-control form-control-lg"> <!-- velký --> |
||||
|
|
||||
|
<!-- Full width button --> |
||||
|
<button class="btn btn-primary w-100">Tlačítko</button> |
||||
|
``` |
||||
|
|
||||
|
## 🎯 Praktické využití |
||||
|
|
||||
|
- **Návštěvní kniha** - uživatelé zanechávají vzkazy |
||||
|
- **Chat aplikace** - zprávy v real-time |
||||
|
- **Komentáře** - pod článkem |
||||
|
- **Todo list** - přidávání úkolů |
||||
|
- **Košík** - přidávání produktů |
||||
|
|
||||
|
## 🔗 Související lekce |
||||
|
|
||||
|
- **Předchozí:** |
||||
|
- 17_modal - Bootstrap modal |
||||
|
- 08_js_form - základy formulářů v JS |
||||
|
- **Další:** 19_todolist - Todo list aplikace |
||||
|
- **Související:** AI/ai_03_form_validation - pokročilá validace formulářů |
||||
@ -0,0 +1,220 @@ |
|||||
|
# 19 - Todo List Aplikace |
||||
|
|
||||
|
## 🎯 Co se naučíte |
||||
|
|
||||
|
V této lekci se naučíte vytvořit kompletní Todo List aplikaci s přidáváním, odebíráním a označováním úkolů. |
||||
|
|
||||
|
## 📚 Témata lekce |
||||
|
|
||||
|
- **Todo List aplikace** - praktický projekt |
||||
|
- **Dynamické přidávání položek** do seznamu |
||||
|
- **Odstranění položek** z DOM |
||||
|
- **Toggle stavu** - splněno/nesplněno |
||||
|
- **Local Storage** - ukládání dat v prohlížeči (možná) |
||||
|
- **Event delegation** - efektivní event handling |
||||
|
- **Bootstrap styling** pro moderní UI |
||||
|
- **Textarea** - víceřádkový vstup |
||||
|
|
||||
|
## 📂 Soubory v projektu |
||||
|
|
||||
|
- `index.html` - HTML s formulářem a seznamem |
||||
|
- `todo.js` - JavaScript pro funkcionalitu |
||||
|
- `css/bootstrap.css` - Bootstrap CSS |
||||
|
|
||||
|
## 💻 Ukázka z lekce |
||||
|
|
||||
|
### HTML struktura: |
||||
|
```html |
||||
|
<textarea class="form-control velkytext" id="todoitem" |
||||
|
placeholder="Zde napiš úkol"></textarea> |
||||
|
<button class="btn btn-primary w-100 my-3" onclick="additem()"> |
||||
|
ZAPSAT |
||||
|
</button> |
||||
|
|
||||
|
<ul id="todoList"></ul> |
||||
|
``` |
||||
|
|
||||
|
### JavaScript - Přidání úkolu: |
||||
|
```javascript |
||||
|
function additem() { |
||||
|
const todoitem = document.getElementById("todoitem").value; |
||||
|
|
||||
|
if (todoitem === "") { |
||||
|
alert("Zadej úkol!"); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
const todoList = document.getElementById("todoList"); |
||||
|
|
||||
|
// Vytvoření <li> elementu |
||||
|
const li = document.createElement("li"); |
||||
|
li.className = "todolistitem"; |
||||
|
li.innerHTML = ` |
||||
|
${todoitem} |
||||
|
<button class="btn btn-danger btn-sm ms-2" |
||||
|
onclick="removeitem(this)">Smazat</button> |
||||
|
<button class="btn btn-success btn-sm ms-1" |
||||
|
onclick="toggleitem(this)">Hotovo</button> |
||||
|
`; |
||||
|
|
||||
|
todoList.appendChild(li); |
||||
|
|
||||
|
// Vyčištění textarea |
||||
|
document.getElementById("todoitem").value = ""; |
||||
|
} |
||||
|
|
||||
|
// Odstranění úkolu |
||||
|
function removeitem(button) { |
||||
|
const li = button.parentElement; |
||||
|
li.remove(); |
||||
|
} |
||||
|
|
||||
|
// Toggle splněno/nesplněno |
||||
|
function toggleitem(button) { |
||||
|
const li = button.parentElement; |
||||
|
li.classList.toggle("text-decoration-line-through"); |
||||
|
li.classList.toggle("text-muted"); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## 🚀 Jak s lekcí pracovat |
||||
|
|
||||
|
1. Otevřete `index.html` v prohlížeči |
||||
|
2. Napište úkol do textarey |
||||
|
3. Klikněte na "ZAPSAT" - úkol se přidá do seznamu |
||||
|
4. Vyzkoušejte tlačítko "Hotovo" - přeškrtne úkol |
||||
|
5. Klikněte na "Smazat" - odstraní úkol ze seznamu |
||||
|
6. Přidejte více úkolů a vyzkoušejte všechny funkce |
||||
|
7. Prozkoumejte `todo.js` - jak funguje logika |
||||
|
|
||||
|
## 💡 Tip pro studenty |
||||
|
|
||||
|
### parentElement: |
||||
|
```javascript |
||||
|
function removeitem(button) { |
||||
|
// button = <button> element |
||||
|
// button.parentElement = <li> element (rodič) |
||||
|
const li = button.parentElement; |
||||
|
li.remove(); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### classList.toggle(): |
||||
|
```javascript |
||||
|
// Toggle třídy (přidá/odebere) |
||||
|
element.classList.toggle("active"); |
||||
|
|
||||
|
// Přidá třídu |
||||
|
element.classList.add("active"); |
||||
|
|
||||
|
// Odebere třídu |
||||
|
element.classList.remove("active"); |
||||
|
|
||||
|
// Zkontroluje, zda má třídu |
||||
|
element.classList.contains("active"); // true/false |
||||
|
``` |
||||
|
|
||||
|
### text-decoration-line-through: |
||||
|
```html |
||||
|
<!-- Přeškrtnutý text --> |
||||
|
<p class="text-decoration-line-through">Dokončeno</p> |
||||
|
``` |
||||
|
|
||||
|
### Validace vstupu: |
||||
|
```javascript |
||||
|
if (todoitem === "" || todoitem.trim() === "") { |
||||
|
alert("Zadej úkol!"); |
||||
|
return; // Ukončí funkci |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## 🎯 Rozšíření projektu |
||||
|
|
||||
|
### 1. Local Storage (ukládání dat): |
||||
|
```javascript |
||||
|
// Uložení do local storage |
||||
|
function saveTodos() { |
||||
|
const todos = []; |
||||
|
document.querySelectorAll("#todoList li").forEach(li => { |
||||
|
todos.push(li.textContent); |
||||
|
}); |
||||
|
localStorage.setItem("todos", JSON.stringify(todos)); |
||||
|
} |
||||
|
|
||||
|
// Načtení z local storage |
||||
|
function loadTodos() { |
||||
|
const todos = JSON.parse(localStorage.getItem("todos")) || []; |
||||
|
todos.forEach(todo => { |
||||
|
// Přidat do seznamu |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
// Volat při načtení stránky |
||||
|
window.onload = loadTodos; |
||||
|
``` |
||||
|
|
||||
|
### 2. Počítadlo úkolů: |
||||
|
```html |
||||
|
<p>Celkem úkolů: <span id="totalCount">0</span></p> |
||||
|
<p>Dokončeno: <span id="completedCount">0</span></p> |
||||
|
``` |
||||
|
|
||||
|
```javascript |
||||
|
function updateCounters() { |
||||
|
const total = document.querySelectorAll("#todoList li").length; |
||||
|
const completed = document.querySelectorAll("#todoList li.text-decoration-line-through").length; |
||||
|
|
||||
|
document.getElementById("totalCount").textContent = total; |
||||
|
document.getElementById("completedCount").textContent = completed; |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### 3. Priorita úkolů: |
||||
|
```html |
||||
|
<select id="priority" class="form-select"> |
||||
|
<option value="low">Nízká</option> |
||||
|
<option value="medium">Střední</option> |
||||
|
<option value="high">Vysoká</option> |
||||
|
</select> |
||||
|
``` |
||||
|
|
||||
|
```javascript |
||||
|
// Barevné označení podle priority |
||||
|
if (priority === "high") { |
||||
|
li.classList.add("border-start", "border-danger", "border-5"); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### 4. Editace úkolů: |
||||
|
```javascript |
||||
|
function edititem(button) { |
||||
|
const li = button.parentElement; |
||||
|
const newText = prompt("Upravit úkol:", li.firstChild.textContent); |
||||
|
if (newText) { |
||||
|
// Aktualizovat text úkolu |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## ⚠️ Důležité poznámky |
||||
|
|
||||
|
1. **Validace vstupu** - kontrolujte prázdné hodnoty |
||||
|
2. **trim()** - odstraní mezery na začátku a konci |
||||
|
3. **this** v onclick - odkazuje na button element |
||||
|
4. **remove()** - moderní způsob odstranění elementu |
||||
|
|
||||
|
## 🎨 Praktické využití |
||||
|
|
||||
|
- **Todo List** - osobní úkoly |
||||
|
- **Nákupní seznam** - položky k nákupu |
||||
|
- **Checklist** - kontrolní seznam |
||||
|
- **Poznámky** - rychlé poznámky |
||||
|
- **Cíle** - tracking cílů |
||||
|
|
||||
|
## 🔗 Související lekce |
||||
|
|
||||
|
- **Předchozí:** 18_forms - formuláře a DOM manipulace |
||||
|
- **Další:** 20_ajax - AJAX a Fetch API |
||||
|
- **Související:** |
||||
|
- 11_json - práce s JSON (pro localStorage) |
||||
|
- 06_js_html - základy DOM manipulace |
||||
@ -0,0 +1,228 @@ |
|||||
|
# 20 - AJAX a Fetch API |
||||
|
|
||||
|
## 🎯 Co se naučíte |
||||
|
|
||||
|
V této lekci se naučíte načítat data z externích API pomocí Fetch API. |
||||
|
|
||||
|
## 📚 Témata lekce |
||||
|
|
||||
|
- **AJAX** - Asynchronous JavaScript and XML |
||||
|
- **Fetch API** - moderní způsob načítání dat |
||||
|
- **Promises** - asynchronní operace |
||||
|
- **async/await** - čistší syntax pro asynchronní kód |
||||
|
- **JSON API** - práce s REST API |
||||
|
- **Pokémon API** - praktická ukázka (pokeapi.co) |
||||
|
- **JSONPlaceholder** - testovací API (jsonplaceholder.typicode.com) |
||||
|
- **Error handling** - ošetření chyb při načítání |
||||
|
|
||||
|
## 📂 Soubory v projektu |
||||
|
|
||||
|
- `index.html` - rozcestník |
||||
|
- `pokemon.html` - práce s Pokémon API |
||||
|
- `todofetch.html` - práce s Todo API |
||||
|
- JavaScript soubory pro fetch operace |
||||
|
|
||||
|
## 💻 Základní Fetch |
||||
|
|
||||
|
### Fetch - Promise syntax: |
||||
|
```javascript |
||||
|
// Načtení dat z API |
||||
|
fetch('https://pokeapi.co/api/v2/pokemon/pikachu') |
||||
|
.then(response => response.json()) // Převod na JSON |
||||
|
.then(data => { |
||||
|
console.log(data); |
||||
|
// Práce s daty |
||||
|
document.getElementById("name").textContent = data.name; |
||||
|
}) |
||||
|
.catch(error => { |
||||
|
console.error('Chyba:', error); |
||||
|
alert('Nepodařilo se načíst data'); |
||||
|
}); |
||||
|
``` |
||||
|
|
||||
|
### Fetch - async/await syntax (modernější): |
||||
|
```javascript |
||||
|
async function loadPokemon() { |
||||
|
try { |
||||
|
const response = await fetch('https://pokeapi.co/api/v2/pokemon/pikachu'); |
||||
|
const data = await response.json(); |
||||
|
|
||||
|
console.log(data); |
||||
|
document.getElementById("name").textContent = data.name; |
||||
|
document.getElementById("image").src = data.sprites.front_default; |
||||
|
|
||||
|
} catch (error) { |
||||
|
console.error('Chyba:', error); |
||||
|
alert('Nepodařilo se načíst data'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Volání funkce |
||||
|
loadPokemon(); |
||||
|
``` |
||||
|
|
||||
|
## 🚀 Jak s lekcí pracovat |
||||
|
|
||||
|
1. Otevřete `index.html` v prohlížeči |
||||
|
2. Klikněte na "Práce s Pokémon API" |
||||
|
3. Sledujte načítání dat z API |
||||
|
4. Otevřete **Console** (F12) - uvidíte API odpovědi |
||||
|
5. Vyzkoušejte "Práce s TodoList API" |
||||
|
6. Prozkoumejte JavaScript kód - jak funguje fetch |
||||
|
7. Zkuste změnit URL API a načíst jiná data |
||||
|
|
||||
|
## 💡 Tip pro studenty |
||||
|
|
||||
|
### Fetch API kroky: |
||||
|
|
||||
|
1. **Zavolat fetch()** s URL |
||||
|
2. **Počkat na response** (.then nebo await) |
||||
|
3. **Převést na JSON** (.json()) |
||||
|
4. **Použít data** (zobrazit, zpracovat) |
||||
|
5. **Ošetřit chyby** (.catch nebo try/catch) |
||||
|
|
||||
|
### GET request (načtení dat): |
||||
|
```javascript |
||||
|
fetch('https://api.example.com/data') |
||||
|
.then(response => response.json()) |
||||
|
.then(data => console.log(data)) |
||||
|
.catch(error => console.error(error)); |
||||
|
``` |
||||
|
|
||||
|
### POST request (odeslání dat): |
||||
|
```javascript |
||||
|
fetch('https://api.example.com/data', { |
||||
|
method: 'POST', |
||||
|
headers: { |
||||
|
'Content-Type': 'application/json' |
||||
|
}, |
||||
|
body: JSON.stringify({ |
||||
|
title: 'Nový úkol', |
||||
|
completed: false |
||||
|
}) |
||||
|
}) |
||||
|
.then(response => response.json()) |
||||
|
.then(data => console.log('Vytvořeno:', data)) |
||||
|
.catch(error => console.error('Chyba:', error)); |
||||
|
``` |
||||
|
|
||||
|
### Async/Await pattern: |
||||
|
```javascript |
||||
|
async function getData() { |
||||
|
try { |
||||
|
const response = await fetch(url); |
||||
|
|
||||
|
// Kontrola, zda je response OK |
||||
|
if (!response.ok) { |
||||
|
throw new Error('Network response was not ok'); |
||||
|
} |
||||
|
|
||||
|
const data = await response.json(); |
||||
|
return data; |
||||
|
|
||||
|
} catch (error) { |
||||
|
console.error('Error:', error); |
||||
|
throw error; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Použití |
||||
|
getData() |
||||
|
.then(data => console.log(data)) |
||||
|
.catch(error => console.error(error)); |
||||
|
``` |
||||
|
|
||||
|
### Loading indicator: |
||||
|
```javascript |
||||
|
async function loadData() { |
||||
|
// Zobrazit loading |
||||
|
document.getElementById("loading").style.display = "block"; |
||||
|
|
||||
|
try { |
||||
|
const response = await fetch(url); |
||||
|
const data = await response.json(); |
||||
|
|
||||
|
// Zpracovat data |
||||
|
displayData(data); |
||||
|
|
||||
|
} catch (error) { |
||||
|
console.error(error); |
||||
|
} finally { |
||||
|
// Skrýt loading |
||||
|
document.getElementById("loading").style.display = "none"; |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## 🎯 Praktické příklady |
||||
|
|
||||
|
### Pokémon API: |
||||
|
```javascript |
||||
|
async function loadPokemon(name) { |
||||
|
const url = `https://pokeapi.co/api/v2/pokemon/${name}`; |
||||
|
|
||||
|
const response = await fetch(url); |
||||
|
const pokemon = await response.json(); |
||||
|
|
||||
|
// Zobrazení dat |
||||
|
document.getElementById("pokemonName").textContent = pokemon.name; |
||||
|
document.getElementById("pokemonImage").src = pokemon.sprites.front_default; |
||||
|
document.getElementById("pokemonType").textContent = pokemon.types[0].type.name; |
||||
|
document.getElementById("pokemonWeight").textContent = pokemon.weight; |
||||
|
} |
||||
|
|
||||
|
// Volání |
||||
|
loadPokemon("pikachu"); |
||||
|
``` |
||||
|
|
||||
|
### JSONPlaceholder - Todo API: |
||||
|
```javascript |
||||
|
async function loadTodos() { |
||||
|
const response = await fetch('https://jsonplaceholder.typicode.com/todos'); |
||||
|
const todos = await response.json(); |
||||
|
|
||||
|
// Zobrazit prvních 10 todos |
||||
|
const ul = document.getElementById("todoList"); |
||||
|
todos.slice(0, 10).forEach(todo => { |
||||
|
const li = document.createElement("li"); |
||||
|
li.className = todo.completed ? "completed" : ""; |
||||
|
li.textContent = todo.title; |
||||
|
ul.appendChild(li); |
||||
|
}); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## 🌐 Užitečná testovací API |
||||
|
|
||||
|
- **JSONPlaceholder**: https://jsonplaceholder.typicode.com/ |
||||
|
- Fake REST API pro testování |
||||
|
- Todos, posts, users, comments |
||||
|
|
||||
|
- **PokeAPI**: https://pokeapi.co/ |
||||
|
- Pokémon data |
||||
|
- Obrázky, statistiky, typy |
||||
|
|
||||
|
- **OpenWeather API**: https://openweathermap.org/api |
||||
|
- Počasí (vyžaduje API klíč) |
||||
|
|
||||
|
- **Cat Facts API**: https://catfact.ninja/fact |
||||
|
- Náhodné fakty o kočkách |
||||
|
|
||||
|
## ⚠️ Důležité poznámky |
||||
|
|
||||
|
1. **CORS** - některá API mohou blokovat requesty z prohlížeče |
||||
|
2. **API klíče** - některá API vyžadují registraci a klíč |
||||
|
3. **Rate limiting** - omezení počtu requestů |
||||
|
4. **async funkce** vrací vždy Promise |
||||
|
5. **await** lze použít pouze v async funkci |
||||
|
6. **try/catch** vždy použijte pro error handling |
||||
|
|
||||
|
## 🔗 Související lekce |
||||
|
|
||||
|
- **Předchozí:** |
||||
|
- 19_todolist - Todo list aplikace |
||||
|
- 11_json - práce s JSON |
||||
|
- **Související:** API integrace v reálných projektech |
||||
|
- **Dokumentace:** |
||||
|
- [Fetch API - MDN](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) |
||||
|
- [Async/Await - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) |
||||
@ -0,0 +1,248 @@ |
|||||
|
# Lekce 18: Bootstrap Formuláře a Validace |
||||
|
|
||||
|
## 🎯 Cíl lekce |
||||
|
|
||||
|
Naučit studenty pracovat s Bootstrap formuláři a validací. **Studenti se s validací formulářů setkávají poprvé!** |
||||
|
|
||||
|
## 📝 Osnova lekce |
||||
|
|
||||
|
### Část 1: Základní Bootstrap formuláře (20 min) |
||||
|
- Třída `form-control` pro input, textarea |
||||
|
- Třída `form-select` pro select |
||||
|
- Třída `form-check-input` pro checkbox/radio |
||||
|
- `form-label` pro popisky |
||||
|
- `form-text` pro nápovědu |
||||
|
- Spacing pomocí `mb-3` (margin-bottom) |
||||
|
|
||||
|
### Část 2: HTML5 validace (15 min) |
||||
|
- Atribut `required` - povinné pole |
||||
|
- `minlength` a `maxlength` - omezení délky |
||||
|
- `min` a `max` pro number input |
||||
|
- `pattern` - regulární výrazy pro formát |
||||
|
- `type="email"`, `type="tel"`, `type="number"` |
||||
|
|
||||
|
### Část 3: Bootstrap validační styly (20 min) |
||||
|
- Třídy `is-valid` a `is-invalid` |
||||
|
- `valid-feedback` - zelená zpráva ✅ |
||||
|
- `invalid-feedback` - červená zpráva ❌ |
||||
|
- Třída `needs-validation` na formuláři |
||||
|
- JavaScript validace s `checkValidity()` |
||||
|
- Třída `was-validated` pro zobrazení feedbacku |
||||
|
|
||||
|
### Část 4: Input Groups (10 min) |
||||
|
- `input-group` pro seskupení |
||||
|
- `input-group-text` pro prefix/suffix |
||||
|
- Praktické použití: @ před emailem, +420 před tel., Kč za cenou |
||||
|
|
||||
|
### Část 5: Floating Labels (10 min) |
||||
|
- Moderní vzhled formulářů |
||||
|
- `form-floating` wrapper |
||||
|
- Label se "vyplaví" nahoru při focusu |
||||
|
|
||||
|
### Část 6: Praktický projekt (40 min) |
||||
|
Studenti vytvoří vlastní kontaktní formulář |
||||
|
|
||||
|
## 📁 Soubory v této lekci |
||||
|
|
||||
|
### `index.html` |
||||
|
Hlavní soubor s 5 demo sekcemi: |
||||
|
1. ✅ Základní formulář |
||||
|
2. ✅ HTML5 validace |
||||
|
3. ✅ Bootstrap validace s JavaScriptem |
||||
|
4. ✅ Input Groups |
||||
|
5. ✅ Floating Labels |
||||
|
|
||||
|
**Použití:** Projděte s studenty všechny sekce postupně. Nechte je vyzkoušet formuláře! |
||||
|
|
||||
|
### `template.html` |
||||
|
Startovací šablona pro studenty s: |
||||
|
- ✅ Základní HTML strukturou |
||||
|
- ✅ Jedním ukázkovým polem |
||||
|
- ✅ Připraveným JavaScriptem |
||||
|
- ✅ Nápovědou v sidebaru |
||||
|
|
||||
|
**Použití:** Studenti mohou tuto šablonu použít jako základ pro svůj projekt. |
||||
|
|
||||
|
### `reseni_kontaktni_formular.html` |
||||
|
Kompletní řešení projektu obsahující: |
||||
|
- ✅ Všechna požadovaná pole |
||||
|
- ✅ Floating labels |
||||
|
- ✅ Input group u telefonu (+420) |
||||
|
- ✅ Validaci všech polí |
||||
|
- ✅ Success alert po odeslání |
||||
|
- ✅ BONUS funkce: |
||||
|
- Real-time validace telefonu |
||||
|
- Počítadlo znaků u zprávy |
||||
|
- Scroll k chybám |
||||
|
- Console.log výpis dat |
||||
|
|
||||
|
**Použití:** Ukažte studentům po dokončení jejich projektů. Projděte zejména JavaScript část! |
||||
|
|
||||
|
## 🎯 Projekt pro studenty |
||||
|
|
||||
|
### Zadání: Kontaktní formulář |
||||
|
|
||||
|
Vytvořte plně funkční kontaktní formulář s těmito požadavky: |
||||
|
|
||||
|
#### Povinná pole: |
||||
|
1. **Jméno** - textový input, required |
||||
|
2. **Email** - emailový input, required, správný formát |
||||
|
3. **Předmět** - select s možnostmi, required |
||||
|
4. **Zpráva** - textarea, required, min. 10 znaků |
||||
|
|
||||
|
#### Nepovinné pole: |
||||
|
5. **Telefon** - tel input s pattern `[0-9]{9}` (české číslo) |
||||
|
|
||||
|
#### Další požadavky: |
||||
|
- ✅ Checkbox "Souhlasím se zpracováním údajů" (required) |
||||
|
- ✅ Použít **Floating labels** pro všechna pole |
||||
|
- ✅ **Input group** u telefonu s textem "+420" |
||||
|
- ✅ Zobrazit **Success Alert** po úspěšném odeslání |
||||
|
- ✅ Validovat pomocí JavaScriptu |
||||
|
- ✅ Resetovat formulář po 3-4 sekundách |
||||
|
|
||||
|
#### Bonus úkoly: |
||||
|
- Real-time validace během psaní |
||||
|
- Počítadlo znaků u zprávy |
||||
|
- Scroll k prvnímu chybnému poli |
||||
|
- Vypsat data do console.log |
||||
|
|
||||
|
### Hodnocení (20 bodů): |
||||
|
|
||||
|
- **HTML struktura (5b)** |
||||
|
- Správná struktura formuláře (2b) |
||||
|
- Všechna pole implementována (2b) |
||||
|
- Správné atributy (required, pattern, type) (1b) |
||||
|
|
||||
|
- **Bootstrap styly (5b)** |
||||
|
- Floating labels použity správně (2b) |
||||
|
- Input group u telefonu (1b) |
||||
|
- Správné Bootstrap třídy (form-control, atd.) (2b) |
||||
|
|
||||
|
- **Validace (6b)** |
||||
|
- HTML5 validace správně nastavena (2b) |
||||
|
- JavaScript validace funguje (3b) |
||||
|
- Invalid/valid feedback zprávy (1b) |
||||
|
|
||||
|
- **Funkčnost (4b)** |
||||
|
- Formulář se validuje před odesláním (2b) |
||||
|
- Success alert se zobrazí (1b) |
||||
|
- Formulář se resetuje (1b) |
||||
|
|
||||
|
**Bonusové body (+5b max):** |
||||
|
- Real-time validace (+2b) |
||||
|
- Počítadlo znaků (+1b) |
||||
|
- Scroll k chybám (+1b) |
||||
|
- Console.log výpis (+1b) |
||||
|
|
||||
|
## 💡 Tipy pro učitele |
||||
|
|
||||
|
### Při výuce: |
||||
|
|
||||
|
1. **Začněte prakticky** - Nechte studenty rovnou zkoušet formuláře v `index.html` |
||||
|
|
||||
|
2. **Vysvětlete rozdíl** mezi: |
||||
|
- HTML5 validací (vestavěná v prohlížeči) |
||||
|
- Bootstrap validací (vizuální feedback) |
||||
|
- JavaScript validací (programová kontrola) |
||||
|
|
||||
|
3. **Ukažte DevTools** - Zapněte Console a ukazujte jak funguje `checkValidity()` |
||||
|
|
||||
|
4. **Pattern (RegEx)** - Nebojte se! Ukažte jen jednoduché příklady: |
||||
|
- `[0-9]{9}` = přesně 9 číslic |
||||
|
- `[0-9]{5}` = PSČ (5 číslic) |
||||
|
- Odkažte na regex101.com pro testování |
||||
|
|
||||
|
5. **Event preventDefault** - Vysvětlete proč musíme zastavit defaultní submit |
||||
|
|
||||
|
### Časté chyby studentů: |
||||
|
|
||||
|
❌ **Zapomenou `novalidate` na formuláři** |
||||
|
- Výsledek: Prohlížeč zobrazí vlastní validaci místo Bootstrap stylů |
||||
|
- Řešení: `<form class="needs-validation" novalidate>` |
||||
|
|
||||
|
❌ **Zapomenou `event.preventDefault()`** |
||||
|
- Výsledek: Formulář se odešle a stránka se obnoví |
||||
|
- Řešení: První řádek v event listeneru musí být `event.preventDefault()` |
||||
|
|
||||
|
❌ **Nepoužijí `was-validated` třídu** |
||||
|
- Výsledek: Feedback zprávy se nezobrazí |
||||
|
- Řešení: Po submitu přidat `form.classList.add('was-validated')` |
||||
|
|
||||
|
❌ **Dají `is-valid` přímo do HTML** |
||||
|
- Výsledek: Pole vypadá validní už před vyplněním |
||||
|
- Řešení: Třídy `is-valid`/`is-invalid` se přidávají JavaScriptem, ne v HTML |
||||
|
|
||||
|
❌ **Špatný selector pro feedback** |
||||
|
- Výsledek: Zprávy se nezobrazí u správného pole |
||||
|
- Řešení: `invalid-feedback` musí být sourozenec inputu (na stejné úrovni) |
||||
|
|
||||
|
## 📊 Postup výuky (90 min) |
||||
|
|
||||
|
### Fáze 1: Představení (10 min) |
||||
|
- Ukažte hotový `reseni_kontaktni_formular.html` |
||||
|
- Vyplňte správně i špatně - ukažte rozdíl |
||||
|
- Motivujte studenty: "Tohle dnes vytvoříte!" |
||||
|
|
||||
|
### Fáze 2: Průchod příklady (30 min) |
||||
|
- Otevřete `index.html` |
||||
|
- Projděte sekci po sekci |
||||
|
- Nechte studenty zkoušet každou sekci |
||||
|
- Vysvětlete JavaScript část u sekce 3 |
||||
|
|
||||
|
### Fáze 3: Template a zadání (10 min) |
||||
|
- Ukažte `template.html` |
||||
|
- Projděte zadání z `index.html` (sekce "Projekt pro vás") |
||||
|
- Zodpovězte dotazy |
||||
|
|
||||
|
### Fáze 4: Samostatná práce (35 min) |
||||
|
- Studenti pracují na projektu |
||||
|
- Procházejte mezi nimi |
||||
|
- Pomáhejte s dotazy |
||||
|
|
||||
|
### Fáze 5: Prezentace řešení (5 min) |
||||
|
- Otevřete `reseni_kontaktni_formular.html` |
||||
|
- Projděte kód |
||||
|
- Ukažte bonus funkce |
||||
|
|
||||
|
## 🔗 Navazující lekce |
||||
|
|
||||
|
Po této lekci můžete pokračovat: |
||||
|
- **19 - Alerts & Toasts** (notifikace uživateli) |
||||
|
- **20 - Navbar & Dropdowns** (navigace) |
||||
|
- **Závěrečný projekt** kombinující formuláře s jinými komponenty |
||||
|
|
||||
|
## 📚 Užitečné odkazy pro studenty |
||||
|
|
||||
|
- [Bootstrap Forms dokumentace](https://getbootstrap.com/docs/5.3/forms/overview/) |
||||
|
- [Bootstrap Validation](https://getbootstrap.com/docs/5.3/forms/validation/) |
||||
|
- [HTML5 Input types](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) |
||||
|
- [RegEx tester](https://regex101.com/) |
||||
|
|
||||
|
## ✅ Co by studenti měli umět po této lekci |
||||
|
|
||||
|
Po absolvování lekce by studenti měli být schopni: |
||||
|
|
||||
|
✅ Vytvořit Bootstrap formulář se správnými třídami |
||||
|
✅ Použít HTML5 validační atributy (required, pattern, minlength) |
||||
|
✅ Implementovat JavaScript validaci pomocí `checkValidity()` |
||||
|
✅ Zobrazit validační feedback (zelený/červený) |
||||
|
✅ Vytvořit Input Groups s prefix/suffix |
||||
|
✅ Použít Floating Labels |
||||
|
✅ Zastavit defaultní odeslání formuláře |
||||
|
✅ Zobrazit Alert po úspěšném odeslání |
||||
|
✅ Resetovat formulář po odeslání |
||||
|
|
||||
|
**Bonusové dovednosti:** |
||||
|
- Real-time validace |
||||
|
- Počítadlo znaků |
||||
|
- Scroll k chybám |
||||
|
- Práce s console.log |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
Vytvořeno: 2. prosince 2025 |
||||
|
Předmět: Webové Technologie (WTL) |
||||
|
Cílová skupina: 3. ročník IT |
||||
|
|
||||
|
**Hodně štěstí při výuce! 🚀** |
||||
File diff suppressed because it is too large
File diff suppressed because one or more lines are too long
@ -0,0 +1,429 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang="cs"> |
||||
|
<head> |
||||
|
<meta charset="UTF-8"> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
|
<title>Bootstrap Forms & Validace</title> |
||||
|
<link rel="stylesheet" href="css/bootstrap.css"> |
||||
|
<style> |
||||
|
.demo-section { |
||||
|
margin: 40px 0; |
||||
|
padding: 30px; |
||||
|
border: 2px solid #dee2e6; |
||||
|
border-radius: 10px; |
||||
|
background-color: #f8f9fa; |
||||
|
} |
||||
|
.demo-title { |
||||
|
color: #0d6efd; |
||||
|
margin-bottom: 20px; |
||||
|
} |
||||
|
</style> |
||||
|
</head> |
||||
|
<body class="d-flex flex-column min-vh-100"> |
||||
|
<!-- Header --> |
||||
|
<div class="container"> |
||||
|
<header class="d-flex flex-wrap justify-content-center py-3 mb-3 border-bottom"> |
||||
|
<a href="#" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-dark text-decoration-none fs-4"> |
||||
|
Bootstrap Forms & Validace |
||||
|
</a> |
||||
|
<ul class="nav nav-pills"> |
||||
|
<li class="nav-item"><a href="#zaklad" class="nav-link">Základy</a></li> |
||||
|
<li class="nav-item"><a href="#html-validace" class="nav-link">HTML validace</a></li> |
||||
|
<li class="nav-item"><a href="#bootstrap-validace" class="nav-link">Bootstrap validace</a></li> |
||||
|
<li class="nav-item"><a href="#projekt" class="nav-link active">Projekt</a></li> |
||||
|
</ul> |
||||
|
</header> |
||||
|
</div> |
||||
|
|
||||
|
<!-- MAIN --> |
||||
|
<main class="container"> |
||||
|
<h1 class="display-4 mb-4">Lekce 18: Formuláře a Validace</h1> |
||||
|
|
||||
|
<!-- SEKCE 1: Základní formulář --> |
||||
|
<div class="demo-section" id="zaklad"> |
||||
|
<h2 class="demo-title">1️⃣ Základní Bootstrap Formulář</h2> |
||||
|
<p class="lead">Bootstrap poskytuje krásné styly pro formuláře pomocí třídy <code>form-control</code></p> |
||||
|
|
||||
|
<form> |
||||
|
<!-- Textový input --> |
||||
|
<div class="mb-3"> |
||||
|
<label for="jmeno" class="form-label">Jméno:</label> |
||||
|
<input type="text" class="form-control" id="jmeno" placeholder="Zadejte jméno"> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Email input --> |
||||
|
<div class="mb-3"> |
||||
|
<label for="email" class="form-label">Email:</label> |
||||
|
<input type="email" class="form-control" id="email" placeholder="vas@email.cz"> |
||||
|
<div class="form-text">Zadejte platnou emailovou adresu</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Password --> |
||||
|
<div class="mb-3"> |
||||
|
<label for="heslo" class="form-label">Heslo:</label> |
||||
|
<input type="password" class="form-control" id="heslo"> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Textarea --> |
||||
|
<div class="mb-3"> |
||||
|
<label for="zprava" class="form-label">Zpráva:</label> |
||||
|
<textarea class="form-control" id="zprava" rows="3"></textarea> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Select --> |
||||
|
<div class="mb-3"> |
||||
|
<label for="zeme" class="form-label">Země:</label> |
||||
|
<select class="form-select" id="zeme"> |
||||
|
<option selected>Vyberte zemi...</option> |
||||
|
<option value="cz">Česká republika</option> |
||||
|
<option value="sk">Slovensko</option> |
||||
|
<option value="pl">Polsko</option> |
||||
|
</select> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Checkbox --> |
||||
|
<div class="mb-3 form-check"> |
||||
|
<input type="checkbox" class="form-check-input" id="souhlas"> |
||||
|
<label class="form-check-label" for="souhlas"> |
||||
|
Souhlasím s podmínkami |
||||
|
</label> |
||||
|
</div> |
||||
|
|
||||
|
<button type="submit" class="btn btn-primary">Odeslat</button> |
||||
|
</form> |
||||
|
</div> |
||||
|
|
||||
|
<!-- SEKCE 2: HTML5 validace --> |
||||
|
<div class="demo-section" id="html-validace"> |
||||
|
<h2 class="demo-title">2️⃣ HTML5 Validace (zabudovaná v prohlížeči)</h2> |
||||
|
<p class="lead">Pomocí HTML atributů <code>required</code>, <code>minlength</code>, <code>maxlength</code>, <code>pattern</code></p> |
||||
|
|
||||
|
<div class="alert alert-info"> |
||||
|
<strong>💡 Tip:</strong> Zkuste odeslat formulář bez vyplnění - prohlížeč vás upozorní! |
||||
|
</div> |
||||
|
|
||||
|
<form> |
||||
|
<!-- Required pole --> |
||||
|
<div class="mb-3"> |
||||
|
<label for="jmeno2" class="form-label">Jméno (povinné):</label> |
||||
|
<input type="text" class="form-control" id="jmeno2" required> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Email s required --> |
||||
|
<div class="mb-3"> |
||||
|
<label for="email2" class="form-label">Email (povinný):</label> |
||||
|
<input type="email" class="form-control" id="email2" required> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Min a max length --> |
||||
|
<div class="mb-3"> |
||||
|
<label for="username" class="form-label">Uživatelské jméno (min 3, max 15 znaků):</label> |
||||
|
<input type="text" class="form-control" id="username" |
||||
|
minlength="3" maxlength="15" required> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Pattern (regulární výraz) --> |
||||
|
<div class="mb-3"> |
||||
|
<label for="telefon" class="form-label">Telefon (formát: 123456789):</label> |
||||
|
<input type="tel" class="form-control" id="telefon" |
||||
|
pattern="[0-9]{9}" |
||||
|
placeholder="123456789" |
||||
|
required> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Number s min/max --> |
||||
|
<div class="mb-3"> |
||||
|
<label for="vek" class="form-label">Věk (15-120):</label> |
||||
|
<input type="number" class="form-control" id="vek" |
||||
|
min="15" max="120" required> |
||||
|
</div> |
||||
|
|
||||
|
<button type="submit" class="btn btn-success">Odeslat formulář</button> |
||||
|
</form> |
||||
|
</div> |
||||
|
|
||||
|
<!-- SEKCE 3: Bootstrap validace s vizuálním feedbackem --> |
||||
|
<div class="demo-section" id="bootstrap-validace"> |
||||
|
<h2 class="demo-title">3️⃣ Bootstrap Validace (s vizuálním feedbackem)</h2> |
||||
|
<p class="lead">Bootstrap třídy: <code>is-valid</code> (zelená), <code>is-invalid</code> (červená)</p> |
||||
|
|
||||
|
<!-- Příklad: správně vyplněné pole --> |
||||
|
<h4>✅ Správně vyplněné pole:</h4> |
||||
|
<div class="mb-3"> |
||||
|
<label for="validEmail" class="form-label">Email:</label> |
||||
|
<input type="email" class="form-control is-valid" id="validEmail" value="uzivatel@email.cz"> |
||||
|
<div class="valid-feedback"> |
||||
|
Vypadá to dobře! |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Příklad: špatně vyplněné pole --> |
||||
|
<h4>❌ Špatně vyplněné pole:</h4> |
||||
|
<div class="mb-3"> |
||||
|
<label for="invalidEmail" class="form-label">Email:</label> |
||||
|
<input type="email" class="form-control is-invalid" id="invalidEmail" value="spatnyemail"> |
||||
|
<div class="invalid-feedback"> |
||||
|
Zadejte prosím platnou emailovou adresu! |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<hr class="my-5"> |
||||
|
|
||||
|
<!-- Komplexní formulář s Bootstrap validací --> |
||||
|
<h3>📝 Kompletní registrační formulář s validací</h3> |
||||
|
<div class="alert alert-warning"> |
||||
|
<strong>⚠️ Pozor:</strong> Tento formulář používá JavaScript pro validaci. Klikněte na "Registrovat" a uvidíte validaci v akci! |
||||
|
</div> |
||||
|
|
||||
|
<form id="registrationForm" class="needs-validation" novalidate> |
||||
|
<div class="row"> |
||||
|
<div class="col-md-6 mb-3"> |
||||
|
<label for="regJmeno" class="form-label">Jméno *</label> |
||||
|
<input type="text" class="form-control" id="regJmeno" required> |
||||
|
<div class="invalid-feedback"> |
||||
|
Vyplňte prosím jméno. |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="col-md-6 mb-3"> |
||||
|
<label for="regPrijmeni" class="form-label">Příjmení *</label> |
||||
|
<input type="text" class="form-control" id="regPrijmeni" required> |
||||
|
<div class="invalid-feedback"> |
||||
|
Vyplňte prosím příjmení. |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="mb-3"> |
||||
|
<label for="regEmail" class="form-label">Email *</label> |
||||
|
<input type="email" class="form-control" id="regEmail" required> |
||||
|
<div class="invalid-feedback"> |
||||
|
Zadejte platnou emailovou adresu. |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="mb-3"> |
||||
|
<label for="regUsername" class="form-label">Uživatelské jméno *</label> |
||||
|
<input type="text" class="form-control" id="regUsername" |
||||
|
minlength="3" maxlength="15" required> |
||||
|
<div class="invalid-feedback"> |
||||
|
Uživatelské jméno musí mít 3-15 znaků. |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="mb-3"> |
||||
|
<label for="regHeslo" class="form-label">Heslo *</label> |
||||
|
<input type="password" class="form-control" id="regHeslo" |
||||
|
minlength="6" required> |
||||
|
<div class="invalid-feedback"> |
||||
|
Heslo musí mít alespoň 6 znaků. |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="mb-3"> |
||||
|
<label for="regHeslo2" class="form-label">Heslo znovu *</label> |
||||
|
<input type="password" class="form-control" id="regHeslo2" required> |
||||
|
<div class="invalid-feedback"> |
||||
|
Hesla se musí shodovat. |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="mb-3"> |
||||
|
<label for="regVek" class="form-label">Věk *</label> |
||||
|
<input type="number" class="form-control" id="regVek" |
||||
|
min="15" max="120" required> |
||||
|
<div class="invalid-feedback"> |
||||
|
Věk musí být mezi 15 a 120 lety. |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="mb-3"> |
||||
|
<label for="regZeme" class="form-label">Země *</label> |
||||
|
<select class="form-select" id="regZeme" required> |
||||
|
<option value="">Vyberte...</option> |
||||
|
<option value="cz">Česká republika</option> |
||||
|
<option value="sk">Slovensko</option> |
||||
|
<option value="pl">Polsko</option> |
||||
|
<option value="at">Rakousko</option> |
||||
|
</select> |
||||
|
<div class="invalid-feedback"> |
||||
|
Vyberte prosím zemi. |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="mb-3 form-check"> |
||||
|
<input type="checkbox" class="form-check-input" id="regSouhlas" required> |
||||
|
<label class="form-check-label" for="regSouhlas"> |
||||
|
Souhlasím s podmínkami * |
||||
|
</label> |
||||
|
<div class="invalid-feedback"> |
||||
|
Musíte souhlasit s podmínkami. |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<button type="submit" class="btn btn-primary btn-lg">Registrovat</button> |
||||
|
</form> |
||||
|
|
||||
|
<div id="successMessage" class="alert alert-success mt-4" style="display: none;"> |
||||
|
<h4>✅ Registrace úspěšná!</h4> |
||||
|
<p>Všechna pole byla vyplněna správně.</p> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- SEKCE 4: Input Groups --> |
||||
|
<div class="demo-section"> |
||||
|
<h2 class="demo-title">4️⃣ Input Groups (pole s ikonami/textem)</h2> |
||||
|
<p class="lead">Přidání textu nebo symbolů před/za input pole</p> |
||||
|
|
||||
|
<div class="mb-3"> |
||||
|
<label for="username3" class="form-label">Uživatelské jméno:</label> |
||||
|
<div class="input-group"> |
||||
|
<span class="input-group-text">@</span> |
||||
|
<input type="text" class="form-control" id="username3" placeholder="uzivatel"> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="mb-3"> |
||||
|
<label for="website" class="form-label">Webová stránka:</label> |
||||
|
<div class="input-group"> |
||||
|
<span class="input-group-text">https://</span> |
||||
|
<input type="text" class="form-control" id="website" placeholder="www.example.com"> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="mb-3"> |
||||
|
<label for="cena" class="form-label">Cena:</label> |
||||
|
<div class="input-group"> |
||||
|
<input type="number" class="form-control" id="cena" placeholder="0"> |
||||
|
<span class="input-group-text">Kč</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="mb-3"> |
||||
|
<label for="search" class="form-label">Vyhledávání:</label> |
||||
|
<div class="input-group"> |
||||
|
<input type="text" class="form-control" id="search" placeholder="Hledat..."> |
||||
|
<button class="btn btn-outline-secondary" type="button">🔍</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- SEKCE 5: Floating Labels --> |
||||
|
<div class="demo-section"> |
||||
|
<h2 class="demo-title">5️⃣ Floating Labels (moderní vzhled)</h2> |
||||
|
<p class="lead">Label se zobrazí uvnitř inputu a "vyplave" nahoru po kliknutí</p> |
||||
|
|
||||
|
<div class="form-floating mb-3"> |
||||
|
<input type="text" class="form-control" id="floatingJmeno" placeholder="Jméno"> |
||||
|
<label for="floatingJmeno">Jméno</label> |
||||
|
</div> |
||||
|
|
||||
|
<div class="form-floating mb-3"> |
||||
|
<input type="email" class="form-control" id="floatingEmail" placeholder="name@example.com"> |
||||
|
<label for="floatingEmail">Email</label> |
||||
|
</div> |
||||
|
|
||||
|
<div class="form-floating mb-3"> |
||||
|
<select class="form-select" id="floatingSelect"> |
||||
|
<option selected>Vyberte...</option> |
||||
|
<option value="1">Option 1</option> |
||||
|
<option value="2">Option 2</option> |
||||
|
</select> |
||||
|
<label for="floatingSelect">Výběr</label> |
||||
|
</div> |
||||
|
|
||||
|
<div class="form-floating mb-3"> |
||||
|
<textarea class="form-control" placeholder="Zpráva" id="floatingTextarea" style="height: 100px"></textarea> |
||||
|
<label for="floatingTextarea">Zpráva</label> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- PROJEKT PRO STUDENTY --> |
||||
|
<div class="demo-section bg-warning bg-opacity-10" id="projekt"> |
||||
|
<h2 class="demo-title text-danger">🎯 PROJEKT PRO VÁS: Kontaktní formulář</h2> |
||||
|
<div class="alert alert-danger"> |
||||
|
<h4>📝 Zadání:</h4> |
||||
|
<p>Vytvořte vlastní kontaktní formulář s těmito požadavky:</p> |
||||
|
<ol> |
||||
|
<li><strong>Pole:</strong> Jméno, Email, Telefon, Předmět, Zpráva</li> |
||||
|
<li><strong>Validace:</strong> Všechna pole povinná (kromě Telefon)</li> |
||||
|
<li><strong>Telefon:</strong> Pattern pro české číslo (9 číslic)</li> |
||||
|
<li><strong>Email:</strong> Správný email formát</li> |
||||
|
<li><strong>Zpráva:</strong> Minimálně 10 znaků</li> |
||||
|
<li><strong>Vzhled:</strong> Použijte Floating labels</li> |
||||
|
<li><strong>Bonus:</strong> Přidejte Input group k telefonu (+420)</li> |
||||
|
<li><strong>Submit:</strong> Při úspěšném odeslání zobrazte Alert</li> |
||||
|
</ol> |
||||
|
<p class="mb-0"><strong>Vytvořte nový soubor:</strong> <code>muj_formular.html</code></p> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
</main> |
||||
|
|
||||
|
<!-- Footer --> |
||||
|
<footer class="footer mt-auto py-3 bg-light"> |
||||
|
<div class="container text-center"> |
||||
|
<span class="text-muted">Bootstrap Forms & Validace | WTL 2025</span> |
||||
|
</div> |
||||
|
</footer> |
||||
|
|
||||
|
<!-- JAVASCRIPT pro validaci --> |
||||
|
<script src="js/bootstrap.bundle.js"></script> |
||||
|
<script> |
||||
|
// ===== VALIDACE REGISTRAČNÍHO FORMULÁŘE ===== |
||||
|
|
||||
|
// Najdeme formulář |
||||
|
const form = document.getElementById('registrationForm'); |
||||
|
const successMessage = document.getElementById('successMessage'); |
||||
|
|
||||
|
// Přidáme event listener na submit |
||||
|
form.addEventListener('submit', function(event) { |
||||
|
// Zastavíme defaultní odeslání formuláře |
||||
|
event.preventDefault(); |
||||
|
event.stopPropagation(); |
||||
|
|
||||
|
// Speciální validace: hesla se musí shodovat |
||||
|
const heslo = document.getElementById('regHeslo'); |
||||
|
const heslo2 = document.getElementById('regHeslo2'); |
||||
|
|
||||
|
if (heslo.value !== heslo2.value) { |
||||
|
heslo2.setCustomValidity('Hesla se neshodují'); |
||||
|
} else { |
||||
|
heslo2.setCustomValidity(''); |
||||
|
} |
||||
|
|
||||
|
// Zkontrolujeme validitu formuláře |
||||
|
if (form.checkValidity()) { |
||||
|
// Formulář je validní - zobrazíme success zprávu |
||||
|
form.classList.add('was-validated'); |
||||
|
successMessage.style.display = 'block'; |
||||
|
|
||||
|
// Scrollujeme ke zprávě |
||||
|
successMessage.scrollIntoView({ behavior: 'smooth' }); |
||||
|
|
||||
|
// Resetujeme formulář po 3 sekundách |
||||
|
setTimeout(function() { |
||||
|
form.reset(); |
||||
|
form.classList.remove('was-validated'); |
||||
|
successMessage.style.display = 'none'; |
||||
|
}, 3000); |
||||
|
} else { |
||||
|
// Formulář není validní - zobrazíme chyby |
||||
|
form.classList.add('was-validated'); |
||||
|
successMessage.style.display = 'none'; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
// Real-time validace hesel |
||||
|
document.getElementById('regHeslo2').addEventListener('input', function() { |
||||
|
const heslo = document.getElementById('regHeslo'); |
||||
|
const heslo2 = this; |
||||
|
|
||||
|
if (heslo.value !== heslo2.value) { |
||||
|
heslo2.setCustomValidity('Hesla se neshodují'); |
||||
|
} else { |
||||
|
heslo2.setCustomValidity(''); |
||||
|
} |
||||
|
}); |
||||
|
</script> |
||||
|
</body> |
||||
|
</html> |
||||
File diff suppressed because it is too large
File diff suppressed because one or more lines are too long
@ -0,0 +1,280 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang="cs"> |
||||
|
<head> |
||||
|
<meta charset="UTF-8"> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
|
<title>Kontaktní Formulář - Řešení</title> |
||||
|
<link rel="stylesheet" href="css/bootstrap.css"> |
||||
|
</head> |
||||
|
<body class="d-flex flex-column min-vh-100"> |
||||
|
<!-- Header --> |
||||
|
<div class="container"> |
||||
|
<header class="d-flex flex-wrap justify-content-center py-3 mb-3 border-bottom"> |
||||
|
<a href="#" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-dark text-decoration-none fs-4"> |
||||
|
Kontaktní Formulář |
||||
|
</a> |
||||
|
<ul class="nav nav-pills"> |
||||
|
<li class="nav-item"><a href="index.html" class="nav-link">Zpět na příklady</a></li> |
||||
|
<li class="nav-item"><a href="template.html" class="nav-link">Šablona</a></li> |
||||
|
</ul> |
||||
|
</header> |
||||
|
</div> |
||||
|
|
||||
|
<!-- MAIN --> |
||||
|
<main class="container"> |
||||
|
<div class="row justify-content-center"> |
||||
|
<div class="col-lg-8"> |
||||
|
<h1 class="display-5 mb-2">📧 Kontaktujte nás</h1> |
||||
|
<p class="text-muted mb-4">Vyplňte formulář níže a ozveme se vám co nejdříve.</p> |
||||
|
|
||||
|
<!-- Kontaktní formulář --> |
||||
|
<form id="contactForm" class="needs-validation" novalidate> |
||||
|
|
||||
|
<!-- Jméno - Floating label --> |
||||
|
<div class="form-floating mb-3"> |
||||
|
<input type="text" |
||||
|
class="form-control" |
||||
|
id="jmeno" |
||||
|
placeholder="Jméno" |
||||
|
required> |
||||
|
<label for="jmeno">Jméno *</label> |
||||
|
<div class="invalid-feedback"> |
||||
|
Prosím vyplňte vaše jméno. |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Email - Floating label --> |
||||
|
<div class="form-floating mb-3"> |
||||
|
<input type="email" |
||||
|
class="form-control" |
||||
|
id="email" |
||||
|
placeholder="Email" |
||||
|
required> |
||||
|
<label for="email">Email *</label> |
||||
|
<div class="invalid-feedback"> |
||||
|
Prosím zadejte platnou emailovou adresu. |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Telefon - Input group s +420 a pattern pro 9 číslic --> |
||||
|
<div class="mb-3"> |
||||
|
<label for="telefon" class="form-label">Telefon (nepovinné)</label> |
||||
|
<div class="input-group"> |
||||
|
<span class="input-group-text">+420</span> |
||||
|
<input type="tel" |
||||
|
class="form-control" |
||||
|
id="telefon" |
||||
|
placeholder="123456789" |
||||
|
pattern="[0-9]{9}"> |
||||
|
<div class="invalid-feedback"> |
||||
|
Zadejte telefonní číslo ve formátu 123456789 (9 číslic). |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="form-text">Formát: 9 číslic bez mezer (např. 777123456)</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Předmět - Floating select --> |
||||
|
<div class="form-floating mb-3"> |
||||
|
<select class="form-select" id="predmet" required> |
||||
|
<option value="">Vyberte předmět...</option> |
||||
|
<option value="dotaz">Obecný dotaz</option> |
||||
|
<option value="podpora">Technická podpora</option> |
||||
|
<option value="objednavka">Objednávka</option> |
||||
|
<option value="reklamace">Reklamace</option> |
||||
|
<option value="jine">Jiné</option> |
||||
|
</select> |
||||
|
<label for="predmet">Předmět *</label> |
||||
|
<div class="invalid-feedback"> |
||||
|
Vyberte prosím předmět zprávy. |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Zpráva - Floating textarea s minlength 10 --> |
||||
|
<div class="form-floating mb-3"> |
||||
|
<textarea class="form-control" |
||||
|
id="zprava" |
||||
|
placeholder="Zpráva" |
||||
|
style="height: 150px" |
||||
|
minlength="10" |
||||
|
required></textarea> |
||||
|
<label for="zprava">Zpráva * (min. 10 znaků)</label> |
||||
|
<div class="invalid-feedback"> |
||||
|
Zpráva musí obsahovat alespoň 10 znaků. |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- GDPR souhlas --> |
||||
|
<div class="mb-4 form-check"> |
||||
|
<input type="checkbox" |
||||
|
class="form-check-input" |
||||
|
id="souhlas" |
||||
|
required> |
||||
|
<label class="form-check-label" for="souhlas"> |
||||
|
Souhlasím se zpracováním osobních údajů * |
||||
|
</label> |
||||
|
<div class="invalid-feedback"> |
||||
|
Musíte souhlasit se zpracováním osobních údajů. |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Submit button --> |
||||
|
<div class="d-grid gap-2"> |
||||
|
<button type="submit" class="btn btn-primary btn-lg"> |
||||
|
📨 Odeslat zprávu |
||||
|
</button> |
||||
|
</div> |
||||
|
|
||||
|
<p class="text-muted mt-3 small">* = povinné pole</p> |
||||
|
</form> |
||||
|
|
||||
|
<!-- Success Alert (skrytý, zobrazí se po úspěšném odeslání) --> |
||||
|
<div id="successAlert" class="alert alert-success alert-dismissible fade show mt-4" style="display: none;"> |
||||
|
<h4 class="alert-heading">✅ Zpráva byla odeslána!</h4> |
||||
|
<p>Děkujeme za váš kontakt. Ozveme se vám co nejdříve.</p> |
||||
|
<hr> |
||||
|
<p class="mb-0">Obvykle odpovídáme do 24 hodin.</p> |
||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Info sekce --> |
||||
|
<div class="card mt-5 bg-light"> |
||||
|
<div class="card-body"> |
||||
|
<h5 class="card-title">📞 Další kontaktní možnosti</h5> |
||||
|
<ul class="list-unstyled mb-0"> |
||||
|
<li>📧 Email: info@example.cz</li> |
||||
|
<li>☎️ Telefon: +420 123 456 789</li> |
||||
|
<li>🏢 Adresa: Praha 1, Václavské náměstí 1</li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
</div> |
||||
|
</div> |
||||
|
</main> |
||||
|
|
||||
|
<!-- Footer --> |
||||
|
<footer class="footer mt-auto py-3 bg-light"> |
||||
|
<div class="container text-center"> |
||||
|
<span class="text-muted">Kontaktní Formulář - Řešení | WTL 2025</span> |
||||
|
</div> |
||||
|
</footer> |
||||
|
|
||||
|
<script src="js/bootstrap.bundle.js"></script> |
||||
|
<script> |
||||
|
// ===== JAVASCRIPT VALIDACE ===== |
||||
|
|
||||
|
// 1. Najdeme formulář a success alert |
||||
|
const contactForm = document.getElementById('contactForm'); |
||||
|
const successAlert = document.getElementById('successAlert'); |
||||
|
|
||||
|
// 2. Přidáme event listener na odeslání formuláře |
||||
|
contactForm.addEventListener('submit', function(event) { |
||||
|
// Zastavíme defaultní odeslání formuláře |
||||
|
event.preventDefault(); |
||||
|
event.stopPropagation(); |
||||
|
|
||||
|
// 3. Zkontrolujeme validitu formuláře |
||||
|
if (contactForm.checkValidity()) { |
||||
|
// ✅ Formulář je validní |
||||
|
|
||||
|
// Přidáme třídu pro zobrazení validních polí |
||||
|
contactForm.classList.add('was-validated'); |
||||
|
|
||||
|
// Zobrazíme success zprávu |
||||
|
successAlert.style.display = 'block'; |
||||
|
|
||||
|
// Scrollujeme k success zprávě |
||||
|
successAlert.scrollIntoView({ behavior: 'smooth', block: 'center' }); |
||||
|
|
||||
|
// BONUS: Vypíšeme data z formuláře do console |
||||
|
console.log('=== DATA Z FORMULÁŘE ==='); |
||||
|
console.log('Jméno:', document.getElementById('jmeno').value); |
||||
|
console.log('Email:', document.getElementById('email').value); |
||||
|
console.log('Telefon:', document.getElementById('telefon').value || 'nevyplněno'); |
||||
|
console.log('Předmět:', document.getElementById('predmet').value); |
||||
|
console.log('Zpráva:', document.getElementById('zprava').value); |
||||
|
console.log('Souhlas GDPR:', document.getElementById('souhlas').checked); |
||||
|
|
||||
|
// Resetujeme formulář po 4 sekundách |
||||
|
setTimeout(function() { |
||||
|
contactForm.reset(); |
||||
|
contactForm.classList.remove('was-validated'); |
||||
|
successAlert.style.display = 'none'; |
||||
|
}, 4000); |
||||
|
|
||||
|
} else { |
||||
|
// ❌ Formulář není validní |
||||
|
|
||||
|
// Přidáme třídu pro zobrazení chyb |
||||
|
contactForm.classList.add('was-validated'); |
||||
|
|
||||
|
// Skryjeme success zprávu (kdyby byla zobrazená) |
||||
|
successAlert.style.display = 'none'; |
||||
|
|
||||
|
// Najdeme první invalidní pole a scrollujeme k němu |
||||
|
const firstInvalid = contactForm.querySelector(':invalid'); |
||||
|
if (firstInvalid) { |
||||
|
firstInvalid.focus(); |
||||
|
firstInvalid.scrollIntoView({ behavior: 'smooth', block: 'center' }); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
// ===== BONUS: Real-time validace pro telefon ===== |
||||
|
// Zobrazíme feedback už při psaní (ne až po submitu) |
||||
|
const telefonInput = document.getElementById('telefon'); |
||||
|
|
||||
|
telefonInput.addEventListener('input', function() { |
||||
|
// Pokud je pole prázdné, je to OK (není required) |
||||
|
if (this.value === '') { |
||||
|
this.classList.remove('is-invalid'); |
||||
|
this.classList.remove('is-valid'); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// Zkontrolujeme pattern (9 číslic) |
||||
|
const pattern = /^[0-9]{9}$/; |
||||
|
|
||||
|
if (pattern.test(this.value)) { |
||||
|
this.classList.remove('is-invalid'); |
||||
|
this.classList.add('is-valid'); |
||||
|
} else { |
||||
|
this.classList.remove('is-valid'); |
||||
|
this.classList.add('is-invalid'); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
// ===== BONUS: Počítadlo znaků u zprávy ===== |
||||
|
const zpravaInput = document.getElementById('zprava'); |
||||
|
|
||||
|
// Vytvoříme element pro zobrazení počtu znaků |
||||
|
const counterDiv = document.createElement('div'); |
||||
|
counterDiv.className = 'form-text'; |
||||
|
counterDiv.id = 'zpravaCounter'; |
||||
|
zpravaInput.parentElement.appendChild(counterDiv); |
||||
|
|
||||
|
// Funkce pro update počítadla |
||||
|
function updateCounter() { |
||||
|
const length = zpravaInput.value.length; |
||||
|
const min = 10; |
||||
|
|
||||
|
if (length === 0) { |
||||
|
counterDiv.textContent = `Napište zprávu (min. ${min} znaků)`; |
||||
|
counterDiv.className = 'form-text'; |
||||
|
} else if (length < min) { |
||||
|
counterDiv.textContent = `${length}/${min} znaků - ještě ${min - length} znaků`; |
||||
|
counterDiv.className = 'form-text text-danger'; |
||||
|
} else { |
||||
|
counterDiv.textContent = `${length} znaků ✓`; |
||||
|
counterDiv.className = 'form-text text-success'; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Zavoláme hned na začátku |
||||
|
updateCounter(); |
||||
|
|
||||
|
// A při každé změně |
||||
|
zpravaInput.addEventListener('input', updateCounter); |
||||
|
</script> |
||||
|
</body> |
||||
|
</html> |
||||
@ -0,0 +1,128 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang="cs"> |
||||
|
<head> |
||||
|
<meta charset="UTF-8"> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
|
<title>Bootstrap Formulář - Šablona</title> |
||||
|
<link rel="stylesheet" href="css/bootstrap.css"> |
||||
|
</head> |
||||
|
<body class="d-flex flex-column min-vh-100"> |
||||
|
<!-- Header --> |
||||
|
<div class="container"> |
||||
|
<header class="d-flex flex-wrap justify-content-center py-3 mb-3 border-bottom"> |
||||
|
<a href="#" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-dark text-decoration-none fs-4"> |
||||
|
Můj Formulář |
||||
|
</a> |
||||
|
<ul class="nav nav-pills"> |
||||
|
<li class="nav-item"><a href="index.html" class="nav-link">Zpět na příklady</a></li> |
||||
|
</ul> |
||||
|
</header> |
||||
|
</div> |
||||
|
|
||||
|
<!-- MAIN --> |
||||
|
<main class="container"> |
||||
|
<h1 class="display-4 mb-4">🚀 Startovací šablona pro váš projekt</h1> |
||||
|
|
||||
|
<div class="row"> |
||||
|
<div class="col-md-8"> |
||||
|
<!-- TODO: Zde vytvořte svůj formulář --> |
||||
|
<form id="myForm" class="needs-validation" novalidate> |
||||
|
|
||||
|
<!-- Příklad jednoho pole - můžete použít jako vzor --> |
||||
|
<div class="mb-3"> |
||||
|
<label for="exampleInput" class="form-label">Ukázkové pole:</label> |
||||
|
<input type="text" class="form-control" id="exampleInput" required> |
||||
|
<div class="invalid-feedback"> |
||||
|
Toto pole je povinné. |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- TODO: Přidejte další pole podle zadání --> |
||||
|
|
||||
|
|
||||
|
<button type="submit" class="btn btn-primary">Odeslat</button> |
||||
|
</form> |
||||
|
|
||||
|
<!-- Success zpráva --> |
||||
|
<div id="successAlert" class="alert alert-success mt-4" style="display: none;"> |
||||
|
<h4>✅ Formulář odeslán!</h4> |
||||
|
<p>Všechna pole byla vyplněna správně.</p> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="col-md-4"> |
||||
|
<!-- Nápověda --> |
||||
|
<div class="card bg-light"> |
||||
|
<div class="card-body"> |
||||
|
<h5 class="card-title">💡 Nápověda</h5> |
||||
|
<h6>Bootstrap třídy:</h6> |
||||
|
<ul class="small"> |
||||
|
<li><code>form-control</code> - pro input, textarea</li> |
||||
|
<li><code>form-select</code> - pro select</li> |
||||
|
<li><code>form-label</code> - pro label</li> |
||||
|
<li><code>form-check-input</code> - pro checkbox/radio</li> |
||||
|
<li><code>invalid-feedback</code> - chybová zpráva</li> |
||||
|
<li><code>valid-feedback</code> - úspěšná zpráva</li> |
||||
|
</ul> |
||||
|
|
||||
|
<h6 class="mt-3">HTML atributy:</h6> |
||||
|
<ul class="small"> |
||||
|
<li><code>required</code> - povinné pole</li> |
||||
|
<li><code>minlength="X"</code> - min počet znaků</li> |
||||
|
<li><code>maxlength="X"</code> - max počet znaků</li> |
||||
|
<li><code>pattern="..."</code> - regex vzor</li> |
||||
|
<li><code>min="X"</code> - minimální hodnota</li> |
||||
|
<li><code>max="X"</code> - maximální hodnota</li> |
||||
|
</ul> |
||||
|
|
||||
|
<h6 class="mt-3">Užitečné vzory (pattern):</h6> |
||||
|
<ul class="small"> |
||||
|
<li>Tel.: <code>[0-9]{9}</code></li> |
||||
|
<li>PSČ: <code>[0-9]{5}</code></li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</main> |
||||
|
|
||||
|
<!-- Footer --> |
||||
|
<footer class="footer mt-auto py-3 bg-light"> |
||||
|
<div class="container text-center"> |
||||
|
<span class="text-muted">Vytvořil/a: [VAŠE JMÉNO] | WTL 2025</span> |
||||
|
</div> |
||||
|
</footer> |
||||
|
|
||||
|
<script src="js/bootstrap.bundle.js"></script> |
||||
|
<script> |
||||
|
// TODO: Zde přidejte JavaScript pro validaci |
||||
|
|
||||
|
const form = document.getElementById('myForm'); |
||||
|
const successAlert = document.getElementById('successAlert'); |
||||
|
|
||||
|
form.addEventListener('submit', function(event) { |
||||
|
event.preventDefault(); |
||||
|
event.stopPropagation(); |
||||
|
|
||||
|
// TODO: Přidejte vlastní validaci (např. porovnání hesel) |
||||
|
|
||||
|
if (form.checkValidity()) { |
||||
|
// Formulář je validní |
||||
|
form.classList.add('was-validated'); |
||||
|
successAlert.style.display = 'block'; |
||||
|
|
||||
|
// Resetovat formulář po 3 sekundách |
||||
|
setTimeout(function() { |
||||
|
form.reset(); |
||||
|
form.classList.remove('was-validated'); |
||||
|
successAlert.style.display = 'none'; |
||||
|
}, 3000); |
||||
|
} else { |
||||
|
// Formulář není validní |
||||
|
form.classList.add('was-validated'); |
||||
|
successAlert.style.display = 'none'; |
||||
|
} |
||||
|
}); |
||||
|
</script> |
||||
|
</body> |
||||
|
</html> |
||||
@ -0,0 +1,208 @@ |
|||||
|
# Rychlý návod - XSS Demo (Zjednodušená verze) |
||||
|
|
||||
|
## 📁 Jednoduché soubory pro rychlou demonstraci: |
||||
|
|
||||
|
### ⚠️ `zranitelna_jednoducha.html` - NEBEZPEČNÁ |
||||
|
- Minimální kód |
||||
|
- Používá `innerHTML` - lze hacknout! |
||||
|
- Útoky připravené v pravém sloupci |
||||
|
|
||||
|
### ✅ `bezpecna_jednoducha.html` - BEZPEČNÁ |
||||
|
- Minimální kód |
||||
|
- Používá `textContent` - nelze hacknout! |
||||
|
- Zobrazí XSS útoky jako text |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 🎯 Jak demonstrovat (5 minut): |
||||
|
|
||||
|
### 1. Otevřete `zranitelna_jednoducha.html` |
||||
|
|
||||
|
### 2. Zkopírujte tento útok do pole "Zpráva": |
||||
|
```html |
||||
|
<img src=x onerror="alert('HACKNUTÝ!')"> |
||||
|
``` |
||||
|
**POZOR:** Nepoužívejte `<script>` - prohlížeč ho zablokuje! Používejte event handlery! |
||||
|
|
||||
|
### 3. Klikněte "Přidat záznam" |
||||
|
- **💥 Alert se zobrazí! = ÚTOK USPĚL!** |
||||
|
|
||||
|
### 4. Otevřete `bezpecna_jednoducha.html` |
||||
|
|
||||
|
### 5. Zkuste stejný útok |
||||
|
- ✅ **Zobrazí se jako text, nespustí se!** |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 💡 Další fungující útoky k vyzkoušení: |
||||
|
|
||||
|
```html |
||||
|
<!-- Změna pozadí na červenou --> |
||||
|
<img src=x onerror="document.body.style.background='red'"> |
||||
|
|
||||
|
<!-- Velký červený nápis --> |
||||
|
<h1 style="color:red; font-size:50px">HACKED!</h1> |
||||
|
|
||||
|
<!-- SVG útok --> |
||||
|
<svg onload="alert('XSS')"> |
||||
|
``` |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 💡 Co dělá `e.preventDefault()`? |
||||
|
|
||||
|
```javascript |
||||
|
form.addEventListener('submit', function(e) { |
||||
|
e.preventDefault(); // ← CO TO DĚLÁ? |
||||
|
|
||||
|
// ... zbytek kódu |
||||
|
}); |
||||
|
``` |
||||
|
|
||||
|
### Bez `e.preventDefault()`: |
||||
|
1. Kliknete "Odeslat" |
||||
|
2. Formulář se odešle na server |
||||
|
3. Stránka se **OBNOVÍ** (reload) |
||||
|
4. Ztratíte všechna data! |
||||
|
|
||||
|
### S `e.preventDefault()`: |
||||
|
1. Kliknete "Odeslat" |
||||
|
2. **ZASTAVÍ** normální odeslání |
||||
|
3. Stránka se **NEOBNOVÍ** |
||||
|
4. Můžete zpracovat data v JavaScriptu! |
||||
|
|
||||
|
### Příklad: |
||||
|
```javascript |
||||
|
// ❌ BEZ preventDefault - stránka se obnoví |
||||
|
form.addEventListener('submit', function(e) { |
||||
|
console.log('Odesláno!'); |
||||
|
// → Stránka se obnoví a nic neuvidíte! |
||||
|
}); |
||||
|
|
||||
|
// ✅ S preventDefault - stránka zůstane |
||||
|
form.addEventListener('submit', function(e) { |
||||
|
e.preventDefault(); // Zastaví refresh! |
||||
|
console.log('Odesláno!'); |
||||
|
// → Zpráva se zobrazí v konzoli! |
||||
|
}); |
||||
|
``` |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 🔍 Porovnání kódu: |
||||
|
|
||||
|
### ❌ NEBEZPEČNÉ (innerHTML): |
||||
|
```javascript |
||||
|
const html = `<p><strong>${name}</strong>: ${message}</p>`; |
||||
|
entriesDiv.innerHTML += html; // SPUSTÍ JavaScript! |
||||
|
``` |
||||
|
|
||||
|
### ✅ BEZPEČNÉ (textContent): |
||||
|
```javascript |
||||
|
const div = document.createElement('div'); |
||||
|
const strong = document.createElement('strong'); |
||||
|
strong.textContent = name; // JEN TEXT, ne kód! |
||||
|
|
||||
|
const text = document.createElement('span'); |
||||
|
text.textContent = ': ' + message; // JEN TEXT! |
||||
|
|
||||
|
div.appendChild(strong); |
||||
|
div.appendChild(text); |
||||
|
entriesDiv.appendChild(div); |
||||
|
``` |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 🎓 Pro studenty: |
||||
|
|
||||
|
### Zapamatujte si: |
||||
|
|
||||
|
1. **innerHTML = NEBEZPEČNÉ** ❌ |
||||
|
- Spustí HTML a JavaScript |
||||
|
|
||||
|
2. **textContent = BEZPEČNÉ** ✅ |
||||
|
- Zobrazí jen čistý text |
||||
|
|
||||
|
3. **createElement() = BEZPEČNÉ** ✅ |
||||
|
- Vytvoří čisté DOM elementy |
||||
|
|
||||
|
4. **Zlaté pravidlo:** |
||||
|
``` |
||||
|
🚨 DON'T TRUST THE INPUT! 🚨 |
||||
|
``` |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 🚀 XSS útoky k vyzkoušení: |
||||
|
|
||||
|
### ✅ FUNGUJÍCÍ útoky (použijte tyto!): |
||||
|
|
||||
|
```html |
||||
|
<!-- 1. IMG útok - FUNGUJE! --> |
||||
|
<img src=x onerror="alert('HACKNUTÝ!')"> |
||||
|
|
||||
|
<!-- 2. Změna pozadí - FUNGUJE! --> |
||||
|
<img src=x onerror="document.body.style.background='red'"> |
||||
|
|
||||
|
<!-- 3. SVG útok - FUNGUJE! --> |
||||
|
<svg onload="alert('XSS')"> |
||||
|
|
||||
|
<!-- 4. Input útok - FUNGUJE! --> |
||||
|
<input onfocus="alert('XSS')" autofocus> |
||||
|
|
||||
|
<!-- 5. Změna HTML (bez JS) - FUNGUJE! --> |
||||
|
<h1 style="color:red; font-size:50px">HACKED!</h1> |
||||
|
``` |
||||
|
|
||||
|
### ❌ NEFUNGUJÍCÍ útoky (prohlížeč blokuje): |
||||
|
|
||||
|
```html |
||||
|
<!-- Script tag v innerHTML - NEFUNGUJE! --> |
||||
|
<script>alert('XSS')</script> |
||||
|
<!-- Prohlížeč tohle automaticky zablokuje --> |
||||
|
``` |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 🔍 DŮLEŽITÉ: Proč `<script>` nefunguje? |
||||
|
|
||||
|
### Bezpečnostní opatření prohlížečů: |
||||
|
|
||||
|
**`<script>` tagy přidané přes `innerHTML` se NESPUSTÍ!** |
||||
|
|
||||
|
```javascript |
||||
|
// ❌ Toto NESPUSTÍ JavaScript: |
||||
|
element.innerHTML = '<script>alert(1)</script>'; |
||||
|
``` |
||||
|
|
||||
|
**Proč?** Moderní prohlížeče (Chrome, Firefox, Edge) mají bezpečnostní ochranu. |
||||
|
|
||||
|
### ALE! Event handlery FUNGUJÍ: |
||||
|
|
||||
|
```javascript |
||||
|
// ✅ Toto SPUSTÍ JavaScript: |
||||
|
element.innerHTML = '<img src=x onerror="alert(1)">'; |
||||
|
``` |
||||
|
|
||||
|
**Proto je innerHTML STÁLE nebezpečné!** |
||||
|
|
||||
|
Útočníci nepoužívají `<script>`, ale: |
||||
|
- `onerror` (img, video, audio) |
||||
|
- `onload` (svg, iframe, body) |
||||
|
- `onfocus` (input, textarea) |
||||
|
- `onmouseover` (div, span, a) |
||||
|
- atd. |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## ⚠️ DŮLEŽITÉ! |
||||
|
|
||||
|
- Toto je **jen pro vzdělávání** |
||||
|
- **NIKDY** nepoužívejte zranitelný kód v produkci |
||||
|
- Hacking bez povolení = **TRESTNÝ ČIN** |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
**Hotovo! Teď máte jednoduchou demonstraci XSS útoku.** 🎯 |
||||
@ -0,0 +1,377 @@ |
|||||
|
# Lekce 18a: XSS Demo - "Don't Trust The Input" |
||||
|
|
||||
|
## 🎯 Cíl lekce |
||||
|
|
||||
|
**Naučit studenty základy bezpečnosti webových aplikací** prostřednictvím praktické demonstrace XSS (Cross-Site Scripting) útoku. Studenti pochopí: |
||||
|
- Proč je důležité ošetřovat vstup od uživatelů |
||||
|
- Rozdíl mezi `innerHTML` (nebezpečné) a `textContent` (bezpečné) |
||||
|
- Pravidlo **"DON'T TRUST THE INPUT"** |
||||
|
|
||||
|
## ⚠️ DŮLEŽITÉ UPOZORNĚNÍ |
||||
|
|
||||
|
Tato lekce obsahuje **ZÁMĚRNĚ ZRANITELNÝ KÓD** pro vzdělávací účely! |
||||
|
- ❌ **NIKDY** nepoužívejte zranitelnou verzi v produkčním prostředí |
||||
|
- ✅ Vysvětlete studentům, že jde o **demonstraci bezpečnostní chyby** |
||||
|
- ✅ Zdůrazněte, že **hacking bez povolení je TRESTNÝ ČIN** |
||||
|
|
||||
|
## 📁 Soubory v této lekci |
||||
|
|
||||
|
### 1. `index.html` - Úvodní stránka |
||||
|
- Přehled lekce |
||||
|
- Vysvětlení XSS |
||||
|
- Porovnání nebezpečného vs. bezpečného kódu |
||||
|
- Doporučený postup lekce |
||||
|
|
||||
|
### 2. `zranitelna_verze.html` - NEBEZPEČNÁ verze |
||||
|
- Návštěvní kniha s formulářem |
||||
|
- **Používá `innerHTML`** - umožňuje XSS útoky! |
||||
|
- V pravém sloupci jsou připravené XSS útoky ke zkopírování |
||||
|
- Červené varování nahoře |
||||
|
|
||||
|
### 3. `bezpecna_verze.html` - BEZPEČNÁ verze |
||||
|
- Stejná návštěvní kniha |
||||
|
- **Používá `textContent` a `createElement()`** |
||||
|
- XSS útoky zde nefungují |
||||
|
- Zelené potvrzení bezpečnosti |
||||
|
- Detailní vysvětlení bezpečného kódu |
||||
|
|
||||
|
## 🎓 Postup výuky (60 minut) |
||||
|
|
||||
|
### Fáze 1: Úvod (10 min) |
||||
|
|
||||
|
1. **Otevřete `index.html`** na projektoru |
||||
|
2. **Zeptejte se studentů:** |
||||
|
- "Myslíte si, že formuláře na webu jsou bezpečné?" |
||||
|
- "Co když útočník zadá do formuláře škodlivý kód?" |
||||
|
- "Slyšeli jste o XSS útoku?" |
||||
|
|
||||
|
3. **Vysvětlete XSS:** |
||||
|
- Cross-Site Scripting |
||||
|
- Útočník vkládá škodlivý kód do webové stránky |
||||
|
- Kód se spustí v prohlížeči jiného uživatele |
||||
|
- Může ukrást hesla, cookies, osobní data |
||||
|
|
||||
|
4. **Ukažte zlaté pravidlo:** |
||||
|
``` |
||||
|
🚨 "DON'T TRUST THE INPUT" 🚨 |
||||
|
Nikdy nedůvěřujte tomu, co uživatel zadá! |
||||
|
``` |
||||
|
|
||||
|
### Fáze 2: Demonstrace útoku (20 min) |
||||
|
|
||||
|
1. **Otevřete `zranitelna_verze.html`** |
||||
|
|
||||
|
2. **Základní funkce (2 min):** |
||||
|
- Ukažte normální použití formuláře |
||||
|
- Zadejte běžné jméno a zprávu |
||||
|
- Uveďte: "Takto to vypadá normálně" |
||||
|
|
||||
|
3. **ÚTOK #1 - Alert (3 min):** |
||||
|
```html |
||||
|
<script>alert('Byl jsi hacknutý!')</script> |
||||
|
``` |
||||
|
- Zkopírujte do pole "Zpráva" |
||||
|
- Odešlete formulář |
||||
|
- **BOOM! Alert se zobrazí!** 💥 |
||||
|
- Vysvětlete: "To je XSS útok! JavaScript se spustil!" |
||||
|
|
||||
|
4. **ÚTOK #2 - Změna stránky (3 min):** |
||||
|
```html |
||||
|
<h1 style="color:red; font-size:60px;">HACKED!</h1> |
||||
|
``` |
||||
|
- Vložte do formuláře |
||||
|
- Odešlete |
||||
|
- Stránka se vizuálně změní |
||||
|
- Vysvětlete: "Útočník změnil HTML strukturu!" |
||||
|
|
||||
|
5. **ÚTOK #3 - Krádež cookies (5 min):** |
||||
|
```html |
||||
|
<script>alert('Tvoje cookies: ' + document.cookie)</script> |
||||
|
``` |
||||
|
- Vložte do formuláře |
||||
|
- Alert zobrazí cookies |
||||
|
- **KRITICKY DŮLEŽITÉ:** Vysvětlete, že v cookies mohou být: |
||||
|
- Session ID (přihlášení) |
||||
|
- Tokeny |
||||
|
- Osobní data |
||||
|
- Útočník může cookies odeslat na svůj server! |
||||
|
|
||||
|
6. **ÚTOK #4 - Obrázek s onerror (3 min):** |
||||
|
```html |
||||
|
<img src=x onerror="alert('XSS útok přes obrázek!')"> |
||||
|
``` |
||||
|
- Tento útok funguje i když admin zakáže `<script>` |
||||
|
- Ukažte, že útočníci jsou kreativní |
||||
|
|
||||
|
7. **Diskuze (4 min):** |
||||
|
- "Co si myslíte, co by se stalo na skutečném webu?" |
||||
|
- "Co by mohl útočník udělat s vašimi přihlašovacími údaji?" |
||||
|
- "Je to nebezpečné?" |
||||
|
|
||||
|
### Fáze 3: Bezpečné řešení (15 min) |
||||
|
|
||||
|
1. **Otevřete `bezpecna_verze.html`** |
||||
|
|
||||
|
2. **Zkuste stejné útoky (5 min):** |
||||
|
- Vložte `<script>alert('XSS')</script>` |
||||
|
- Nefunguje! Zobrazí se jako text! ✅ |
||||
|
- Zkuste další útoky - žádný nefunguje |
||||
|
|
||||
|
3. **Vysvětlete rozdíl (5 min):** |
||||
|
- Scrollujte dolů k sekci "Jak to funguje?" |
||||
|
- Ukažte rozdíl v kódu: |
||||
|
|
||||
|
**NEBEZPEČNÉ:** |
||||
|
```javascript |
||||
|
element.innerHTML = `<p>${message}</p>`; |
||||
|
// innerHTML SPUSTÍ JavaScript! |
||||
|
``` |
||||
|
|
||||
|
**BEZPEČNÉ:** |
||||
|
```javascript |
||||
|
const p = document.createElement('p'); |
||||
|
p.textContent = message; |
||||
|
element.appendChild(p); |
||||
|
// textContent NESPUSTÍ JavaScript! |
||||
|
``` |
||||
|
|
||||
|
4. **Projděte bezpečný kód (5 min):** |
||||
|
- Ukažte použití `createElement()` |
||||
|
- Ukažte použití `textContent` |
||||
|
- Vysvětlete: "HTML se zobrazí jako TEXT, ne jako KÓD" |
||||
|
|
||||
|
### Fáze 4: Praktické cvičení (10 min) |
||||
|
|
||||
|
**Zadání pro studenty:** |
||||
|
1. Otevřete oba soubory vedle sebe |
||||
|
2. Porovnejte JavaScript kód |
||||
|
3. Najděte všechny rozdíly |
||||
|
4. Napište do poznámek: |
||||
|
- Co je nebezpečné? |
||||
|
- Co je bezpečné? |
||||
|
- Proč? |
||||
|
|
||||
|
### Fáze 5: Shrnutí a test (5 min) |
||||
|
|
||||
|
**Otázky pro studenty:** |
||||
|
1. ❓ "Co znamená XSS?" |
||||
|
- ✅ Cross-Site Scripting |
||||
|
|
||||
|
2. ❓ "Proč je `innerHTML` nebezpečné?" |
||||
|
- ✅ Spouští HTML a JavaScript kód |
||||
|
|
||||
|
3. ❓ "Co je bezpečnější: `innerHTML` nebo `textContent`?" |
||||
|
- ✅ `textContent` |
||||
|
|
||||
|
4. ❓ "Jaké je zlaté pravidlo bezpečnosti?" |
||||
|
- ✅ "DON'T TRUST THE INPUT" |
||||
|
|
||||
|
5. ❓ "Můžu důvěřovat tomu, co uživatel zadá do formuláře?" |
||||
|
- ✅ NE! NIKDY! |
||||
|
|
||||
|
## 🎯 XSS útoky k vyzkoušení |
||||
|
|
||||
|
### Základní útoky (pro všechny studenty): |
||||
|
|
||||
|
```html |
||||
|
<!-- 1. Jednoduchý alert --> |
||||
|
<script>alert('XSS útok!')</script> |
||||
|
|
||||
|
<!-- 2. Změna textu --> |
||||
|
<h1 style="color:red;">HACKED!</h1> |
||||
|
|
||||
|
<!-- 3. Změna pozadí --> |
||||
|
<script>document.body.style.background = 'red'</script> |
||||
|
|
||||
|
<!-- 4. Obrázek s chybou --> |
||||
|
<img src=x onerror="alert('XSS')"> |
||||
|
|
||||
|
<!-- 5. Zobrazení cookies --> |
||||
|
<script>alert(document.cookie)</script> |
||||
|
``` |
||||
|
|
||||
|
### Pokročilé útoky (pro šikovnější studenty): |
||||
|
|
||||
|
```html |
||||
|
<!-- 6. Přesměrování --> |
||||
|
<script>window.location = 'https://google.com'</script> |
||||
|
|
||||
|
<!-- 7. Změna HTML --> |
||||
|
<script>document.body.innerHTML = '<h1>Byl jsi hacknutý!</h1>'</script> |
||||
|
|
||||
|
<!-- 8. Infinite loop (pozor, může zhavarovat!) --> |
||||
|
<script>while(true){alert('SPAM')}</script> |
||||
|
|
||||
|
<!-- 9. Console log spam --> |
||||
|
<script>setInterval(()=>console.log('HACKED'), 100)</script> |
||||
|
|
||||
|
<!-- 10. Krádež formulářových dat --> |
||||
|
<script> |
||||
|
const data = document.getElementById('name').value; |
||||
|
alert('Ukradl jsem: ' + data); |
||||
|
</script> |
||||
|
``` |
||||
|
|
||||
|
### Kreativní útoky (bonus): |
||||
|
|
||||
|
```html |
||||
|
<!-- 11. Falešný login formulář --> |
||||
|
<div style="position:fixed; top:0; left:0; width:100%; height:100%; background:white; z-index:9999;"> |
||||
|
<h1>Přihlášení vypršelo</h1> |
||||
|
<input type="text" placeholder="Uživatel"> |
||||
|
<input type="password" placeholder="Heslo"> |
||||
|
<button>Přihlásit</button> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 12. Vibrace (na mobilu) --> |
||||
|
<script>navigator.vibrate([200, 100, 200])</script> |
||||
|
|
||||
|
<!-- 13. Text-to-speech (pokud podporováno) --> |
||||
|
<script> |
||||
|
const utterance = new SpeechSynthesisUtterance('Byl jsi hacknutý!'); |
||||
|
speechSynthesis.speak(utterance); |
||||
|
</script> |
||||
|
``` |
||||
|
|
||||
|
## 📊 Hodnocení (doporučené) |
||||
|
|
||||
|
Pokud chcete lekci ohodnotit, můžete použít tento systém: |
||||
|
|
||||
|
### Praktický test (10 bodů): |
||||
|
|
||||
|
1. **Identifikace problému (3b)** |
||||
|
- Student najde `innerHTML` v nebezpečném kódu |
||||
|
- Vysvětlí proč je to problém |
||||
|
|
||||
|
2. **Provedení útoku (2b)** |
||||
|
- Student úspěšně provede XSS útok na zranitelné verzi |
||||
|
|
||||
|
3. **Obrana (3b)** |
||||
|
- Student vysvětlí jak se bránit |
||||
|
- Zmíní `textContent` nebo `createElement()` |
||||
|
|
||||
|
4. **Pravidlo (2b)** |
||||
|
- Student zná a umí vysvětlit "Don't Trust The Input" |
||||
|
|
||||
|
## 💡 Tipy pro učitele |
||||
|
|
||||
|
### Při výuce: |
||||
|
|
||||
|
1. **Dramatizujte:** |
||||
|
- Když se spustí XSS útok, reagujte: "Oh ne! Stránka je kompromitovaná!" |
||||
|
- Studenti si to lépe zapamatují |
||||
|
|
||||
|
2. **Real-world příklady:** |
||||
|
- Facebook měl XSS zranitelnost v 2011 |
||||
|
- Twitter XSS worm v 2010 |
||||
|
- eBay XSS v 2014 |
||||
|
|
||||
|
3. **Etika:** |
||||
|
- **DŮLEŽITÉ:** Zdůrazněte, že hacking bez povolení je TRESTNÝ ČIN |
||||
|
- Vysvětlete rozdíl mezi "ethical hacking" a kyberzločinem |
||||
|
- Toto je edukační prostředí |
||||
|
|
||||
|
4. **Bezpečnostní myšlení:** |
||||
|
- Motivujte studenty myslet jako útočníci |
||||
|
- "Jak bych tohle mohl zneužít?" |
||||
|
- Pak myslet jako obránci: "Jak tomu zabráním?" |
||||
|
|
||||
|
### Časté dotazy studentů: |
||||
|
|
||||
|
**Q: "Kde se s XSS můžu setkat?"** |
||||
|
A: Všude kde je formulář: komentáře, profily, vyhledávání, chat, recenze... |
||||
|
|
||||
|
**Q: "Proč vývojáři dělají takové chyby?"** |
||||
|
A: Někdy neznají rizika, někdy spěchají, někdy zapomenou ošetřit vstup. |
||||
|
|
||||
|
**Q: "Je to jediná bezpečnostní hrozba?"** |
||||
|
A: Ne! Existuje SQL injection, CSRF, clickjacking, a mnoho dalších. Bezpečnost je obrovské téma. |
||||
|
|
||||
|
**Q: "Stačí použít `textContent` a jsem v bezpečí?"** |
||||
|
A: Je to dobrý start, ale bezpečnost má mnoho vrstev. Na serveru musíte data také validovat! |
||||
|
|
||||
|
**Q: "Můžu takhle hacknout Facebook?"** |
||||
|
A: Ne! Velké firmy mají security týmy. A bylo by to TRESTNÉ! Nikdy to nezkoušejte bez povolení. |
||||
|
|
||||
|
## 🔗 Další studium |
||||
|
|
||||
|
### Pro zvídavé studenty doporučte: |
||||
|
|
||||
|
1. **OWASP Top 10** - seznam nejčastějších bezpečnostních hrozeb |
||||
|
2. **PortSwigger Web Security Academy** - zdarma kurzy o web security |
||||
|
3. **HackTheBox** - legální hackování (s povolením) |
||||
|
4. **Bug Bounty programy** - etické hackování za peníze |
||||
|
|
||||
|
### Užitečné nástroje: |
||||
|
|
||||
|
- **Browser DevTools (F12)** - konzole, network, elements |
||||
|
- **Burp Suite** - testování web aplikací |
||||
|
- **ZAP (Zed Attack Proxy)** - open-source security scanner |
||||
|
|
||||
|
## 📝 Domácí úkol (nepovinné) |
||||
|
|
||||
|
**Zadání:** |
||||
|
Vytvořte vlastní mini webovou aplikaci (např. TODO list nebo chat) s těmito požadavky: |
||||
|
|
||||
|
1. Formulář pro přidání položky |
||||
|
2. Zobrazení položek na stránce |
||||
|
3. **Implementujte to BEZPEČNĚ** (použijte `textContent`) |
||||
|
4. V komentářích vysvětlete, proč je to bezpečné |
||||
|
5. Zkuste to "hacknout" - mělo by to selhat! |
||||
|
|
||||
|
**Bonus:** |
||||
|
Vytvořte také zranitelnou verzi a ukažte rozdíl. |
||||
|
|
||||
|
## ✅ Co by studenti měli umět po lekci |
||||
|
|
||||
|
Po této lekci by studenti měli: |
||||
|
|
||||
|
✅ Vědět co je XSS (Cross-Site Scripting) |
||||
|
✅ Umět vysvětlit proč je `innerHTML` nebezpečné |
||||
|
✅ Znát rozdíl mezi `innerHTML` a `textContent` |
||||
|
✅ Umět použít `createElement()` pro tvorbu DOM elementů |
||||
|
✅ Znát pravidlo "DON'T TRUST THE INPUT" |
||||
|
✅ Chápat proč je bezpečnost webů důležitá |
||||
|
✅ Vědět, že hacking bez povolení je trestný čin |
||||
|
✅ Mít základní bezpečnostní mindset při vývoji webů |
||||
|
|
||||
|
## 🚨 Důležité poznámky |
||||
|
|
||||
|
### Pro učitele: |
||||
|
|
||||
|
1. **Tato lekce obsahuje funkční XSS exploity** |
||||
|
- Soubory jsou jen pro vzdělávací účely |
||||
|
- Nikdy je nenahrajte na veřejný server |
||||
|
- Nesdílejte je mimo bezpečné prostředí |
||||
|
|
||||
|
2. **Etika a zákon:** |
||||
|
- Vysvětlete studentům, že: |
||||
|
- Hacking bez povolení = trestný čin |
||||
|
- V ČR: Zákon č. 40/2009 Sb. trestní zákoník, § 230 |
||||
|
- Hrozí až 3 roky vězení |
||||
|
- Ethical hacking se dělá POUZE s písemným povolením |
||||
|
|
||||
|
3. **Zodpovědnost:** |
||||
|
- Zdůrazněte odpovědnost vývojářů |
||||
|
- "Se schopností přichází odpovědnost" |
||||
|
- Vývojáři chrání data a soukromí uživatelů |
||||
|
|
||||
|
## 🎉 Závěr |
||||
|
|
||||
|
Tato lekce poskytuje studentům **praktickou zkušenost s bezpečnostní zranitelností** v kontrolovaném prostředí. Je to jeden z nejlepších způsobů, jak naučit důležitost bezpečného programování. |
||||
|
|
||||
|
**Klíčové sdělení:** |
||||
|
``` |
||||
|
🔐 BEZPEČNOST NENÍ VOLITELNÁ! |
||||
|
🔐 VŽDY OŠETŘUJTE VSTUP! |
||||
|
🔐 DON'T TRUST THE INPUT! |
||||
|
``` |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
Vytvořeno: 2. prosince 2025 |
||||
|
Předmět: Webové Technologie (WTL) |
||||
|
Cílová skupina: 3. ročník IT |
||||
|
Téma: Bezpečnost webových aplikací - XSS |
||||
|
|
||||
|
**Hodně úspěchů při výuce! 🛡️** |
||||
@ -0,0 +1,157 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang="cs"> |
||||
|
<head> |
||||
|
<meta charset="UTF-8"> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
|
<title>Návštěvní kniha - BEZPEČNÁ ✅</title> |
||||
|
<link rel="stylesheet" href="css/bootstrap.css"> |
||||
|
</head> |
||||
|
<body class="bg-light p-4"> |
||||
|
<div class="container"> |
||||
|
<!-- BEZPEČNOST --> |
||||
|
<div class="alert alert-success"> |
||||
|
<h3>✅ BEZPEČNÁ VERZE - nelze hacknout!</h3> |
||||
|
<p>Používá <code>textContent</code> - SPRÁVNĚ!</p> |
||||
|
</div> |
||||
|
|
||||
|
<div class="row"> |
||||
|
<!-- FORMULÁŘ --> |
||||
|
<div class="col-md-6"> |
||||
|
<div class="card"> |
||||
|
<div class="card-header bg-success text-white"> |
||||
|
<h3>📖 Návštěvní kniha</h3> |
||||
|
</div> |
||||
|
<div class="card-body"> |
||||
|
<form id="myForm"> |
||||
|
<div class="mb-3"> |
||||
|
<label class="form-label">Jméno:</label> |
||||
|
<input type="text" class="form-control" id="name"> |
||||
|
</div> |
||||
|
<div class="mb-3"> |
||||
|
<label class="form-label">Zpráva:</label> |
||||
|
<textarea class="form-control" id="message" rows="3"></textarea> |
||||
|
</div> |
||||
|
<button type="submit" class="btn btn-success">Přidat záznam</button> |
||||
|
</form> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- ZKUSTE HACKNOUT --> |
||||
|
<div class="col-md-6"> |
||||
|
<div class="card border-success"> |
||||
|
<div class="card-header bg-dark text-white"> |
||||
|
<h4>🛡️ Zkuste to hacknout!</h4> |
||||
|
</div> |
||||
|
<div class="card-body"> |
||||
|
<h5>Zkuste stejné "útoky":</h5> |
||||
|
|
||||
|
<div class="alert alert-secondary"> |
||||
|
<strong>1. Alert:</strong> |
||||
|
<pre><code><script>alert('HACKNUTÝ!')</script></code></pre> |
||||
|
<small class="text-success">✅ Nefunguje!</small> |
||||
|
</div> |
||||
|
|
||||
|
<div class="alert alert-secondary"> |
||||
|
<strong>2. HTML:</strong> |
||||
|
<pre><code><h1>HACKED!</h1></code></pre> |
||||
|
<small class="text-success">✅ Zobrazí se jako text!</small> |
||||
|
</div> |
||||
|
|
||||
|
<div class="alert alert-info"> |
||||
|
<strong>💡 Výsledek:</strong><br> |
||||
|
Vše se zobrazí jako TEXT, ne jako KÓD! |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- ZÁZNAMY --> |
||||
|
<div class="card mt-4"> |
||||
|
<div class="card-header bg-success text-white"> |
||||
|
<h4>📝 Záznamy</h4> |
||||
|
</div> |
||||
|
<div class="card-body"> |
||||
|
<div id="entries"> |
||||
|
<p class="text-muted">Zatím žádné záznamy...</p> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- VYSVĚTLENÍ --> |
||||
|
<div class="card mt-4 border-success"> |
||||
|
<div class="card-header bg-success text-white"> |
||||
|
<h4>🔍 Bezpečný kód:</h4> |
||||
|
</div> |
||||
|
<div class="card-body"> |
||||
|
<pre class="bg-light p-3"><code>// ✅ BEZPEČNÉ! |
||||
|
const div = document.createElement('div'); |
||||
|
const strong = document.createElement('strong'); |
||||
|
const text = document.createElement('span'); |
||||
|
|
||||
|
strong.textContent = name; // ✅ textContent = BEZPEČNÉ |
||||
|
text.textContent = ': ' + message; // ✅ textContent = BEZPEČNÉ |
||||
|
|
||||
|
div.appendChild(strong); |
||||
|
div.appendChild(text); |
||||
|
entriesDiv.appendChild(div);</code></pre> |
||||
|
<div class="alert alert-success mt-3"> |
||||
|
<strong>Proč je to bezpečné?</strong><br> |
||||
|
<code>textContent</code> zobrazí vše jako ČISTÝ TEXT.<br> |
||||
|
HTML a JavaScript se NIKDY nespustí! |
||||
|
</div> |
||||
|
|
||||
|
<h5 class="mt-4">📊 Porovnání:</h5> |
||||
|
<table class="table table-bordered"> |
||||
|
<tr class="table-danger"> |
||||
|
<td><code>innerHTML</code></td> |
||||
|
<td>❌ NEBEZPEČNÉ</td> |
||||
|
<td>Spustí kód</td> |
||||
|
</tr> |
||||
|
<tr class="table-success"> |
||||
|
<td><code>textContent</code></td> |
||||
|
<td>✅ BEZPEČNÉ</td> |
||||
|
<td>Jen text</td> |
||||
|
</tr> |
||||
|
</table> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<script> |
||||
|
// ===== BEZPEČNÝ KÓD ===== |
||||
|
|
||||
|
const form = document.getElementById('myForm'); |
||||
|
const entriesDiv = document.getElementById('entries'); |
||||
|
|
||||
|
form.addEventListener('submit', function(e) { |
||||
|
e.preventDefault(); // Zastaví odeslání formuláře |
||||
|
|
||||
|
// Získáme data |
||||
|
const name = document.getElementById('name').value; |
||||
|
const message = document.getElementById('message').value; |
||||
|
|
||||
|
// ✅ BEZPEČNÉ: Vytvoříme elementy |
||||
|
const div = document.createElement('div'); |
||||
|
div.className = 'alert alert-info'; |
||||
|
|
||||
|
const strong = document.createElement('strong'); |
||||
|
strong.textContent = name; // ✅ textContent je BEZPEČNÉ! |
||||
|
|
||||
|
const text = document.createElement('span'); |
||||
|
text.textContent = ': ' + message; // ✅ textContent je BEZPEČNÉ! |
||||
|
|
||||
|
// Složíme dohromady |
||||
|
div.appendChild(strong); |
||||
|
div.appendChild(text); |
||||
|
|
||||
|
// Přidáme na stránku |
||||
|
entriesDiv.appendChild(div); |
||||
|
|
||||
|
// Resetujeme formulář |
||||
|
form.reset(); |
||||
|
}); |
||||
|
</script> |
||||
|
</body> |
||||
|
</html> |
||||
@ -0,0 +1,335 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang="cs"> |
||||
|
<head> |
||||
|
<meta charset="UTF-8"> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
|
<title>Návštěvní kniha - BEZPEČNÁ VERZE ✅</title> |
||||
|
<link rel="stylesheet" href="css/bootstrap.css"> |
||||
|
<style> |
||||
|
.entry { |
||||
|
border-left: 4px solid #198754; |
||||
|
transition: all 0.3s; |
||||
|
} |
||||
|
.entry:hover { |
||||
|
background-color: #f8f9fa; |
||||
|
transform: translateX(5px); |
||||
|
} |
||||
|
.safe-banner { |
||||
|
background: linear-gradient(135deg, #198754 0%, #146c43 100%); |
||||
|
color: white; |
||||
|
padding: 20px; |
||||
|
margin-bottom: 30px; |
||||
|
border-radius: 10px; |
||||
|
box-shadow: 0 4px 6px rgba(25, 135, 84, 0.3); |
||||
|
} |
||||
|
</style> |
||||
|
</head> |
||||
|
<body class="bg-light"> |
||||
|
<!-- BEZPEČNOST BANNER --> |
||||
|
<div class="container mt-4"> |
||||
|
<div class="safe-banner"> |
||||
|
<h2>✅ BEZPEČNÁ VERZE ✅</h2> |
||||
|
<p class="mb-0"> |
||||
|
<strong>Tato verze je BEZPEČNÁ!</strong> Používá <code>textContent</code> a správně ošetřuje vstup. |
||||
|
XSS útoky zde NEFUNGUJÍ. |
||||
|
</p> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="container"> |
||||
|
<div class="row"> |
||||
|
<!-- LEVÝ SLOUPEC - Formulář --> |
||||
|
<div class="col-md-6"> |
||||
|
<div class="card shadow-sm"> |
||||
|
<div class="card-header bg-success text-white"> |
||||
|
<h3 class="mb-0">📖 Návštěvní kniha</h3> |
||||
|
</div> |
||||
|
<div class="card-body"> |
||||
|
<form id="guestbookForm"> |
||||
|
<div class="mb-3"> |
||||
|
<label for="name" class="form-label">Vaše jméno:</label> |
||||
|
<input type="text" class="form-control" id="name" required> |
||||
|
</div> |
||||
|
|
||||
|
<div class="mb-3"> |
||||
|
<label for="message" class="form-label">Zpráva:</label> |
||||
|
<textarea class="form-control" id="message" rows="4" required></textarea> |
||||
|
<div class="form-text">Napište nám, co si myslíte...</div> |
||||
|
</div> |
||||
|
|
||||
|
<button type="submit" class="btn btn-success w-100"> |
||||
|
💾 Přidat záznam |
||||
|
</button> |
||||
|
</form> |
||||
|
|
||||
|
<div class="alert alert-success mt-3"> |
||||
|
<small> |
||||
|
<strong>🔒 Tento formulář je BEZPEČNÝ!</strong><br> |
||||
|
Zkuste zadat HTML nebo JavaScript - nezpůsobí to nic nebezpečného. |
||||
|
</small> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- PRAVÝ SLOUPEC - Zkuste to hacknout (nebude fungovat) --> |
||||
|
<div class="col-md-6"> |
||||
|
<div class="card shadow-sm border-success"> |
||||
|
<div class="card-header bg-dark text-white"> |
||||
|
<h4 class="mb-0">🛡️ Zkuste to hacknout!</h4> |
||||
|
</div> |
||||
|
<div class="card-body"> |
||||
|
<h5>Zkuste tyto "útoky" (nebudou fungovat):</h5> |
||||
|
|
||||
|
<div class="alert alert-secondary"> |
||||
|
<strong>1. Alert:</strong> |
||||
|
<pre class="mb-0"><code><script>alert('Pokus o útok')</script></code></pre> |
||||
|
<small class="text-success">✅ Zobrazí se jako text, nespustí se!</small> |
||||
|
</div> |
||||
|
|
||||
|
<div class="alert alert-secondary"> |
||||
|
<strong>2. HTML:</strong> |
||||
|
<pre class="mb-0"><code><h1 style="color:red">HACKED!</h1></code></pre> |
||||
|
<small class="text-success">✅ Zobrazí se jako text!</small> |
||||
|
</div> |
||||
|
|
||||
|
<div class="alert alert-secondary"> |
||||
|
<strong>3. Obrázek:</strong> |
||||
|
<pre class="mb-0"><code><img src=x onerror="alert('XSS')"></code></pre> |
||||
|
<small class="text-success">✅ Nespustí se JavaScript!</small> |
||||
|
</div> |
||||
|
|
||||
|
<div class="alert alert-info mt-3"> |
||||
|
<strong>💡 Výsledek:</strong><br> |
||||
|
Veškerý HTML kód se zobrazí jako obyčejný text, nespustí se! |
||||
|
Stránka je v bezpečí. 🛡️ |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- ZÁZNAMY --> |
||||
|
<div class="row mt-4"> |
||||
|
<div class="col-12"> |
||||
|
<div class="card shadow-sm"> |
||||
|
<div class="card-header bg-success text-white d-flex justify-content-between align-items-center"> |
||||
|
<h4 class="mb-0">📝 Záznamy návštěvníků</h4> |
||||
|
<button class="btn btn-sm btn-light" onclick="clearEntries()">🗑️ Smazat vše</button> |
||||
|
</div> |
||||
|
<div class="card-body"> |
||||
|
<div id="entries"> |
||||
|
<div class="alert alert-info"> |
||||
|
<em>Zatím žádné záznamy. Buďte první!</em> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- VYSVĚTLENÍ --> |
||||
|
<div class="row mt-4 mb-4"> |
||||
|
<div class="col-12"> |
||||
|
<div class="card border-success"> |
||||
|
<div class="card-header bg-success text-white"> |
||||
|
<h4 class="mb-0">🔍 Jak to funguje? (BEZPEČNÝ KÓD)</h4> |
||||
|
</div> |
||||
|
<div class="card-body"> |
||||
|
<h5>Bezpečný kód v JavaScriptu:</h5> |
||||
|
<pre class="bg-light p-3 rounded"><code>// ✅ BEZPEČNÉ - používá createElement a textContent! |
||||
|
|
||||
|
// 1. Vytvoříme elementy pomocí createElement (ne innerHTML!) |
||||
|
const entryDiv = document.createElement('div'); |
||||
|
entryDiv.className = 'entry mb-3 p-3 bg-white rounded shadow-sm'; |
||||
|
|
||||
|
const nameStrong = document.createElement('strong'); |
||||
|
nameStrong.style.color = '#198754'; |
||||
|
nameStrong.style.fontSize = '1.1em'; |
||||
|
|
||||
|
// 2. Nastavíme text pomocí textContent (ne innerHTML!) |
||||
|
nameStrong.textContent = '👤 ' + name; // ✅ BEZPEČNÉ |
||||
|
|
||||
|
const messagePara = document.createElement('p'); |
||||
|
messagePara.textContent = message; // ✅ BEZPEČNÉ |
||||
|
|
||||
|
// 3. Přidáme do stránky |
||||
|
entryDiv.appendChild(nameStrong); |
||||
|
entryDiv.appendChild(messagePara); |
||||
|
entriesDiv.insertBefore(entryDiv, entriesDiv.firstChild);</code></pre> |
||||
|
|
||||
|
<div class="alert alert-success mt-3"> |
||||
|
<strong>✅ Proč je to bezpečné?</strong> |
||||
|
<ul class="mb-0"> |
||||
|
<li><code>textContent</code> vždy vkládá čistý TEXT, ne HTML</li> |
||||
|
<li><code>createElement()</code> vytváří čisté DOM elementy</li> |
||||
|
<li>Jakýkoliv <code><script></code> nebo <code><img></code> tag se zobrazí jako text</li> |
||||
|
<li>JavaScript kód se NIKDY nespustí</li> |
||||
|
<li>Stránka je chráněná před XSS útoky</li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
|
||||
|
<h5 class="mt-4">📚 Pravidlo #1 bezpečnosti:</h5> |
||||
|
<div class="alert alert-warning"> |
||||
|
<h4 class="alert-heading">🚨 "DON'T TRUST THE INPUT"</h4> |
||||
|
<p><strong>NIKDY nedůvěřujte vstupu od uživatele!</strong></p> |
||||
|
<ul class="mb-0"> |
||||
|
<li>Vždy ošetřujte vstup</li> |
||||
|
<li>Používejte <code>textContent</code> místo <code>innerHTML</code></li> |
||||
|
<li>Validujte data na serveru i klientu</li> |
||||
|
<li>Escapujte speciální znaky</li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
|
||||
|
<h5 class="mt-4">Porovnání:</h5> |
||||
|
<table class="table table-bordered"> |
||||
|
<thead class="table-dark"> |
||||
|
<tr> |
||||
|
<th>Metoda</th> |
||||
|
<th>Bezpečnost</th> |
||||
|
<th>Použití</th> |
||||
|
</tr> |
||||
|
</thead> |
||||
|
<tbody> |
||||
|
<tr class="table-danger"> |
||||
|
<td><code>innerHTML</code></td> |
||||
|
<td>❌ NEBEZPEČNÉ</td> |
||||
|
<td>Spustí HTML a JavaScript</td> |
||||
|
</tr> |
||||
|
<tr class="table-success"> |
||||
|
<td><code>textContent</code></td> |
||||
|
<td>✅ BEZPEČNÉ</td> |
||||
|
<td>Vloží jen čistý text</td> |
||||
|
</tr> |
||||
|
<tr class="table-success"> |
||||
|
<td><code>createElement()</code></td> |
||||
|
<td>✅ BEZPEČNÉ</td> |
||||
|
<td>Vytvoří čistý DOM element</td> |
||||
|
</tr> |
||||
|
</tbody> |
||||
|
</table> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<script src="js/bootstrap.bundle.js"></script> |
||||
|
<script> |
||||
|
// ===== BEZPEČNÝ KÓD - POUŽÍVEJTE TOTO! ===== |
||||
|
|
||||
|
const form = document.getElementById('guestbookForm'); |
||||
|
const entriesDiv = document.getElementById('entries'); |
||||
|
|
||||
|
// Načteme záznamy z localStorage při načtení stránky |
||||
|
loadEntries(); |
||||
|
|
||||
|
form.addEventListener('submit', function(e) { |
||||
|
e.preventDefault(); |
||||
|
|
||||
|
// Získáme data z formuláře |
||||
|
const name = document.getElementById('name').value; |
||||
|
const message = document.getElementById('message').value; |
||||
|
|
||||
|
// ✅ BEZPEČNÉ: Vytvoříme elementy pomocí DOM API |
||||
|
createEntry(name, message); |
||||
|
|
||||
|
// Uložíme do localStorage |
||||
|
saveEntry(name, message); |
||||
|
|
||||
|
// Resetujeme formulář |
||||
|
form.reset(); |
||||
|
}); |
||||
|
|
||||
|
function createEntry(name, message) { |
||||
|
// Smažeme "žádné záznamy" zprávu pokud existuje |
||||
|
const emptyAlert = entriesDiv.querySelector('.alert-info'); |
||||
|
if (emptyAlert) { |
||||
|
emptyAlert.remove(); |
||||
|
} |
||||
|
|
||||
|
// ✅ KROK 1: Vytvoříme hlavní kontejner |
||||
|
const entryDiv = document.createElement('div'); |
||||
|
entryDiv.className = 'entry mb-3 p-3 bg-white rounded shadow-sm'; |
||||
|
|
||||
|
// ✅ KROK 2: Vytvoříme header s jménem a časem |
||||
|
const headerDiv = document.createElement('div'); |
||||
|
headerDiv.className = 'd-flex justify-content-between align-items-start'; |
||||
|
|
||||
|
const nameDiv = document.createElement('div'); |
||||
|
|
||||
|
const nameStrong = document.createElement('strong'); |
||||
|
nameStrong.style.color = '#198754'; |
||||
|
nameStrong.style.fontSize = '1.1em'; |
||||
|
nameStrong.textContent = '👤 ' + name; // ✅ textContent je BEZPEČNÉ! |
||||
|
|
||||
|
const timeSmall = document.createElement('small'); |
||||
|
timeSmall.className = 'text-muted ms-2'; |
||||
|
timeSmall.textContent = new Date().toLocaleString('cs-CZ'); |
||||
|
|
||||
|
nameDiv.appendChild(nameStrong); |
||||
|
nameDiv.appendChild(timeSmall); |
||||
|
headerDiv.appendChild(nameDiv); |
||||
|
|
||||
|
// ✅ KROK 3: Přidáme oddělovač |
||||
|
const hr = document.createElement('hr'); |
||||
|
|
||||
|
// ✅ KROK 4: Vytvoříme zprávu |
||||
|
const messagePara = document.createElement('p'); |
||||
|
messagePara.className = 'mb-0'; |
||||
|
messagePara.textContent = message; // ✅ textContent je BEZPEČNÉ! |
||||
|
|
||||
|
// ✅ KROK 5: Složíme vše dohromady |
||||
|
entryDiv.appendChild(headerDiv); |
||||
|
entryDiv.appendChild(hr); |
||||
|
entryDiv.appendChild(messagePara); |
||||
|
|
||||
|
// ✅ KROK 6: Přidáme na začátek seznamu |
||||
|
entriesDiv.insertBefore(entryDiv, entriesDiv.firstChild); |
||||
|
} |
||||
|
|
||||
|
function saveEntry(name, message) { |
||||
|
// Získáme existující záznamy |
||||
|
let entries = JSON.parse(localStorage.getItem('guestbook_safe') || '[]'); |
||||
|
|
||||
|
// Přidáme nový záznam |
||||
|
entries.unshift({ |
||||
|
name: name, |
||||
|
message: message, |
||||
|
timestamp: new Date().toISOString() |
||||
|
}); |
||||
|
|
||||
|
// Uložíme zpět |
||||
|
localStorage.setItem('guestbook_safe', JSON.stringify(entries)); |
||||
|
} |
||||
|
|
||||
|
function loadEntries() { |
||||
|
const entries = JSON.parse(localStorage.getItem('guestbook_safe') || '[]'); |
||||
|
|
||||
|
if (entries.length === 0) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// Smažeme prázdnou zprávu |
||||
|
entriesDiv.innerHTML = ''; |
||||
|
|
||||
|
// ✅ Vytvoříme každý záznam BEZPEČNĚ |
||||
|
entries.forEach(entry => { |
||||
|
createEntry(entry.name, entry.message); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function clearEntries() { |
||||
|
if (confirm('Opravdu chcete smazat všechny záznamy?')) { |
||||
|
localStorage.removeItem('guestbook_safe'); |
||||
|
entriesDiv.innerHTML = '<div class="alert alert-info"><em>Zatím žádné záznamy. Buďte první!</em></div>'; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Info pro console |
||||
|
console.log('%c✅ BEZPEČNÁ VERZE ✅', 'color: green; font-size: 20px; font-weight: bold;'); |
||||
|
console.log('Tato stránka je CHRÁNĚNÁ před XSS útoky!'); |
||||
|
console.log('Používá textContent a createElement() místo innerHTML.'); |
||||
|
</script> |
||||
|
</body> |
||||
|
</html> |
||||
File diff suppressed because it is too large
File diff suppressed because one or more lines are too long
@ -0,0 +1,313 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang="cs"> |
||||
|
<head> |
||||
|
<meta charset="UTF-8"> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
|
<title>XSS Demo - Don't Trust The Input</title> |
||||
|
<link rel="stylesheet" href="css/bootstrap.css"> |
||||
|
<style> |
||||
|
.hero { |
||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
||||
|
color: white; |
||||
|
padding: 60px 0; |
||||
|
margin-bottom: 40px; |
||||
|
} |
||||
|
.version-card { |
||||
|
transition: transform 0.3s, box-shadow 0.3s; |
||||
|
height: 100%; |
||||
|
} |
||||
|
.version-card:hover { |
||||
|
transform: translateY(-5px); |
||||
|
box-shadow: 0 10px 20px rgba(0,0,0,0.2); |
||||
|
} |
||||
|
</style> |
||||
|
</head> |
||||
|
<body> |
||||
|
<!-- HERO SEKCE --> |
||||
|
<div class="hero"> |
||||
|
<div class="container text-center"> |
||||
|
<h1 class="display-3 mb-3">🛡️ XSS Security Demo</h1> |
||||
|
<p class="lead">Lekce bezpečnosti: "Don't Trust The Input"</p> |
||||
|
<p class="fs-5">Porovnání zranitelné vs. bezpečné implementace formuláře</p> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="container mb-5"> |
||||
|
<!-- CO SE NAUČÍTE --> |
||||
|
<div class="row mb-5"> |
||||
|
<div class="col-12"> |
||||
|
<div class="card shadow"> |
||||
|
<div class="card-body"> |
||||
|
<h2 class="card-title">🎯 Co se v této lekci naučíte:</h2> |
||||
|
<div class="row mt-4"> |
||||
|
<div class="col-md-6"> |
||||
|
<ul class="list-unstyled"> |
||||
|
<li class="mb-2">✅ Co je XSS (Cross-Site Scripting) útok</li> |
||||
|
<li class="mb-2">✅ Jak funguje nebezpečný kód s <code>innerHTML</code></li> |
||||
|
<li class="mb-2">✅ Jak útočníci zneužívají formuláře</li> |
||||
|
<li class="mb-2">✅ Reálné příklady XSS útoků</li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
<div class="col-md-6"> |
||||
|
<ul class="list-unstyled"> |
||||
|
<li class="mb-2">✅ Jak správně ošetřit vstup od uživatele</li> |
||||
|
<li class="mb-2">✅ Použití <code>textContent</code> místo <code>innerHTML</code></li> |
||||
|
<li class="mb-2">✅ Bezpečné vytváření DOM elementů</li> |
||||
|
<li class="mb-2">✅ Pravidlo "Don't Trust The Input"</li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- POROVNÁNÍ VERZÍ --> |
||||
|
<h2 class="text-center mb-4">🔍 Vyberte verzi k testování:</h2> |
||||
|
|
||||
|
<div class="row g-4"> |
||||
|
<!-- ZRANITELNÁ VERZE --> |
||||
|
<div class="col-md-6"> |
||||
|
<div class="card version-card border-danger border-3"> |
||||
|
<div class="card-header bg-danger text-white text-center"> |
||||
|
<h3 class="mb-0">⚠️ ZRANITELNÁ VERZE</h3> |
||||
|
</div> |
||||
|
<div class="card-body"> |
||||
|
<div class="text-center mb-3"> |
||||
|
<span class="badge bg-danger fs-5">NEBEZPEČNÁ</span> |
||||
|
</div> |
||||
|
|
||||
|
<h5>Co tato verze dělá ŠPATNĚ:</h5> |
||||
|
<ul> |
||||
|
<li>Používá <code>innerHTML</code></li> |
||||
|
<li>Neošetřuje vstup od uživatele</li> |
||||
|
<li>Spouští HTML a JavaScript kód</li> |
||||
|
<li>Je zranitelná vůči XSS útokům</li> |
||||
|
</ul> |
||||
|
|
||||
|
<div class="alert alert-danger"> |
||||
|
<strong>⚠️ Varování:</strong> Toto je ukázka jak <strong>NEDĚLAT</strong> webové aplikace! |
||||
|
</div> |
||||
|
|
||||
|
<h5 class="mt-3">Zkuste tyto útoky:</h5> |
||||
|
<div class="bg-light p-2 rounded mb-2"> |
||||
|
<code><script>alert('XSS')</script></code> |
||||
|
</div> |
||||
|
<div class="bg-light p-2 rounded mb-2"> |
||||
|
<code><h1>HACKED!</h1></code> |
||||
|
</div> |
||||
|
<div class="bg-light p-2 rounded"> |
||||
|
<code><img src=x onerror="alert('Útok')"></code> |
||||
|
</div> |
||||
|
|
||||
|
<div class="d-grid gap-2 mt-4"> |
||||
|
<a href="zranitelna_verze.html" class="btn btn-danger btn-lg"> |
||||
|
🔓 Otevřít zranitelnou verzi |
||||
|
</a> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- BEZPEČNÁ VERZE --> |
||||
|
<div class="col-md-6"> |
||||
|
<div class="card version-card border-success border-3"> |
||||
|
<div class="card-header bg-success text-white text-center"> |
||||
|
<h3 class="mb-0">✅ BEZPEČNÁ VERZE</h3> |
||||
|
</div> |
||||
|
<div class="card-body"> |
||||
|
<div class="text-center mb-3"> |
||||
|
<span class="badge bg-success fs-5">BEZPEČNÁ</span> |
||||
|
</div> |
||||
|
|
||||
|
<h5>Co tato verze dělá SPRÁVNĚ:</h5> |
||||
|
<ul> |
||||
|
<li>Používá <code>textContent</code></li> |
||||
|
<li>Používá <code>createElement()</code></li> |
||||
|
<li>Ošetřuje veškerý vstup</li> |
||||
|
<li>XSS útoky nefungují!</li> |
||||
|
</ul> |
||||
|
|
||||
|
<div class="alert alert-success"> |
||||
|
<strong>✅ Bezpečnost:</strong> Toto je správný způsob, jak psát bezpečné webové aplikace! |
||||
|
</div> |
||||
|
|
||||
|
<h5 class="mt-3">Zkuste "hacknout":</h5> |
||||
|
<div class="bg-light p-2 rounded mb-2"> |
||||
|
<code><script>alert('XSS')</script></code> |
||||
|
<span class="text-success">→ Zobrazí jako text ✅</span> |
||||
|
</div> |
||||
|
<div class="bg-light p-2 rounded mb-2"> |
||||
|
<code><h1>HACKED!</h1></code> |
||||
|
<span class="text-success">→ Zobrazí jako text ✅</span> |
||||
|
</div> |
||||
|
<div class="bg-light p-2 rounded"> |
||||
|
<code><img src=x onerror="alert('Útok')"></code> |
||||
|
<span class="text-success">→ Nespustí se ✅</span> |
||||
|
</div> |
||||
|
|
||||
|
<div class="d-grid gap-2 mt-4"> |
||||
|
<a href="bezpecna_verze.html" class="btn btn-success btn-lg"> |
||||
|
🔒 Otevřít bezpečnou verzi |
||||
|
</a> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- CO JE XSS --> |
||||
|
<div class="row mt-5"> |
||||
|
<div class="col-12"> |
||||
|
<div class="card shadow"> |
||||
|
<div class="card-header bg-dark text-white"> |
||||
|
<h3 class="mb-0">🧠 Co je XSS (Cross-Site Scripting)?</h3> |
||||
|
</div> |
||||
|
<div class="card-body"> |
||||
|
<p class="lead"> |
||||
|
XSS je bezpečnostní zranitelnost, která umožňuje útočníkovi vložit škodlivý kód |
||||
|
(obvykle JavaScript) do webové stránky. |
||||
|
</p> |
||||
|
|
||||
|
<div class="row mt-4"> |
||||
|
<div class="col-md-6"> |
||||
|
<h5>🔴 Co může útočník udělat:</h5> |
||||
|
<ul> |
||||
|
<li>Ukrást přihlašovací údaje (cookies, session)</li> |
||||
|
<li>Změnit obsah stránky</li> |
||||
|
<li>Přesměrovat na phishingovou stránku</li> |
||||
|
<li>Instalovat keylogger (zaznamenávat stisky kláves)</li> |
||||
|
<li>Spustit malware</li> |
||||
|
<li>Zneužít identitu uživatele</li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
<div class="col-md-6"> |
||||
|
<h5>🛡️ Jak se bránit:</h5> |
||||
|
<ul> |
||||
|
<li><strong>Nikdy nedůvěřujte vstupu!</strong> (Don't Trust The Input)</li> |
||||
|
<li>Používejte <code>textContent</code> místo <code>innerHTML</code></li> |
||||
|
<li>Používejte <code>createElement()</code> pro DOM elementy</li> |
||||
|
<li>Validujte a sanitizujte vstup</li> |
||||
|
<li>Escapujte HTML entity</li> |
||||
|
<li>Používejte Content Security Policy (CSP)</li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="alert alert-warning mt-4"> |
||||
|
<h5 class="alert-heading">⚡ Zlaté pravidlo bezpečnosti:</h5> |
||||
|
<p class="mb-0 fs-5"> |
||||
|
<strong>"DON'T TRUST THE INPUT"</strong> - Nikdy nedůvěřujte tomu, co uživatel zadá do formuláře! |
||||
|
</p> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- POSTUP LEKCE --> |
||||
|
<div class="row mt-5"> |
||||
|
<div class="col-12"> |
||||
|
<div class="card shadow border-primary"> |
||||
|
<div class="card-header bg-primary text-white"> |
||||
|
<h3 class="mb-0">📚 Doporučený postup lekce:</h3> |
||||
|
</div> |
||||
|
<div class="card-body"> |
||||
|
<ol class="fs-5"> |
||||
|
<li class="mb-3"> |
||||
|
<strong>Krok 1:</strong> Nejdřív otevřete <span class="badge bg-danger">ZRANITELNOU VERZI</span> |
||||
|
<ul class="mt-2"> |
||||
|
<li>Vyzkoušejte si XSS útoky z pravého sloupce</li> |
||||
|
<li>Zkopírujte kód a vložte do formuláře</li> |
||||
|
<li>Pozorujte, co se stane!</li> |
||||
|
</ul> |
||||
|
</li> |
||||
|
<li class="mb-3"> |
||||
|
<strong>Krok 2:</strong> Prostudujte si kód ve spodní sekci "Co se děje pod kapotou" |
||||
|
<ul class="mt-2"> |
||||
|
<li>Pochopte proč je <code>innerHTML</code> nebezpečné</li> |
||||
|
<li>Podívejte se do konzole (F12)</li> |
||||
|
</ul> |
||||
|
</li> |
||||
|
<li class="mb-3"> |
||||
|
<strong>Krok 3:</strong> Otevřete <span class="badge bg-success">BEZPEČNOU VERZI</span> |
||||
|
<ul class="mt-2"> |
||||
|
<li>Zkuste stejné útoky - nebudou fungovat!</li> |
||||
|
<li>Pochopte rozdíl v přístupu</li> |
||||
|
</ul> |
||||
|
</li> |
||||
|
<li class="mb-3"> |
||||
|
<strong>Krok 4:</strong> Prostudujte si bezpečný kód |
||||
|
<ul class="mt-2"> |
||||
|
<li>Naučte se používat <code>textContent</code></li> |
||||
|
<li>Naučte se <code>createElement()</code></li> |
||||
|
</ul> |
||||
|
</li> |
||||
|
<li> |
||||
|
<strong>Krok 5:</strong> Zapamatujte si pravidlo <strong>"DON'T TRUST THE INPUT"</strong> |
||||
|
</li> |
||||
|
</ol> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- POROVNÁNÍ KÓDU --> |
||||
|
<div class="row mt-5 mb-5"> |
||||
|
<div class="col-12"> |
||||
|
<div class="card shadow"> |
||||
|
<div class="card-header bg-info text-white"> |
||||
|
<h3 class="mb-0">💻 Porovnání kódu</h3> |
||||
|
</div> |
||||
|
<div class="card-body"> |
||||
|
<div class="row"> |
||||
|
<div class="col-md-6"> |
||||
|
<h5 class="text-danger">❌ NEBEZPEČNÝ KÓD:</h5> |
||||
|
<pre class="bg-danger bg-opacity-10 p-3 rounded"><code>// ❌ ŠPATNĚ - innerHTML! |
||||
|
const html = ` |
||||
|
<div> |
||||
|
<strong>${name}</strong> |
||||
|
<p>${message}</p> |
||||
|
</div> |
||||
|
`; |
||||
|
element.innerHTML = html; |
||||
|
|
||||
|
// Pokud message obsahuje: |
||||
|
// <script>alert('XSS')</script> |
||||
|
// → SPUSTÍ SE!</code></pre> |
||||
|
</div> |
||||
|
<div class="col-md-6"> |
||||
|
<h5 class="text-success">✅ BEZPEČNÝ KÓD:</h5> |
||||
|
<pre class="bg-success bg-opacity-10 p-3 rounded"><code>// ✅ SPRÁVNĚ - createElement! |
||||
|
const div = document.createElement('div'); |
||||
|
|
||||
|
const strong = document.createElement('strong'); |
||||
|
strong.textContent = name; |
||||
|
|
||||
|
const p = document.createElement('p'); |
||||
|
p.textContent = message; |
||||
|
|
||||
|
div.appendChild(strong); |
||||
|
div.appendChild(p); |
||||
|
element.appendChild(div); |
||||
|
|
||||
|
// HTML kód se zobrazí jako TEXT |
||||
|
// → BEZPEČNÉ!</code></pre> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<footer class="bg-dark text-white py-4"> |
||||
|
<div class="container text-center"> |
||||
|
<p class="mb-0">🛡️ XSS Security Demo | WTL 2025 | Lekce 18a</p> |
||||
|
<p class="mb-0"><small>Remember: <strong>DON'T TRUST THE INPUT!</strong></small></p> |
||||
|
</div> |
||||
|
</footer> |
||||
|
|
||||
|
<script src="js/bootstrap.bundle.js"></script> |
||||
|
</body> |
||||
|
</html> |
||||
File diff suppressed because it is too large
File diff suppressed because one or more lines are too long
@ -0,0 +1,154 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang="cs"> |
||||
|
<head> |
||||
|
<meta charset="UTF-8"> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
|
<title>Návštěvní kniha - ZRANITELNÁ ⚠️</title> |
||||
|
<link rel="stylesheet" href="css/bootstrap.css"> |
||||
|
</head> |
||||
|
<body class="bg-light p-4"> |
||||
|
<div class="container"> |
||||
|
<!-- VAROVÁNÍ --> |
||||
|
<div class="alert alert-danger"> |
||||
|
<h3>⚠️ NEBEZPEČNÁ VERZE - lze hacknout!</h3> |
||||
|
<p>Používá <code>innerHTML</code> - ŠPATNĚ!</p> |
||||
|
</div> |
||||
|
|
||||
|
<div class="row"> |
||||
|
<!-- FORMULÁŘ --> |
||||
|
<div class="col-md-6"> |
||||
|
<div class="card"> |
||||
|
<div class="card-header bg-danger text-white"> |
||||
|
<h3>📖 Návštěvní kniha</h3> |
||||
|
</div> |
||||
|
<div class="card-body"> |
||||
|
<form id="myForm"> |
||||
|
<div class="mb-3"> |
||||
|
<label class="form-label">Jméno:</label> |
||||
|
<input type="text" class="form-control" id="name"> |
||||
|
</div> |
||||
|
<div class="mb-3"> |
||||
|
<label class="form-label">Zpráva:</label> |
||||
|
<textarea class="form-control" id="message" rows="3"></textarea> |
||||
|
</div> |
||||
|
<button type="submit" class="btn btn-danger">Přidat záznam</button> |
||||
|
</form> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- ÚTOKY --> |
||||
|
<div class="col-md-6"> |
||||
|
<div class="card border-danger"> |
||||
|
<div class="card-header bg-dark text-white"> |
||||
|
<h4>🎯 Zkuste tyto útoky:</h4> |
||||
|
</div> |
||||
|
<div class="card-body"> |
||||
|
<h5>Do pole "Zpráva" zkopírujte:</h5> |
||||
|
|
||||
|
<div class="alert alert-success mb-2"> |
||||
|
<strong>✅ 1. IMG útok (FUNGUJE!):</strong> |
||||
|
<pre class="mb-0"><code><img src=x onerror="alert('HACKNUTÝ!')"></code></pre> |
||||
|
</div> |
||||
|
|
||||
|
<div class="alert alert-success mb-2"> |
||||
|
<strong>✅ 2. Změna pozadí (FUNGUJE!):</strong> |
||||
|
<pre class="mb-0"><code><img src=x onerror="document.body.style.background='red'"></code></pre> |
||||
|
</div> |
||||
|
|
||||
|
<div class="alert alert-success mb-2"> |
||||
|
<strong>✅ 3. Změna HTML (FUNGUJE!):</strong> |
||||
|
<pre class="mb-0"><code><h1 style="color:red; font-size:50px">HACKED!</h1></code></pre> |
||||
|
</div> |
||||
|
|
||||
|
<div class="alert alert-secondary mb-2"> |
||||
|
<strong>❌ 4. Script tag (NEFUNGUJE):</strong> |
||||
|
<pre class="mb-0"><code><script>alert('XSS')</script></code></pre> |
||||
|
<small class="text-muted">Prohlížeč blokuje script v innerHTML</small> |
||||
|
</div> |
||||
|
|
||||
|
<div class="alert alert-info"> |
||||
|
<strong>💡 Poznámka:</strong><br> |
||||
|
<code><script></code> v innerHTML nefunguje, ale <strong>event handlery ANO</strong>!<br> |
||||
|
Proto je innerHTML STÁLE nebezpečné! |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- ZÁZNAMY --> |
||||
|
<div class="card mt-4"> |
||||
|
<div class="card-header bg-primary text-white"> |
||||
|
<h4>📝 Záznamy</h4> |
||||
|
</div> |
||||
|
<div class="card-body"> |
||||
|
<div id="entries"> |
||||
|
<p class="text-muted">Zatím žádné záznamy...</p> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- VYSVĚTLENÍ --> |
||||
|
<div class="card mt-4 border-warning"> |
||||
|
<div class="card-header bg-warning"> |
||||
|
<h4>🔍 Problematický kód:</h4> |
||||
|
</div> |
||||
|
<div class="card-body"> |
||||
|
<pre class="bg-light p-3"><code>// ❌ NEBEZPEČNÉ! |
||||
|
const html = `<p><strong>${name}</strong>: ${message}</p>`; |
||||
|
entriesDiv.innerHTML += html; // TADY JE PROBLÉM!</code></pre> |
||||
|
<div class="alert alert-danger mt-3 mb-0"> |
||||
|
<h5 class="alert-heading">⚠️ Proč je to nebezpečné?</h5> |
||||
|
<p><strong>innerHTML spustí HTML a event handlery!</strong></p> |
||||
|
|
||||
|
<h6 class="mt-3">❌ Nefunguje (prohlížeč blokuje):</h6> |
||||
|
<ul> |
||||
|
<li><code><script>alert(1)</script></code> - NESPUSTÍ SE</li> |
||||
|
</ul> |
||||
|
|
||||
|
<h6 class="mt-3">✅ Funguje (útočníci tohle používají!):</h6> |
||||
|
<ul class="mb-0"> |
||||
|
<li><code><img src=x onerror="alert(1)"></code> - SPUSTÍ SE!</li> |
||||
|
<li><code><body onload="alert(1)"></code> - SPUSTÍ SE!</li> |
||||
|
<li><code><svg onload="alert(1)"></code> - SPUSTÍ SE!</li> |
||||
|
<li><code><input onfocus="alert(1)" autofocus></code> - SPUSTÍ SE!</li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<script> |
||||
|
// ===== ZRANITELNÝ KÓD ===== |
||||
|
|
||||
|
const form = document.getElementById('myForm'); |
||||
|
const entriesDiv = document.getElementById('entries'); |
||||
|
|
||||
|
// Co dělá e.preventDefault()? |
||||
|
// → Zastaví normální chování formuláře (odeslání a reload stránky) |
||||
|
// → Abychom mohli zpracovat data sami přes JavaScript |
||||
|
|
||||
|
form.addEventListener('submit', function(e) { |
||||
|
e.preventDefault(); // Zastaví odeslání formuláře! |
||||
|
|
||||
|
// Získáme data |
||||
|
const name = document.getElementById('name').value; |
||||
|
const message = document.getElementById('message').value; |
||||
|
|
||||
|
// ❌ PROBLÉM: Vytvoříme HTML string |
||||
|
const html = ` |
||||
|
<div class="alert alert-info"> |
||||
|
<strong>${name}</strong>: ${message} |
||||
|
</div> |
||||
|
`; |
||||
|
|
||||
|
// ❌ KRITICKÝ PROBLÉM: innerHTML spustí JavaScript! |
||||
|
entriesDiv.innerHTML += html; |
||||
|
|
||||
|
// Resetujeme formulář |
||||
|
form.reset(); |
||||
|
}); |
||||
|
</script> |
||||
|
</body> |
||||
|
</html> |
||||
@ -0,0 +1,273 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang="cs"> |
||||
|
<head> |
||||
|
<meta charset="UTF-8"> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
|
<title>Návštěvní kniha - ZRANITELNÁ VERZE ⚠️</title> |
||||
|
<link rel="stylesheet" href="css/bootstrap.css"> |
||||
|
<style> |
||||
|
.entry { |
||||
|
border-left: 4px solid #0d6efd; |
||||
|
transition: all 0.3s; |
||||
|
} |
||||
|
.entry:hover { |
||||
|
background-color: #f8f9fa; |
||||
|
transform: translateX(5px); |
||||
|
} |
||||
|
.danger-banner { |
||||
|
background: linear-gradient(135deg, #dc3545 0%, #c82333 100%); |
||||
|
color: white; |
||||
|
padding: 20px; |
||||
|
margin-bottom: 30px; |
||||
|
border-radius: 10px; |
||||
|
box-shadow: 0 4px 6px rgba(220, 53, 69, 0.3); |
||||
|
} |
||||
|
</style> |
||||
|
</head> |
||||
|
<body class="bg-light"> |
||||
|
<!-- NEBEZPEČÍ BANNER --> |
||||
|
<div class="container mt-4"> |
||||
|
<div class="danger-banner"> |
||||
|
<h2>⚠️ VAROVÁNÍ: ZRANITELNÁ VERZE ⚠️</h2> |
||||
|
<p class="mb-0"> |
||||
|
<strong>Tato verze JE ZÁMĚRNĚ NEBEZPEČNÁ!</strong> Používá <code>innerHTML</code> bez ošetření vstupu. |
||||
|
Lze ji zneužít pomocí XSS (Cross-Site Scripting) útoku. |
||||
|
</p> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="container"> |
||||
|
<div class="row"> |
||||
|
<!-- LEVÝ SLOUPEC - Formulář --> |
||||
|
<div class="col-md-6"> |
||||
|
<div class="card shadow-sm"> |
||||
|
<div class="card-header bg-danger text-white"> |
||||
|
<h3 class="mb-0">📖 Návštěvní kniha</h3> |
||||
|
</div> |
||||
|
<div class="card-body"> |
||||
|
<form id="guestbookForm"> |
||||
|
<div class="mb-3"> |
||||
|
<label for="name" class="form-label">Vaše jméno:</label> |
||||
|
<input type="text" class="form-control" id="name" required> |
||||
|
</div> |
||||
|
|
||||
|
<div class="mb-3"> |
||||
|
<label for="message" class="form-label">Zpráva:</label> |
||||
|
<textarea class="form-control" id="message" rows="4" required></textarea> |
||||
|
<div class="form-text">Napište nám, co si myslíte...</div> |
||||
|
</div> |
||||
|
|
||||
|
<button type="submit" class="btn btn-danger w-100"> |
||||
|
💾 Přidat záznam |
||||
|
</button> |
||||
|
</form> |
||||
|
|
||||
|
<div class="alert alert-warning mt-3"> |
||||
|
<small> |
||||
|
<strong>🔓 Tento formulář NEOŠETŘUJE vstup!</strong><br> |
||||
|
Zkuste zadat HTML nebo JavaScript kód... |
||||
|
</small> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- PRAVÝ SLOUPEC - Nápověda pro hackování --> |
||||
|
<div class="col-md-6"> |
||||
|
<div class="card shadow-sm border-danger"> |
||||
|
<div class="card-header bg-dark text-white"> |
||||
|
<h4 class="mb-0">🎯 Návody na útoky</h4> |
||||
|
</div> |
||||
|
<div class="card-body"> |
||||
|
<h5>Zkuste tyto XSS útoky:</h5> |
||||
|
|
||||
|
<div class="alert alert-danger"> |
||||
|
<strong>1. Jednoduchý alert:</strong> |
||||
|
<pre class="mb-0"><code><script>alert('Byl jsi hacknutý!')</script></code></pre> |
||||
|
</div> |
||||
|
|
||||
|
<div class="alert alert-danger"> |
||||
|
<strong>2. Změna stránky:</strong> |
||||
|
<pre class="mb-0"><code><h1 style="color:red">HACKED!</h1></code></pre> |
||||
|
</div> |
||||
|
|
||||
|
<div class="alert alert-danger"> |
||||
|
<strong>3. Obrázek s chybou:</strong> |
||||
|
<pre class="mb-0"><code><img src=x onerror="alert('XSS')"></code></pre> |
||||
|
</div> |
||||
|
|
||||
|
<div class="alert alert-danger"> |
||||
|
<strong>4. Krádež cookies:</strong> |
||||
|
<pre class="mb-0"><code><script>alert(document.cookie)</script></code></pre> |
||||
|
</div> |
||||
|
|
||||
|
<div class="alert alert-danger"> |
||||
|
<strong>5. Nekonečná smyčka:</strong> |
||||
|
<pre class="mb-0"><code><script>while(true){alert('SPAM')}</script></code></pre> |
||||
|
</div> |
||||
|
|
||||
|
<div class="alert alert-info mt-3"> |
||||
|
<strong>💡 Tip:</strong> Zkopírujte kód výše a vložte do formuláře! |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- ZÁZNAMY --> |
||||
|
<div class="row mt-4"> |
||||
|
<div class="col-12"> |
||||
|
<div class="card shadow-sm"> |
||||
|
<div class="card-header bg-primary text-white d-flex justify-content-between align-items-center"> |
||||
|
<h4 class="mb-0">📝 Záznamy návštěvníků</h4> |
||||
|
<button class="btn btn-sm btn-light" onclick="clearEntries()">🗑️ Smazat vše</button> |
||||
|
</div> |
||||
|
<div class="card-body"> |
||||
|
<div id="entries"> |
||||
|
<div class="alert alert-info"> |
||||
|
<em>Zatím žádné záznamy. Buďte první!</em> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- VYSVĚTLENÍ --> |
||||
|
<div class="row mt-4 mb-4"> |
||||
|
<div class="col-12"> |
||||
|
<div class="card border-warning"> |
||||
|
<div class="card-header bg-warning"> |
||||
|
<h4 class="mb-0">🔍 Co se děje "pod kapotou"?</h4> |
||||
|
</div> |
||||
|
<div class="card-body"> |
||||
|
<h5>Problematický kód v JavaScriptu:</h5> |
||||
|
<pre class="bg-light p-3 rounded"><code>// ❌ NEBEZPEČNÉ - používá innerHTML! |
||||
|
const entryHTML = ` |
||||
|
<div class="entry mb-3 p-3 bg-white rounded"> |
||||
|
<strong>${name}</strong> |
||||
|
<p>${message}</p> |
||||
|
</div> |
||||
|
`; |
||||
|
entriesDiv.innerHTML += entryHTML; // PROBLÉM JE TADY!</code></pre> |
||||
|
|
||||
|
<div class="alert alert-danger mt-3"> |
||||
|
<strong>⚠️ Proč je to nebezpečné?</strong><br> |
||||
|
Když použijeme <code>innerHTML</code>, prohlížeč SPUSTÍ veškerý HTML a JavaScript kód, který uživatel zadá! |
||||
|
Útočník může: |
||||
|
<ul class="mb-0"> |
||||
|
<li>Spustit libovolný JavaScript</li> |
||||
|
<li>Ukrást cookies (přihlašovací údaje)</li> |
||||
|
<li>Změnit vzhled stránky</li> |
||||
|
<li>Přesměrovat na phishingovou stránku</li> |
||||
|
<li>Instalovat keylogger</li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<script src="js/bootstrap.bundle.js"></script> |
||||
|
<script> |
||||
|
// ===== ZRANITELNÝ KÓD - NEPOUŽÍVEJTE V PRODUKCI! ===== |
||||
|
|
||||
|
const form = document.getElementById('guestbookForm'); |
||||
|
const entriesDiv = document.getElementById('entries'); |
||||
|
|
||||
|
// Načteme záznamy z localStorage při načtení stránky |
||||
|
loadEntries(); |
||||
|
|
||||
|
form.addEventListener('submit', function(e) { |
||||
|
e.preventDefault(); |
||||
|
|
||||
|
// Získáme data z formuláře |
||||
|
const name = document.getElementById('name').value; |
||||
|
const message = document.getElementById('message').value; |
||||
|
|
||||
|
// ❌ PROBLÉM: Vytvoříme HTML pomocí template literálu |
||||
|
// Toto NEOŠETŘUJE uživatelský vstup! |
||||
|
const entryHTML = ` |
||||
|
<div class="entry mb-3 p-3 bg-white rounded shadow-sm"> |
||||
|
<div class="d-flex justify-content-between align-items-start"> |
||||
|
<div> |
||||
|
<strong style="color: #0d6efd; font-size: 1.1em;">👤 ${name}</strong> |
||||
|
<small class="text-muted ms-2">${new Date().toLocaleString('cs-CZ')}</small> |
||||
|
</div> |
||||
|
</div> |
||||
|
<hr> |
||||
|
<p class="mb-0">${message}</p> |
||||
|
</div> |
||||
|
`; |
||||
|
|
||||
|
// ❌ KRITICKÝ PROBLÉM: innerHTML spustí veškerý JavaScript! |
||||
|
if (entriesDiv.querySelector('.alert-info')) { |
||||
|
entriesDiv.innerHTML = ''; |
||||
|
} |
||||
|
entriesDiv.innerHTML = entryHTML + entriesDiv.innerHTML; |
||||
|
|
||||
|
// Uložíme do localStorage |
||||
|
saveEntry(name, message); |
||||
|
|
||||
|
// Resetujeme formulář |
||||
|
form.reset(); |
||||
|
}); |
||||
|
|
||||
|
function saveEntry(name, message) { |
||||
|
// Získáme existující záznamy |
||||
|
let entries = JSON.parse(localStorage.getItem('guestbook_vulnerable') || '[]'); |
||||
|
|
||||
|
// Přidáme nový záznam |
||||
|
entries.unshift({ |
||||
|
name: name, |
||||
|
message: message, |
||||
|
timestamp: new Date().toISOString() |
||||
|
}); |
||||
|
|
||||
|
// Uložíme zpět |
||||
|
localStorage.setItem('guestbook_vulnerable', JSON.stringify(entries)); |
||||
|
} |
||||
|
|
||||
|
function loadEntries() { |
||||
|
const entries = JSON.parse(localStorage.getItem('guestbook_vulnerable') || '[]'); |
||||
|
|
||||
|
if (entries.length === 0) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
entriesDiv.innerHTML = ''; |
||||
|
|
||||
|
entries.forEach(entry => { |
||||
|
// ❌ Opět používáme innerHTML - NEBEZPEČNÉ! |
||||
|
const entryHTML = ` |
||||
|
<div class="entry mb-3 p-3 bg-white rounded shadow-sm"> |
||||
|
<div class="d-flex justify-content-between align-items-start"> |
||||
|
<div> |
||||
|
<strong style="color: #0d6efd; font-size: 1.1em;">👤 ${entry.name}</strong> |
||||
|
<small class="text-muted ms-2">${new Date(entry.timestamp).toLocaleString('cs-CZ')}</small> |
||||
|
</div> |
||||
|
</div> |
||||
|
<hr> |
||||
|
<p class="mb-0">${entry.message}</p> |
||||
|
</div> |
||||
|
`; |
||||
|
entriesDiv.innerHTML += entryHTML; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function clearEntries() { |
||||
|
if (confirm('Opravdu chcete smazat všechny záznamy?')) { |
||||
|
localStorage.removeItem('guestbook_vulnerable'); |
||||
|
entriesDiv.innerHTML = '<div class="alert alert-info"><em>Zatím žádné záznamy. Buďte první!</em></div>'; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// BONUS: Ukázka co útočník může udělat |
||||
|
// (Toto je jen pro demonstraci - v reálném útoku by to bylo skryté) |
||||
|
console.log('%c⚠️ BEZPEČNOSTNÍ VAROVÁNÍ ⚠️', 'color: red; font-size: 20px; font-weight: bold;'); |
||||
|
console.log('Tato stránka je ZRANITELNÁ vůči XSS útokům!'); |
||||
|
console.log('Zkuste zadat do formuláře: <script>alert("XSS útok!")</script>'); |
||||
|
</script> |
||||
|
</body> |
||||
|
</html> |
||||
@ -0,0 +1,249 @@ |
|||||
|
# 🤖 AI Projekty - Příprava na Bootstrap |
||||
|
|
||||
|
Tyto projekty byly vytvořeny pro důkladné procvičení HTML, CSS a JavaScript konceptů před začátkem práce s Bootstrap frameworkem. |
||||
|
|
||||
|
## 📋 Přehled projektů |
||||
|
|
||||
|
### 🎨 AI 01 - CSS Grid & Responzivní Layout |
||||
|
**Složitost:** ⭐⭐ Střední |
||||
|
**Čas:** 45-60 minut |
||||
|
**Zaměření:** CSS Grid, pokročilé selektory, media queries |
||||
|
|
||||
|
**Co se naučíte:** |
||||
|
- CSS Grid (grid-template-columns, gap) |
||||
|
- nth-child, first-child, last-child selektory |
||||
|
- Responzivní design s media queries |
||||
|
- CSS transformace při hover |
||||
|
|
||||
|
**Soubory:** |
||||
|
- `ai_01_grid_responsive/ZADANI.md` - Detailní zadání |
||||
|
- `ai_01_grid_responsive/index.html` - Startovací HTML |
||||
|
- `ai_01_grid_responsive/style.css` - CSS šablona |
||||
|
- `ai_01_grid_responsive/reseni.html` - Kompletní řešení |
||||
|
- `ai_01_grid_responsive/reseni.css` - CSS řešení s komentáři |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
### 🏗️ AI 02 - Sémantické HTML5 & CSS Positioning |
||||
|
**Složitost:** ⭐⭐⭐ Náročné |
||||
|
**Čas:** 60-90 minut |
||||
|
**Zaměření:** Sémantické elementy, positioning, jednotky |
||||
|
|
||||
|
**Co se naučíte:** |
||||
|
- HTML5 sémantické elementy (nav, main, section, article, aside) |
||||
|
- Position: fixed, sticky, relative, absolute |
||||
|
- CSS jednotky: rem, em, vh, vw |
||||
|
- Strukturu moderní webové stránky |
||||
|
|
||||
|
**Soubory:** |
||||
|
- `ai_02_semantic_portfolio/ZADANI.md` |
||||
|
- `ai_02_semantic_portfolio/index.html` |
||||
|
- `ai_02_semantic_portfolio/style.css` |
||||
|
- `ai_02_semantic_portfolio/reseni.html` |
||||
|
- `ai_02_semantic_portfolio/reseni.css` |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
### ✅ AI 03 - Pokročilá Validace Formulářů |
||||
|
**Složitost:** ⭐⭐⭐ Náročné |
||||
|
**Čas:** 90-120 minut |
||||
|
**Zaměření:** JavaScript validace, event handling, RegEx |
||||
|
|
||||
|
**Co se naučíte:** |
||||
|
- addEventListener vs inline events |
||||
|
- Form validation s JavaScriptem |
||||
|
- Regulární výrazy (RegEx) |
||||
|
- DOM manipulace (classList, innerHTML) |
||||
|
- Real-time validace s visual feedbackem |
||||
|
|
||||
|
**Soubory:** |
||||
|
- `ai_03_form_validation/ZADANI.md` |
||||
|
- `ai_03_form_validation/index.html` |
||||
|
- `ai_03_form_validation/style.css` |
||||
|
- `ai_03_form_validation/script.js` |
||||
|
- `ai_03_form_validation/reseni.html` |
||||
|
- `ai_03_form_validation/reseni.css` |
||||
|
- `ai_03_form_validation/reseni.js` |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
### 🚀 AI 04 - Komplexní Bootstrap Prep Projekt |
||||
|
**Složitost:** ⭐⭐⭐⭐ Velmi náročné |
||||
|
**Čas:** 4-6 hodin |
||||
|
**Zaměření:** Komplexní projekt kombinující VŠE |
||||
|
|
||||
|
**Co se naučíte:** |
||||
|
- Všechny předchozí koncepty dohromady |
||||
|
- CSS proměnné (custom properties) |
||||
|
- Pokročilé animace |
||||
|
- Scroll efekty |
||||
|
- Modal okna, accordions |
||||
|
- Mobile-first přístup |
||||
|
|
||||
|
**Soubory:** |
||||
|
- `ai_04_prep_bootstrap/ZADANI.md` - Komplexní zadání |
||||
|
- `ai_04_prep_bootstrap/index.html` |
||||
|
- `ai_04_prep_bootstrap/css/style.css` |
||||
|
- `ai_04_prep_bootstrap/css/animations.css` |
||||
|
- `ai_04_prep_bootstrap/js/main.js` |
||||
|
- `ai_04_prep_bootstrap/js/validation.js` |
||||
|
- `ai_04_prep_bootstrap/js/scroll.js` |
||||
|
- `ai_04_prep_bootstrap/RESENI_PRIKLAD.md` - Tips & příklady |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 🎯 Doporučený postup |
||||
|
|
||||
|
### Týden 1: |
||||
|
- **Den 1-2:** AI 01 - CSS Grid (+ opakování Grid konceptů) |
||||
|
- **Den 3-4:** AI 02 - Sémantika & Positioning (+ opakování HTML5) |
||||
|
|
||||
|
### Týden 2: |
||||
|
- **Den 1-3:** AI 03 - Validace formulářů (+ opakování JavaScript) |
||||
|
- **Den 4-5:** Příprava na AI 04 (zopakovat vše) |
||||
|
|
||||
|
### Týden 3: |
||||
|
- **Celý týden:** AI 04 - Komplexní projekt |
||||
|
- **Prezentace:** Konec týdne |
||||
|
|
||||
|
### Týden 4: |
||||
|
- **Začátek Bootstrap!** 🎉 |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 📚 Co budete znát po dokončení všech projektů |
||||
|
|
||||
|
### HTML: |
||||
|
✅ Sémantické HTML5 elementy |
||||
|
✅ Formuláře a jejich atributy |
||||
|
✅ Správná struktura dokumentu |
||||
|
✅ Accessibility best practices |
||||
|
|
||||
|
### CSS: |
||||
|
✅ CSS Grid systém |
||||
|
✅ Flexbox |
||||
|
✅ Positioning (všechny typy) |
||||
|
✅ Media queries & responzivita |
||||
|
✅ CSS proměnné |
||||
|
✅ Jednotky (rem, em, vh, vw) |
||||
|
✅ Pseudo-třídy a pseudo-elementy |
||||
|
✅ Animations & transitions |
||||
|
✅ Gradienty, shadows, filters |
||||
|
|
||||
|
### JavaScript: |
||||
|
✅ DOM manipulace |
||||
|
✅ Event handling (addEventListener) |
||||
|
✅ Form validation |
||||
|
✅ Podmínky a cykly |
||||
|
✅ Funkce (function declaration, arrow functions) |
||||
|
✅ Regulární výrazy (RegEx) |
||||
|
✅ Práce s objekty a poli |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 🔍 Proč tyto projekty před Bootstrapem? |
||||
|
|
||||
|
Bootstrap je mocný framework, ale bez pochopení základních konceptů můžete: |
||||
|
- Neumět framework přizpůsobit svým potřebám |
||||
|
- Vytvářet neefektivní kód |
||||
|
- Záviset na frameworku místo pochopení problému |
||||
|
- Mít problém při debugování |
||||
|
|
||||
|
**Po těchto projektech budete:** |
||||
|
- Rozumět, co Bootstrap dělá "pod kapotou" |
||||
|
- Umět rozhodnout, kdy Bootstrap použít a kdy ne |
||||
|
- Efektivně přizpůsobovat Bootstrap komponenty |
||||
|
- Psát lepší a čistší kód |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 💡 Tips pro studenty |
||||
|
|
||||
|
### Při řešení projektů: |
||||
|
1. **Nejdřív HTML struktura** - Vždy začněte správnou HTML strukturou |
||||
|
2. **Pak mobile styling** - CSS začněte od mobile verzí (mobile-first) |
||||
|
3. **JavaScript na konec** - Interaktivitu přidávejte, když máte hotové HTML & CSS |
||||
|
4. **Časté testování** - Testujte často v browseru, nečekejte na konec |
||||
|
5. **Developer tools** - Používejte Chrome DevTools (F12) pro debugování |
||||
|
6. **Commit často** - Pokud používáte Git, commitujte po každé sekci |
||||
|
|
||||
|
### Užitečné VS Code extensions: |
||||
|
- Live Server - pro live reload |
||||
|
- Prettier - pro formátování kódu |
||||
|
- Auto Rename Tag - automatické přejmenování párových tagů |
||||
|
- CSS Peek - rychlý náhled CSS definic |
||||
|
|
||||
|
### Keyboard shortcuts: |
||||
|
- `Ctrl + /` - zakomentovat/odkomentovat |
||||
|
- `Alt + Shift + F` - formátovat dokument |
||||
|
- `Ctrl + D` - vybrat další výskyt slova |
||||
|
- `Alt + Up/Down` - přesunout řádek nahoru/dolů |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 🆘 Když si nevíte rady |
||||
|
|
||||
|
1. **Přečtěte si zadání znovu** - Často je odpověď v zadání |
||||
|
2. **Podívejte se na předchozí lekce** - V `01_uvod` až `11_json` najdete příklady |
||||
|
3. **Použijte console.log()** - Pro debugging JavaScriptu |
||||
|
4. **Zkontrolujte Developer Tools** - Konzole často ukazuje chyby |
||||
|
5. **Postupujte po malých krocích** - Neřešte vše najednou |
||||
|
6. **Koukněte na reseni.html/css/js** - Ale zkuste to sami nejdřív! |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 📊 Self-assessment checklist |
||||
|
|
||||
|
Po dokončení všech projektů byste měli umět odpovědět ANO na tyto otázky: |
||||
|
|
||||
|
### HTML: |
||||
|
- [ ] Znám rozdíl mezi `<div>` a sémantickými elementy? |
||||
|
- [ ] Umím vytvořit správnou strukturu formuláře? |
||||
|
- [ ] Vím, kdy použít `<section>`, `<article>`, `<aside>`? |
||||
|
|
||||
|
### CSS: |
||||
|
- [ ] Rozumím rozdílu mezi Grid a Flexbox? |
||||
|
- [ ] Umím vytvořit responzivní layout bez frameworku? |
||||
|
- [ ] Znám všechny typy positioningu a kdy je použít? |
||||
|
- [ ] Umím pracovat s rem, em jednotkami? |
||||
|
- [ ] Vím, jak fungují media queries? |
||||
|
|
||||
|
### JavaScript: |
||||
|
- [ ] Umím validovat formulář pomocí JavaScriptu? |
||||
|
- [ ] Rozumím event listenerům? |
||||
|
- [ ] Umím manipulovat s DOM? |
||||
|
- [ ] Znám základy RegEx? |
||||
|
- [ ] Umím vytvořit jednoduchou interaktivitu? |
||||
|
|
||||
|
**Pokud máte většinu zaškrtlou, jste připraveni na Bootstrap!** 🎉 |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 📈 Co dál po Bootstrap? |
||||
|
|
||||
|
Po zvládnutí Bootstrap můžete pokračovat v: |
||||
|
- **Tailwind CSS** - utility-first framework |
||||
|
- **React** - JavaScript library pro UI |
||||
|
- **Vue.js** - progresivní JavaScript framework |
||||
|
- **SASS/SCSS** - CSS preprocessor |
||||
|
- **Node.js & Express** - backend JavaScript |
||||
|
- **Databáze** - MySQL, MongoDB |
||||
|
- **Full-stack projekty** |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 🙏 Acknowledgments |
||||
|
|
||||
|
Projekty vytvořeny pro předmět **Webové Technologie** (WTL) |
||||
|
Cílová skupina: Studenti 3. ročníku IT |
||||
|
Generováno: 2025 |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
**Hodně štěstí s projekty! Pokud máte jakékoliv dotazy, neváhejte se zeptat.** 🚀 |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 📞 Kontakt na učitele |
||||
|
|
||||
|
Při problémech nebo dotazech se obraťte na vyučujícího během konzultačních hodin nebo přes školní email. |
||||
@ -0,0 +1,21 @@ |
|||||
|
Ahoj, |
||||
|
|
||||
|
posílám seznam témát které budeme dělat na programovaní, na náš kurzu na amos "PRG 1 - ročník 25/26" (máme společně s Radkem Rybákem, máme je rozdělené na skupiny) |
||||
|
moje skupina má studijní materiály z hodiny zde: https://git.asgard.odbornaskola.cz/skrabanek/1i_prg |
||||
|
|
||||
|
Seznam témat: |
||||
|
- input, podminky, cykly |
||||
|
- ošetření chyb |
||||
|
- základní moduly (random, os, time, datetime) |
||||
|
- seznamy |
||||
|
- práce se soubory |
||||
|
- funkce |
||||
|
- pokročilá práce s funkcemi |
||||
|
- práce s textem a stringy |
||||
|
- instalace knihoven a pip |
||||
|
- CLI aplikace |
||||
|
- Objektově orientované programování (OOP) |
||||
|
- JSON & HTTP requests |
||||
|
|
||||
|
Děkuji, snad jsem na nic nezpoměl |
||||
|
Jakub Škrabánek |
||||
@ -0,0 +1,428 @@ |
|||||
|
# Individuální studijní plán - Webové technologie (WTL) |
||||
|
## 3. ročník IT - školní rok 2025/2026 |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## Základní informace |
||||
|
|
||||
|
**Předmět:** Webové technologie (WTL) |
||||
|
**Ročník:** 3. ročník IT |
||||
|
**Vyučující:** [Jméno vyučujícího] |
||||
|
**Studijní materiály:** https://[odkaz na váš repozitář] |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## Celkový přehled kurzu |
||||
|
|
||||
|
Kurz Webových technologií je rozdělen do **dvou pololetí**: |
||||
|
|
||||
|
### **1. pololetí (září - leden)** |
||||
|
- Základy HTML, CSS a JavaScriptu |
||||
|
- Responzivní webdesign |
||||
|
- Bootstrap framework |
||||
|
- Webová bezpečnost (XSS útoky) |
||||
|
- Komplexní frontend projekty |
||||
|
|
||||
|
### **2. pololetí (únor - červen)** |
||||
|
- PHP základy |
||||
|
- Databáze (MySQL/MariaDB) |
||||
|
- Backend web development |
||||
|
- Webové aplikace (frontend + backend) |
||||
|
- Závěrečný projekt |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 📋 Seznam témat - 1. pololetí (Frontend) |
||||
|
|
||||
|
### 1. Úvod do HTML |
||||
|
- Struktura HTML dokumentu |
||||
|
- Sémantické elementy (header, nav, main, footer, article, section, aside) |
||||
|
- Nadpisy, odstavce, seznamy |
||||
|
- Odkazy a obrázky |
||||
|
- Tabulky |
||||
|
- **Výstup:** Základní webová stránka |
||||
|
|
||||
|
### 2. HTML Formuláře |
||||
|
- Formulářové elementy (input, textarea, select, button) |
||||
|
- Typy inputů (text, email, password, number, date, atd.) |
||||
|
- Atributy formulářů (required, placeholder, pattern) |
||||
|
- **Výstup:** Kontaktní formulář |
||||
|
|
||||
|
### 3. Úvod do CSS |
||||
|
- Selektory (element, třída, ID) |
||||
|
- Box model (margin, padding, border) |
||||
|
- Barvy a pozadí |
||||
|
- Typografie (fonts, text styling) |
||||
|
- Pseudo-třídy (:hover, :visited, :focus) |
||||
|
- Gradienty |
||||
|
- **Výstup:** Nastylovaná webová stránka |
||||
|
|
||||
|
### 4. CSS Layout |
||||
|
- Display property (block, inline, inline-block) |
||||
|
- Float a clear (legacy layout) |
||||
|
- Základy pozicování (static, relative, absolute, fixed) |
||||
|
- **Výstup:** Vícesloupcový layout |
||||
|
|
||||
|
### 5. Úvod do JavaScriptu |
||||
|
- Proměnné (let, const, var) |
||||
|
- Datové typy (string, number, boolean, object, array) |
||||
|
- Operátory |
||||
|
- Podmínky (if, else, switch) |
||||
|
- Cykly (for, while) |
||||
|
- Funkce (function declaration, arrow functions) |
||||
|
- **Výstup:** Jednoduché JS skripty |
||||
|
|
||||
|
### 6. JavaScript + HTML |
||||
|
- DOM (Document Object Model) |
||||
|
- Manipulace s HTML elementy |
||||
|
- querySelector, getElementById |
||||
|
- innerHTML, innerText, textContent |
||||
|
- Vytváření a odstraňování elementů |
||||
|
- **Výstup:** Dynamická webová stránka |
||||
|
|
||||
|
### 7. JavaScript + CSS + DOM |
||||
|
- Manipulace s CSS přes JavaScript |
||||
|
- classList (add, remove, toggle) |
||||
|
- Změna stylů (style property) |
||||
|
- Event handling (onclick, addEventListener) |
||||
|
- **Výstup:** Interaktivní elementy (toggle, show/hide) |
||||
|
|
||||
|
### 8. JavaScript Formuláře |
||||
|
- Event handling u formulářů |
||||
|
- Validace formulářů s JavaScriptem |
||||
|
- preventDefault() |
||||
|
- Získávání hodnot z formulářů |
||||
|
- **Výstup:** Validovaný formulář |
||||
|
|
||||
|
### 9. Media Queries & Responzivní Design |
||||
|
- Viewport meta tag |
||||
|
- Media queries (@media) |
||||
|
- Breakpoints (mobile, tablet, desktop) |
||||
|
- Mobile-first přístup |
||||
|
- Responzivní obrázky |
||||
|
- **Výstup:** Responzivní webová stránka |
||||
|
|
||||
|
### 10. CSS Flexbox |
||||
|
- display: flex |
||||
|
- flex-direction, justify-content, align-items |
||||
|
- flex-wrap, gap |
||||
|
- Flex položky (flex-grow, flex-shrink, flex-basis) |
||||
|
- **Výstup:** Flexbox layout |
||||
|
|
||||
|
### 11. JSON & AJAX |
||||
|
- Formát JSON |
||||
|
- Fetch API |
||||
|
- Asynchronní JavaScript (promises, async/await) |
||||
|
- REST API základy |
||||
|
- **Výstup:** Aplikace načítající data z API |
||||
|
|
||||
|
### 12. CSS Grid |
||||
|
- display: grid |
||||
|
- grid-template-columns, grid-template-rows |
||||
|
- gap, grid-gap |
||||
|
- Grid areas |
||||
|
- Rozdíl Grid vs Flexbox |
||||
|
- **Výstup:** Grid-based layout |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 🤖 AI Přípravné projekty (před Bootstrapem) |
||||
|
|
||||
|
### AI 01 - CSS Grid & Responzivní Layout |
||||
|
- Pokročilé Grid techniky |
||||
|
- nth-child selektory |
||||
|
- Komplexní responzivní design |
||||
|
- **Hodnocení:** 10 bodů |
||||
|
|
||||
|
### AI 02 - Sémantické HTML5 & CSS Positioning |
||||
|
- Sémantická struktura stránky |
||||
|
- Position: sticky, absolute, fixed, relative |
||||
|
- CSS jednotky (rem, em, vh, vw) |
||||
|
- **Hodnocení:** 15 bodů |
||||
|
|
||||
|
### AI 03 - Pokročilá Validace Formulářů |
||||
|
- addEventListener |
||||
|
- Regulární výrazy (RegEx) |
||||
|
- Real-time validace |
||||
|
- Visual feedback |
||||
|
- **Hodnocení:** 20 bodů |
||||
|
|
||||
|
### AI 04 - Komplexní Bootstrap Prep Projekt |
||||
|
- Kombinace všech předchozích konceptů |
||||
|
- CSS custom properties (variables) |
||||
|
- Pokročilé animace |
||||
|
- Modal, accordion implementace |
||||
|
- **Hodnocení:** 30 bodů |
||||
|
|
||||
|
**Celkem za AI projekty: 75 bodů** |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 🎨 Bootstrap Framework |
||||
|
|
||||
|
### 13. Bootstrap Úvod |
||||
|
- Instalace a setup Bootstrapu |
||||
|
- Bootstrap grid systém |
||||
|
- Container, row, col |
||||
|
- Utility třídy |
||||
|
|
||||
|
### 14. Bootstrap Layout |
||||
|
- Responzivní breakpoints |
||||
|
- Grid options |
||||
|
- Flexbox utilities |
||||
|
- Spacing utilities (m-, p-) |
||||
|
|
||||
|
### 15. Bootstrap Komponenty |
||||
|
- Buttons, badges, alerts |
||||
|
- Cards |
||||
|
- Navbar |
||||
|
- Forms (Bootstrap form styling) |
||||
|
- Tables |
||||
|
|
||||
|
### 16. Bootstrap Carousel |
||||
|
- Carousel komponenta |
||||
|
- Indicators, controls |
||||
|
- Automatické přepínání |
||||
|
- **Výstup:** Fotogalerie s carousel |
||||
|
|
||||
|
### 17. Bootstrap Modal |
||||
|
- Modal dialog |
||||
|
- Triggery pro modal |
||||
|
- Modal velikosti |
||||
|
- **Výstup:** Aplikace s modal dialogy |
||||
|
|
||||
|
### 18. Bootstrap Form Validation |
||||
|
- Bootstrap validační třídy |
||||
|
- JavaScript validace s Bootstrapem |
||||
|
- Custom validation styles |
||||
|
- **Výstup:** Profesionální registrační formulář |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 🔒 Webová bezpečnost |
||||
|
|
||||
|
### 19. XSS (Cross-Site Scripting) Demo |
||||
|
- Co je XSS útok |
||||
|
- Reflected XSS |
||||
|
- Stored XSS |
||||
|
- Prevence XSS útoků |
||||
|
- HTML sanitizace |
||||
|
- **Výstup:** Demo aplikace s XSS zranitelností a její oprava |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 🚀 Komplexní Frontend projekty |
||||
|
|
||||
|
### 20. TODO List aplikace |
||||
|
- CRUD operace (Create, Read, Update, Delete) |
||||
|
- LocalStorage |
||||
|
- Bootstrap UI |
||||
|
- JavaScript manipulace s daty |
||||
|
- **Výstup:** Funkční TODO list |
||||
|
|
||||
|
### 21. AJAX & API Integration |
||||
|
- Fetch API pokročile |
||||
|
- Práce s REST API |
||||
|
- Error handling |
||||
|
- Loading states |
||||
|
- **Výstup:** Aplikace pracující s externím API |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 📋 Seznam témat - 2. pololetí (Backend) |
||||
|
|
||||
|
### 22. Úvod do PHP |
||||
|
- PHP syntaxe |
||||
|
- Proměnné a datové typy |
||||
|
- Podmínky a cykly |
||||
|
- Funkce v PHP |
||||
|
- Superglobals ($_GET, $_POST, $_SERVER) |
||||
|
|
||||
|
### 23. PHP & Formuláře |
||||
|
- Zpracování formulářů (GET, POST) |
||||
|
- Validace na straně serveru |
||||
|
- Sanitizace vstupů |
||||
|
- CSRF ochrana |
||||
|
|
||||
|
### 24. PHP & Soubory |
||||
|
- Čtení a zápis souborů |
||||
|
- Nahrávání souborů |
||||
|
- Práce s adresáři |
||||
|
- File permissions |
||||
|
|
||||
|
### 25. Úvod do databází |
||||
|
- Relační databáze |
||||
|
- MySQL/MariaDB |
||||
|
- phpMyAdmin |
||||
|
- SQL příkazy (SELECT, INSERT, UPDATE, DELETE) |
||||
|
|
||||
|
### 26. PHP & MySQL |
||||
|
- Připojení k databázi (mysqli, PDO) |
||||
|
- Prepared statements |
||||
|
- SQL injection prevence |
||||
|
- Databázové dotazy z PHP |
||||
|
|
||||
|
### 27. Sessions & Cookies |
||||
|
- HTTP sessions |
||||
|
- Cookies |
||||
|
- Autentizace uživatelů |
||||
|
- Session security |
||||
|
|
||||
|
### 28. Registrace & Login systém |
||||
|
- Uživatelská registrace |
||||
|
- Hash hesel (password_hash, password_verify) |
||||
|
- Login/logout funkcionalita |
||||
|
- Ochrana stránek (middleware) |
||||
|
|
||||
|
### 29. CRUD Webová aplikace |
||||
|
- Create, Read, Update, Delete operace |
||||
|
- Databázové operace |
||||
|
- Admin rozhraní |
||||
|
- **Výstup:** Blog nebo e-shop backend |
||||
|
|
||||
|
### 30. REST API v PHP |
||||
|
- JSON responses |
||||
|
- API endpoints |
||||
|
- HTTP metody (GET, POST, PUT, DELETE) |
||||
|
- API authentication |
||||
|
|
||||
|
### 31. Závěrečný projekt |
||||
|
- Komplexní webová aplikace |
||||
|
- Frontend (HTML, CSS, JS, Bootstrap) |
||||
|
- Backend (PHP, MySQL) |
||||
|
- Funkční features (registrace, login, CRUD) |
||||
|
- Responzivní design |
||||
|
- **Hodnocení:** 100 bodů |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 🎯 Očekávané výstupy kurzu |
||||
|
|
||||
|
Po dokončení kurzu by studenti měli umět: |
||||
|
|
||||
|
### Frontend: |
||||
|
✅ Vytvořit sémanticky správnou HTML strukturu |
||||
|
✅ Nastylovat moderní responzivní web pomocí CSS |
||||
|
✅ Používat CSS Grid a Flexbox pro layout |
||||
|
✅ Psát čistý a efektivní JavaScript kód |
||||
|
✅ Manipulovat s DOM a zpracovávat eventy |
||||
|
✅ Validovat formuláře |
||||
|
✅ Pracovat s JSON a API pomocí Fetch |
||||
|
✅ Používat Bootstrap framework |
||||
|
✅ Rozumět základům webové bezpečnosti (XSS) |
||||
|
|
||||
|
### Backend: |
||||
|
✅ Psát PHP skripty |
||||
|
✅ Zpracovávat formuláře na serveru |
||||
|
✅ Pracovat s MySQL databází |
||||
|
✅ Vytvořit registrační a přihlašovací systém |
||||
|
✅ Implementovat CRUD operace |
||||
|
✅ Zabezpečit webovou aplikaci (SQL injection, XSS, CSRF) |
||||
|
✅ Vytvořit jednoduché REST API |
||||
|
|
||||
|
### Soft skills: |
||||
|
✅ Debugovat kód pomocí Developer Tools |
||||
|
✅ Používat Git pro verzování kódu |
||||
|
✅ Psát čitelný a udržovatelný kód |
||||
|
✅ Testovat aplikace v různých prohlížečích |
||||
|
✅ Pracovat samostatně i v týmu |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 📊 Hodnocení |
||||
|
|
||||
|
### 1. pololetí: |
||||
|
- **AI projekty:** 75 bodů (AI 01-04) |
||||
|
- **Průběžné projekty:** 50 bodů |
||||
|
- **Aktivita a domácí úkoly:** 25 bodů |
||||
|
- **Test z frontend technologií:** 50 bodů |
||||
|
- **Celkem:** 200 bodů |
||||
|
|
||||
|
### 2. pololetí: |
||||
|
- **PHP & MySQL projekty:** 50 bodů |
||||
|
- **Závěrečný projekt:** 100 bodů |
||||
|
- **Aktivita a domácí úkoly:** 25 bodů |
||||
|
- **Test z backend technologií:** 25 bodů |
||||
|
- **Celkem:** 200 bodů |
||||
|
|
||||
|
### Celkové hodnocení za rok: 400 bodů |
||||
|
|
||||
|
**Klasifikace:** |
||||
|
- 90-100% (360-400 bodů): Výborný (1) |
||||
|
- 75-89% (300-359 bodů): Chvalitebný (2) |
||||
|
- 60-74% (240-299 bodů): Dobrý (3) |
||||
|
- 45-59% (180-239 bodů): Dostatečný (4) |
||||
|
- 0-44% (0-179 bodů): Nedostatečný (5) |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 🛠️ Doporučené nástroje |
||||
|
|
||||
|
### Editor: |
||||
|
- **Visual Studio Code** (primární doporučení) |
||||
|
- Extensions: Live Server, Prettier, Auto Rename Tag, CSS Peek |
||||
|
|
||||
|
### Prohlížeče: |
||||
|
- **Google Chrome** (primární pro vývoj) |
||||
|
- Firefox, Edge (testování kompatibility) |
||||
|
- Chrome DevTools (F12) pro debugging |
||||
|
|
||||
|
### Backend (2. pololetí): |
||||
|
- **XAMPP** nebo **WAMP** (Apache + MySQL + PHP) |
||||
|
- **phpMyAdmin** (správa databáze) |
||||
|
|
||||
|
### Ostatní: |
||||
|
- **Git** pro verzování kódu |
||||
|
- **GitHub/GitLab** pro sdílení projektů |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 📚 Užitečné zdroje |
||||
|
|
||||
|
### Dokumentace: |
||||
|
- [MDN Web Docs](https://developer.mozilla.org/) - HTML, CSS, JavaScript |
||||
|
- [Bootstrap dokumentace](https://getbootstrap.com/docs/) |
||||
|
- [PHP dokumentace](https://www.php.net/manual/en/) |
||||
|
- [MySQL dokumentace](https://dev.mysql.com/doc/) |
||||
|
|
||||
|
### Online nástroje: |
||||
|
- [CodePen](https://codepen.io/) - Online playground |
||||
|
- [regex101.com](https://regex101.com/) - RegEx tester |
||||
|
- [Can I Use](https://caniuse.com/) - Browser compatibility |
||||
|
- [CSS-Tricks](https://css-tricks.com/) - CSS tutoriály |
||||
|
|
||||
|
### Komunita: |
||||
|
- [Stack Overflow](https://stackoverflow.com/) - Q&A |
||||
|
- [CSS Tricks](https://css-tricks.com/) |
||||
|
- [JavaScript.info](https://javascript.info/) |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 📞 Kontakt |
||||
|
|
||||
|
**Vyučující:** [Jméno vyučujícího] |
||||
|
**Email:** [email] |
||||
|
**Konzultační hodiny:** [časy] |
||||
|
**Repozitář:** [odkaz na Git repozitář] |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 📝 Poznámky |
||||
|
|
||||
|
- Všechny projekty a úkoly budou zadávány průběžně během školního roku |
||||
|
- Studenti jsou povinni dodržovat termíny odevzdání |
||||
|
- Plagiátorství (kopírování kódu) bude sankcionováno |
||||
|
- Aktivní účast na hodinách je důležitá pro pochopení látky |
||||
|
- Doporučuje se pravidelné opakování a procvičování |
||||
|
- Studenti mohou využít konzultační hodiny pro pomoc s projekty |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
**Vytvořeno:** [datum] |
||||
|
**Předmět:** Webové Technologie (WTL) |
||||
|
**Cílová skupina:** 3. ročník IT |
||||
|
**Školní rok:** 2025/2026 |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
*Tento studijní plán může být upraven na základě potřeb studentů a aktuálního pokroku ve výuce.* |
||||
@ -0,0 +1,31 @@ |
|||||
|
Ahoj, |
||||
|
|
||||
|
posílám seznam témat které budeme dělat na Webových technologiích, na náš kurzu "WTL - 3. ročník 25/26" |
||||
|
studijní materiály z hodiny zde: [odkaz na repozitář] |
||||
|
|
||||
|
Seznam témat - 1. pololetí (Frontend): |
||||
|
- úvod do HTML (struktura, sémantika, formuláře, tabulky) |
||||
|
- úvod do CSS (selektory, box model, barvy, typografie) |
||||
|
- CSS layout (float, positioning, flexbox, grid) |
||||
|
- media queries a responzivní design |
||||
|
- úvod do JavaScriptu (proměnné, podmínky, cykly, funkce) |
||||
|
- JavaScript + DOM (manipulace s HTML, querySelector, innerHTML) |
||||
|
- JavaScript eventy a validace formulářů |
||||
|
- JSON & Fetch API (asynchronní JavaScript, REST API) |
||||
|
- Bootstrap framework (grid systém, komponenty, utilities) |
||||
|
- Bootstrap pokročile (carousel, modal, form validation) |
||||
|
- webová bezpečnost (XSS útoky, prevence, sanitizace) |
||||
|
- komplexní frontend projekty (TODO list, AJAX aplikace) |
||||
|
|
||||
|
Seznam témat - 2. pololetí (Backend): |
||||
|
- úvod do PHP (syntaxe, proměnné, podmínky, cykly, funkce) |
||||
|
- PHP formuláře (GET, POST, validace, sanitizace) |
||||
|
- PHP práce se soubory (čtení, zápis, upload) |
||||
|
- úvod do databází (MySQL, SQL příkazy) |
||||
|
- PHP & MySQL (připojení, dotazy, prepared statements) |
||||
|
- sessions & cookies (autentizace, zabezpečení) |
||||
|
- registrace & login systém (hash hesel, ochrana) |
||||
|
- závěrečný full-stack projekt (frontend + backend + databáze) |
||||
|
|
||||
|
Děkuji, snad jsem na nic nezapomněl |
||||
|
[Tvoje jméno] |
||||
@ -0,0 +1,101 @@ |
|||||
|
# 📚 AI Extra Material - Doplňkové Studijní Materiály |
||||
|
|
||||
|
## 🎯 Účel této složky |
||||
|
|
||||
|
Tato složka obsahuje **doplňkové studijní materiály** pro hlubší pochopení pokročilých konceptů webového vývoje. Tyto materiály jsou navíc k základnímu učivu a slouží k samostudiu. |
||||
|
|
||||
|
## 📂 Co složka obsahuje |
||||
|
|
||||
|
### 🎓 AI Projekty (Základní verze v /AI složce) |
||||
|
|
||||
|
Tato složka obsahuje **duplikáty AI projektů** z hlavní složky `/AI`. Jsou zde pro pohodlný přístup k materiálům. |
||||
|
|
||||
|
- **ai_01_grid_responsive** - CSS Grid & Responzivní Layout |
||||
|
- **ai_02_semantic_portfolio** - Sémantické HTML5 & CSS Positioning |
||||
|
- **ai_03_form_validation** - Pokročilá Validace Formulářů s JavaScript |
||||
|
- **ai_04_prep_bootstrap** - Komplexní Bootstrap Prep Projekt |
||||
|
|
||||
|
### 📋 Informační Soubory |
||||
|
|
||||
|
- **AI_PROJEKTY_README.md** - Přehled všech AI projektů |
||||
|
- **UCITEL_INFO.md** - Informace pro učitele o projektech, hodnocení, harmonogramu |
||||
|
- **ISP_WTL_3I.md** - Individuální studijní plán (ISP) |
||||
|
- **ISP_WTL_3I.txt** - ISP v textové podobě |
||||
|
|
||||
|
### 🚀 Bootstrap Doplňkové Projekty |
||||
|
|
||||
|
#### 18_form_validation_bootstrap |
||||
|
Ukázka validace formulářů s Bootstrap 5. |
||||
|
- Bootstrap validační třídy |
||||
|
- Custom validace s JavaScriptem |
||||
|
- Visual feedback pro uživatele |
||||
|
- Propojení s Bootstrap styly |
||||
|
|
||||
|
**Kdy použít:** Pro pochopení, jak Bootstrap řeší validaci formulářů "out of the box". |
||||
|
|
||||
|
#### 18a_form_xss_demo |
||||
|
**BEZPEČNOSTNÍ DEMO** - ukázka XSS (Cross-Site Scripting) zranitelnosti. |
||||
|
- Demonstrace nebezpečného kódu |
||||
|
- Jak vznikají XSS útoky |
||||
|
- Jak se bránit (sanitizace inputů) |
||||
|
- Best practices pro bezpečnost |
||||
|
|
||||
|
**⚠️ VAROVÁNÍ:** Tento kód je záměrně zranitelný pro výukové účely. **NIKDY** nepoužívejte tento přístup v produkčním kódu! |
||||
|
|
||||
|
## 🎓 Jak pracovat s těmito materiály |
||||
|
|
||||
|
### Pro studenty: |
||||
|
|
||||
|
1. **Nejdříve dokončete základní AI projekty** v hlavní složce `/AI` |
||||
|
2. **Pokud chcete víc:** Projděte si tyto doplňkové materiály |
||||
|
3. **Bootstrap validace:** Po dokončení AI 03, prozkoumejte `18_form_validation_bootstrap` |
||||
|
4. **Bezpečnost:** Po práci s formuláři si prostudujte `18a_form_xss_demo` |
||||
|
|
||||
|
### Pro učitele: |
||||
|
|
||||
|
- Přečtěte si **UCITEL_INFO.md** pro detailní informace o projektech |
||||
|
- **AI_PROJEKTY_README.md** obsahuje přehled všech AI projektů |
||||
|
- **ISP** soubory obsahují studijní plán |
||||
|
|
||||
|
## 🔗 Návaznost na hlavní kurz |
||||
|
|
||||
|
| Základní kurz | Extra Material | Proč je užitečný | |
||||
|
|---------------|----------------|------------------| |
||||
|
| 08_js_form | 18_form_validation_bootstrap | Ukáže Bootstrap přístup k validaci | |
||||
|
| AI 03 | 18a_form_xss_demo | Bezpečnostní aspekty formulářů | |
||||
|
| 13-17 Bootstrap | AI projekty (duplikáty) | Opakování před Bootstrapem | |
||||
|
|
||||
|
## 💡 Doporučené Pořadí Studia |
||||
|
|
||||
|
1. **Základní kurz** (01-20) - absolvujte nejdříve |
||||
|
2. **AI projekty** v hlavní složce `/AI` - praktické projekty |
||||
|
3. **Bootstrap validace** (`18_form_validation_bootstrap`) - po Bootstrap lekcích |
||||
|
4. **XSS Demo** (`18a_form_xss_demo`) - pro pochopení bezpečnosti |
||||
|
5. **ISP materiály** - pro samostudium dle potřeby |
||||
|
|
||||
|
## ⚠️ Důležité poznámky |
||||
|
|
||||
|
- ✅ Tyto materiály jsou **nepovinné** - jsou navíc k základnímu kurzu |
||||
|
- ✅ Projekty v této složce jsou **duplikáty** - hlavní verze jsou v `/AI` |
||||
|
- ⚠️ **XSS Demo** je záměrně zranitelný - slouží pouze k výukovým účelům |
||||
|
- 📚 **UCITEL_INFO.md** obsahuje hodnocení a harmonogram pro učitele |
||||
|
|
||||
|
## 📖 Hlavní AI Projekty |
||||
|
|
||||
|
**POZOR:** Hlavní verze AI projektů jsou v kořenové složce **`/AI`**! |
||||
|
|
||||
|
Pro práci na projektech používejte soubory z `/AI`, ne z této složky. Tato složka slouží jako archiv a reference. |
||||
|
|
||||
|
## 🎯 Cíle Extra Materiálů |
||||
|
|
||||
|
Po prostudování těchto materiálů budete: |
||||
|
- ✅ Rozumět Bootstrap validaci formulářů |
||||
|
- ✅ Vědět, jak se bránit XSS útokům |
||||
|
- ✅ Mít hlubší pochopení bezpečnosti webových aplikací |
||||
|
- ✅ Umět propojit Bootstrap s custom validací |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
**Hodně štěstí s dalším studiem! 📚✨** |
||||
|
|
||||
|
*Tyto materiály rozšiřují základní kurz a poskytují hlubší vhled do pokročilých témat.* |
||||
@ -0,0 +1,351 @@ |
|||||
|
# 👨🏫 Informace pro učitele - AI Projekty |
||||
|
|
||||
|
## 📊 Analýza současného stavu studentů |
||||
|
|
||||
|
### ✅ Co studenti již zvládají: |
||||
|
|
||||
|
**HTML:** |
||||
|
- Základní struktura, nadpisy (h1-h6) |
||||
|
- Odkazy, obrázky |
||||
|
- Formuláře (input, textarea, select, button) |
||||
|
- Tabulky a seznamy |
||||
|
- Částečně sémantické elementy (header, footer, aside, article) |
||||
|
|
||||
|
**CSS:** |
||||
|
- Základní selektory (element, třída, ID) |
||||
|
- Box model (margin, padding, border) |
||||
|
- Pseudo-třídy (:hover, :visited) |
||||
|
- Gradienty a transitions |
||||
|
- Flexbox (základy) |
||||
|
- Media queries (základy) |
||||
|
- Float-based layout (starší přístup) |
||||
|
|
||||
|
**JavaScript:** |
||||
|
- Proměnné (let, const, var) |
||||
|
- Datové typy a objekty |
||||
|
- Podmínky a cykly |
||||
|
- Funkce |
||||
|
- DOM manipulace (getElementById, innerHTML, innerText) |
||||
|
- Event handling (inline onclick) |
||||
|
- JSON a Fetch API |
||||
|
|
||||
|
### ⚠️ Co chybí před Bootstrapem: |
||||
|
|
||||
|
**KRITICKÉ mezery:** |
||||
|
1. **CSS Grid** - chybí úplně |
||||
|
2. **addEventListener** - používají jen inline onclick |
||||
|
3. **Pokročilé CSS selektory** (nth-child, ::before, ::after) |
||||
|
4. **CSS jednotky** - používají jen px, ne rem/em/vh/vw |
||||
|
5. **Position: sticky a absolute** - nepoužito |
||||
|
6. **Form validace s JS** - jen základní |
||||
|
|
||||
|
**DOPORUČUJI doplnit:** |
||||
|
7. **CSS proměnné** (custom properties) |
||||
|
8. **Pokročilé animace** |
||||
|
9. **Scroll efekty** |
||||
|
10. **Mobile-first přístup** |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 🎯 Účel AI projektů |
||||
|
|
||||
|
Projekty byly navrženy tak, aby: |
||||
|
1. **Systematicky doplnily mezery** v znalostech |
||||
|
2. **Postupně zvyšovaly náročnost** (od jednoduchého po komplexní) |
||||
|
3. **Připravily na Bootstrap filozofii** (grid systém, utility třídy, komponenty) |
||||
|
4. **Poskytly praktickou zkušenost** s real-world projekty |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 📋 Přehled projektů pro učitele |
||||
|
|
||||
|
### AI 01 - CSS Grid & Responzivní Layout |
||||
|
**Čas:** 1-2 vyučovací hodiny |
||||
|
**Obtížnost:** Střední |
||||
|
**Cíl:** Naučit CSS Grid před Bootstrap grid systémem |
||||
|
|
||||
|
**Klíčové koncepty:** |
||||
|
- `display: grid` |
||||
|
- `grid-template-columns` |
||||
|
- `gap` |
||||
|
- Pokročilé selektory (nth-child) |
||||
|
- Media queries pro responzivitu |
||||
|
|
||||
|
**Výstup:** Responzivní galerie s 9 položkami |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
### AI 02 - Sémantické HTML5 & CSS Positioning |
||||
|
**Čas:** 2-3 vyučovací hodiny |
||||
|
**Obtížnost:** Náročné |
||||
|
**Cíl:** Sémantický HTML5 a všechny typy positioning |
||||
|
|
||||
|
**Klíčové koncepty:** |
||||
|
- Sémantické elementy (nav, main, section, article, aside) |
||||
|
- Position: fixed, sticky, relative, absolute |
||||
|
- CSS jednotky: rem, em, vh, vw |
||||
|
- Správná struktura moderní stránky |
||||
|
|
||||
|
**Výstup:** Portfolio stránka s fixní navigací a sticky sidebar |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
### AI 03 - Pokročilá Validace Formulářů |
||||
|
**Čas:** 2-3 vyučovací hodiny |
||||
|
**Obtížnost:** Náročné |
||||
|
**Cíl:** Real-time validace s JS (Bootstrap má validační třídy) |
||||
|
|
||||
|
**Klíčové koncepty:** |
||||
|
- `addEventListener` místo inline events |
||||
|
- RegEx pro validaci |
||||
|
- DOM manipulace (classList) |
||||
|
- Real-time feedback |
||||
|
- UX best practices |
||||
|
|
||||
|
**Výstup:** Registrační formulář s kompletní validací |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
### AI 04 - Komplexní Bootstrap Prep Projekt |
||||
|
**Čas:** 4-6 vyučovacích hodin (nebo domácí práce) |
||||
|
**Obtížnost:** Velmi náročné |
||||
|
**Cíl:** Komplexní projekt kombinující všechny koncepty |
||||
|
|
||||
|
**Klíčové koncepty:** |
||||
|
- Všechny předchozí koncepty |
||||
|
- CSS proměnné |
||||
|
- Scroll efekty |
||||
|
- Modal, accordion |
||||
|
- Mobile-first |
||||
|
|
||||
|
**Výstup:** Plně funkční landing page |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 📅 Doporučený harmonogram |
||||
|
|
||||
|
### Varianta A - Intenzivní (2 týdny) |
||||
|
``` |
||||
|
Týden 1: |
||||
|
Po: AI 01 + AI 02 začátek |
||||
|
St: AI 02 dokončení + AI 03 začátek |
||||
|
Pá: AI 03 dokončení |
||||
|
|
||||
|
Týden 2: |
||||
|
Po-Pá: AI 04 (průběžně) |
||||
|
Pá: Prezentace AI 04 |
||||
|
|
||||
|
Týden 3: |
||||
|
Bootstrap start! |
||||
|
``` |
||||
|
|
||||
|
### Varianta B - Standardní (3 týdny) |
||||
|
``` |
||||
|
Týden 1: |
||||
|
Lekce 1: AI 01 |
||||
|
Lekce 2: AI 02 |
||||
|
|
||||
|
Týden 2: |
||||
|
Lekce 1: AI 03 část 1 |
||||
|
Lekce 2: AI 03 část 2 |
||||
|
|
||||
|
Týden 3: |
||||
|
Lekce 1-2: AI 04 (práce ve třídě) |
||||
|
DÚ: Dokončení AI 04 |
||||
|
|
||||
|
Týden 4: |
||||
|
Bootstrap start! |
||||
|
``` |
||||
|
|
||||
|
### Varianta C - Volnočasová (4 týdny) |
||||
|
``` |
||||
|
Týden 1: AI 01 |
||||
|
Týden 2: AI 02 |
||||
|
Týden 3: AI 03 |
||||
|
Týden 4: AI 04 |
||||
|
|
||||
|
Týden 5: Bootstrap start! |
||||
|
``` |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 🎓 Hodnocení projektů |
||||
|
|
||||
|
### AI 01 (10 bodů) |
||||
|
- HTML struktura (2b) |
||||
|
- CSS Grid implementace (3b) |
||||
|
- Media queries (2b) |
||||
|
- Pokročilé selektory (2b) |
||||
|
- Responzivita (1b) |
||||
|
|
||||
|
### AI 02 (15 bodů) |
||||
|
- Sémantická struktura (4b) |
||||
|
- Position: fixed, sticky (3b) |
||||
|
- Position: relative, absolute (3b) |
||||
|
- CSS jednotky (rem, em, vh) (2b) |
||||
|
- Responzivita (3b) |
||||
|
|
||||
|
### AI 03 (20 bodů) |
||||
|
- addEventListener použití (4b) |
||||
|
- Validační funkce (6b) |
||||
|
- RegEx implementace (3b) |
||||
|
- Visual feedback (4b) |
||||
|
- Submit handling (3b) |
||||
|
|
||||
|
### AI 04 (30 bodů) |
||||
|
- HTML struktura (5b) |
||||
|
- CSS Grid & Flexbox (6b) |
||||
|
- Positioning (4b) |
||||
|
- JavaScript interaktivita (6b) |
||||
|
- Responzivita (4b) |
||||
|
- Design & UX (3b) |
||||
|
- Clean code (2b) |
||||
|
|
||||
|
**Celkem: 75 bodů** |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 💡 Tips pro učitele |
||||
|
|
||||
|
### Při výuce AI 01: |
||||
|
1. Začněte s vysvětlením rozdílu Grid vs Flexbox |
||||
|
2. Ukažte Chrome DevTools - Grid overlay |
||||
|
3. Procvičte nth-child na příkladech |
||||
|
4. Zdůrazněte mobile-first přístup |
||||
|
|
||||
|
### Při výuce AI 02: |
||||
|
1. Narýsujte positioning na tabuli (fixed, sticky, relative, absolute) |
||||
|
2. Ukažte proč používat rem místo px |
||||
|
3. Vysvětlete z-index context |
||||
|
4. Zdůrazněte důležitost sémantiky pro SEO |
||||
|
|
||||
|
### Při výuce AI 03: |
||||
|
1. Živě ukažte addEventListener vs onclick |
||||
|
2. Procvičte RegEx na regex101.com |
||||
|
3. Vysvětlete event.preventDefault() |
||||
|
4. Ukažte console.log() debugging |
||||
|
|
||||
|
### Při výuce AI 04: |
||||
|
1. Nechte studenty pracovat samostatně |
||||
|
2. Buďte dostupní pro konzultace |
||||
|
3. Povzbuďte kreativitu |
||||
|
4. Na konci nechte studenty prezentovat |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 🔧 Troubleshooting - Časté problémy studentů |
||||
|
|
||||
|
### CSS Grid: |
||||
|
**Problem:** "Grid nefunguje" |
||||
|
**Řešení:** Zkontrolujte `display: grid` na kontejneru, ne na položkách |
||||
|
|
||||
|
### Positioning: |
||||
|
**Problem:** "Absolute element je mimo stránku" |
||||
|
**Řešení:** Parent musí mít `position: relative` |
||||
|
|
||||
|
### addEventListener: |
||||
|
**Problem:** "Funkce se volá hned při načtení" |
||||
|
**Řešení:** Nepsat `validateName()` ale jen `validateName` (bez závorek) |
||||
|
|
||||
|
### RegEx: |
||||
|
**Problem:** "RegEx nefunguje" |
||||
|
**Řešení:** Otestujte na regex101.com, možná chybí escape znaky |
||||
|
|
||||
|
### Media queries: |
||||
|
**Problem:** "Responzivita nefunguje" |
||||
|
**Řešení:** Zkontrolujte `<meta name="viewport">` v head |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 📊 Očekávané výsledky |
||||
|
|
||||
|
Po dokončení všech projektů by studenti měli: |
||||
|
|
||||
|
### Znát: |
||||
|
✅ Rozdíl mezi Grid a Flexbox a kdy co použít |
||||
|
✅ Všechny typy CSS positioning |
||||
|
✅ Jak vytvořit responzivní layout bez frameworku |
||||
|
✅ Jak validovat formuláře v JavaScriptu |
||||
|
✅ addEventListener a event handling |
||||
|
✅ Základy RegEx |
||||
|
✅ Mobile-first přístup |
||||
|
|
||||
|
### Umět: |
||||
|
✅ Vytvořit komplexní responzivní stránku |
||||
|
✅ Implementovat interaktivní funkce |
||||
|
✅ Validovat formuláře s visual feedbackem |
||||
|
✅ Používat pokročilé CSS selektory |
||||
|
✅ Debugovat pomocí Developer Tools |
||||
|
|
||||
|
### Být připraveni na: |
||||
|
✅ Bootstrap grid systém (pochopí jak funguje) |
||||
|
✅ Bootstrap komponenty (pochopí co řeší) |
||||
|
✅ Bootstrap utilities (pochopí proč existují) |
||||
|
✅ Přizpůsobení frameworku vlastním potřebám |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 🎯 Přechod na Bootstrap |
||||
|
|
||||
|
Po dokončení AI projektů vysvětlete studentům: |
||||
|
|
||||
|
1. **Co Bootstrap řeší:** |
||||
|
- "Vzpomínáte, jak jste v AI 01 vytvářeli grid systém? Bootstrap to má už hotové." |
||||
|
- "Vzpomínáte na validaci formuláře v AI 03? Bootstrap má validační třídy." |
||||
|
|
||||
|
2. **Proč jsme to dělali ručně:** |
||||
|
- "Teď rozumíte, co Bootstrap dělá pod kapotou." |
||||
|
- "Když něco nebude fungovat, budete vědět proč." |
||||
|
- "Umíte to bez frameworku = jste lepší developer." |
||||
|
|
||||
|
3. **Kdy použít Bootstrap vs custom CSS:** |
||||
|
- Bootstrap: Rychlé prototypování, standardní projekty |
||||
|
- Custom: Unikátní design, optimalizace, portfolio projekty |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 📞 Podpora |
||||
|
|
||||
|
Pokud najdete chyby v projektech nebo máte návrhy na zlepšení, kontaktujte: |
||||
|
- GitHub Issues v repozitáři |
||||
|
- Email učiteli |
||||
|
- Během konzultačních hodin |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 📝 Poznámky k implementaci |
||||
|
|
||||
|
### Struktura souborů: |
||||
|
``` |
||||
|
ai_XX_project_name/ |
||||
|
├── ZADANI.md # Detailní zadání pro studenty |
||||
|
├── index.html # Startovací HTML (s TODO komentáři) |
||||
|
├── style.css # CSS šablona (s TODO komentáři) |
||||
|
├── script.js # JS šablona (s TODO komentáři) |
||||
|
├── reseni.html # Kompletní řešení |
||||
|
├── reseni.css # CSS s podrobnými komentáři |
||||
|
└── reseni.js # JS s podrobnými komentáři |
||||
|
``` |
||||
|
|
||||
|
### Filozofie: |
||||
|
- **Startovací soubory** obsahují TODO a strukturu |
||||
|
- **Řešení** obsahuje detailní komentáře vysvětlující každý řádek |
||||
|
- **ZADANI.md** obsahuje learning objectives a užitečné odkazy |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 🎉 Závěr |
||||
|
|
||||
|
Tyto projekty důkladně připraví studenty na Bootstrap. Investovaný čas se vrátí v: |
||||
|
- Lepším pochopení frameworku |
||||
|
- Schopnosti přizpůsobení |
||||
|
- Debugovacích dovednostech |
||||
|
- Celkově lepších web development skills |
||||
|
|
||||
|
**Hodně štěstí při výuce!** 🚀 |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
Vytvořeno: 24. října 2025 |
||||
|
Předmět: Webové Technologie (WTL) |
||||
|
Cílová skupina: 3. ročník IT |
||||
@ -0,0 +1,158 @@ |
|||||
|
# AI 01 - CSS Grid & Responzivní Layout |
||||
|
|
||||
|
## 🎯 Cíl projektu |
||||
|
|
||||
|
Naučit se pracovat s **CSS Grid systémem** a **pokročilými CSS selektory**, které jsou důležité pro pochopení Bootstrap grid systému. |
||||
|
|
||||
|
## 📋 Co se naučíte |
||||
|
|
||||
|
- **CSS Grid základy** - `display: grid`, `grid-template-columns` |
||||
|
- **Grid gaps** - mezery mezi položkami |
||||
|
- **Responzivní grid** s media queries |
||||
|
- **Pokročilé CSS selektory** - `:nth-child()`, `:first-child`, `:last-child` |
||||
|
- **CSS transformace** - `scale()` při hover |
||||
|
- **Grid template areas** - pojmenované oblasti |
||||
|
|
||||
|
## 📂 Soubory v projektu |
||||
|
|
||||
|
- **ZADANI.md** - detailní zadání projektu s požadavky |
||||
|
- **index.html** - startovací HTML soubor (s TODO komentáři pro vás) |
||||
|
- **style.css** - CSS šablona (s TODO komentáři) |
||||
|
- **reseni.html** - kompletní řešení (podívejte se, když zaseknete) |
||||
|
- **reseni.css** - CSS s podrobnými komentáři vysvětlující každý řádek |
||||
|
|
||||
|
## 🚀 Jak pracovat s projektem |
||||
|
|
||||
|
### Krok 1: Přečtěte si ZADANI.md |
||||
|
- Otevřete `ZADANI.md` a přečtěte si celé zadání |
||||
|
- Pochopte, co máte vytvořit |
||||
|
- Prohlédněte si ukázky kódu |
||||
|
|
||||
|
### Krok 2: Začněte s index.html a style.css |
||||
|
- Otevřete `index.html` a `style.css` |
||||
|
- Najděte **TODO komentáře** - to jsou místa, kde máte psát kód |
||||
|
- Postupujte podle TODO instrukcí |
||||
|
|
||||
|
### Krok 3: Testujte responzivitu |
||||
|
1. Otevřete `index.html` v prohlížeči |
||||
|
2. Otevřete Developer Tools (F12) |
||||
|
3. Zapněte Device Toolbar (Ctrl+Shift+M) |
||||
|
4. Testujte různé velikosti: |
||||
|
- Desktop (> 768px) - 3 sloupce |
||||
|
- Tablet (768px) - 2 sloupce |
||||
|
- Mobil (480px) - 1 sloupec |
||||
|
|
||||
|
### Krok 4: Když se zaseknete |
||||
|
- Podívejte se do `reseni.css` - jsou tam podrobné komentáře |
||||
|
- Zkuste pochopit PROČ to funguje, ne jen zkopírovat |
||||
|
- Porovnejte své řešení s hotovým |
||||
|
|
||||
|
## 💡 Klíčové koncepty |
||||
|
|
||||
|
### CSS Grid pattern: |
||||
|
```css |
||||
|
/* Kontejner */ |
||||
|
.grid-container { |
||||
|
display: grid; /* MUSÍ BÝT! */ |
||||
|
grid-template-columns: repeat(3, 1fr); /* 3 sloupce */ |
||||
|
gap: 20px; /* mezery */ |
||||
|
} |
||||
|
|
||||
|
/* Položky se umístí automaticky */ |
||||
|
.grid-item { |
||||
|
background-color: lightblue; |
||||
|
padding: 20px; |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### Responzivní breakpointy: |
||||
|
```css |
||||
|
/* Desktop (výchozí) - 3 sloupce */ |
||||
|
.grid-container { |
||||
|
grid-template-columns: repeat(3, 1fr); |
||||
|
} |
||||
|
|
||||
|
/* Tablet */ |
||||
|
@media (max-width: 768px) { |
||||
|
.grid-container { |
||||
|
grid-template-columns: repeat(2, 1fr); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* Mobil */ |
||||
|
@media (max-width: 480px) { |
||||
|
.grid-container { |
||||
|
grid-template-columns: 1fr; |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### Pokročilé selektory: |
||||
|
```css |
||||
|
/* Liché položky (1, 3, 5, ...) */ |
||||
|
.grid-item:nth-child(odd) { |
||||
|
background-color: lightblue; |
||||
|
} |
||||
|
|
||||
|
/* Sudé položky (2, 4, 6, ...) */ |
||||
|
.grid-item:nth-child(even) { |
||||
|
background-color: lightgreen; |
||||
|
} |
||||
|
|
||||
|
/* První položka */ |
||||
|
.grid-item:first-child { |
||||
|
background-color: gold; |
||||
|
} |
||||
|
|
||||
|
/* Poslední položka */ |
||||
|
.grid-item:last-child { |
||||
|
background-color: pink; |
||||
|
} |
||||
|
|
||||
|
/* Hover efekt */ |
||||
|
.grid-item:hover { |
||||
|
transform: scale(1.05); /* zvětší o 5% */ |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## ⚠️ Časté chyby |
||||
|
|
||||
|
❌ **Grid nefunguje** |
||||
|
→ Zapomněli jste `display: grid` na kontejneru |
||||
|
|
||||
|
❌ **Responzivita nefunguje** |
||||
|
→ Chybí `<meta name="viewport">` v `<head>` |
||||
|
|
||||
|
❌ **nth-child nefunguje** |
||||
|
→ Zkontrolujte syntax: `:nth-child(odd)` ne `.nth-child(odd)` |
||||
|
|
||||
|
❌ **Gap nefunguje** |
||||
|
→ `gap` funguje pouze v grid/flex containeru |
||||
|
|
||||
|
## 🎓 Hodnocení (10 bodů) |
||||
|
|
||||
|
- **HTML struktura** (2b) - správná struktura, 9+ položek |
||||
|
- **CSS Grid implementace** (3b) - grid-template-columns, gap |
||||
|
- **Media queries** (2b) - 3 breakpointy (desktop, tablet, mobil) |
||||
|
- **Pokročilé selektory** (2b) - nth-child, first/last-child, hover |
||||
|
- **Responzivita** (1b) - funguje na všech zařízeních |
||||
|
|
||||
|
## 🔗 Užitečné odkazy |
||||
|
|
||||
|
- [CSS Grid Guide - CSS Tricks](https://css-tricks.com/snippets/css/complete-guide-grid/) |
||||
|
- [nth-child selector - MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-child) |
||||
|
- [CSS Transform - MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/transform) |
||||
|
|
||||
|
## ⏱️ Odhadovaný čas |
||||
|
|
||||
|
**45-60 minut** |
||||
|
|
||||
|
## 🎯 Po dokončení |
||||
|
|
||||
|
Po dokončení tohoto projektu budete rozumět: |
||||
|
- Jak funguje CSS Grid (základ pro Bootstrap grid) |
||||
|
- Jak vytvořit responzivní layout bez frameworku |
||||
|
- Pokročilé CSS selektory |
||||
|
|
||||
|
➡️ **Další projekt:** AI 02 - Sémantické HTML5 & CSS Positioning |
||||
@ -0,0 +1,49 @@ |
|||||
|
# AI 01 - CSS Grid & Responzivní Layout |
||||
|
|
||||
|
## 🎯 Cíl projektu |
||||
|
Naučit se pracovat s CSS Grid systémem a pokročilými CSS selektory, které jsou důležité pro pochopení Bootstrap grid systému. |
||||
|
|
||||
|
## 📝 Zadání |
||||
|
|
||||
|
Vytvořte responzivní галерию obrázků s následujícími vlastnostmi: |
||||
|
|
||||
|
### HTML požadavky: |
||||
|
1. Vytvořte stránku s hlavičkou (h1) "Galerie Obrázků" |
||||
|
2. Vytvořte grid kontejner s minimálně 9 položkami (divy s třídou "grid-item") |
||||
|
3. Každá položka má obsahovat: |
||||
|
- Číslo položky (h3) |
||||
|
- Text "Obsah položky" (p) |
||||
|
- Barvu pozadí (přes CSS) |
||||
|
|
||||
|
### CSS požadavky: |
||||
|
|
||||
|
#### Grid Layout: |
||||
|
- Použijte `display: grid` |
||||
|
- Na desktopu: 3 sloupce se stejnou šířkou |
||||
|
- Na tabletu (max-width: 768px): 2 sloupce |
||||
|
- Na mobilu (max-width: 480px): 1 sloupec |
||||
|
- Mezery mezi položkami: 20px |
||||
|
|
||||
|
#### Pokročilé selektory: |
||||
|
Použijte tyto selektory pro obarvení položek: |
||||
|
- `.grid-item:nth-child(odd)` - liché položky -> světle modrá |
||||
|
- `.grid-item:nth-child(even)` - sudé položky -> světle zelená |
||||
|
- `.grid-item:first-child` - první položka -> zlatá |
||||
|
- `.grid-item:last-child` - poslední položka -> růžová |
||||
|
- `.grid-item:hover` - při najetí myší -> ztmavnout + zvětšit o 5% |
||||
|
|
||||
|
#### Extra výzva: |
||||
|
- Použijte `grid-template-areas` pro vytvoření speciálního layoutu, kde první položka zabírá 2 sloupce |
||||
|
|
||||
|
## 💡 Co se naučíte: |
||||
|
- CSS Grid základy (display: grid, grid-template-columns) |
||||
|
- Grid gaps (mezery) |
||||
|
- Responzivní grid s media queries |
||||
|
- Pokročilé CSS selektory (nth-child, first-child, last-child) |
||||
|
- CSS transformace (scale při hover) |
||||
|
|
||||
|
## 🔗 Užitečné odkazy: |
||||
|
- CSS Grid Guide: https://css-tricks.com/snippets/css/complete-guide-grid/ |
||||
|
- nth-child selector: https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-child |
||||
|
|
||||
|
## ⏱️ Odhadovaný čas: 45-60 minut |
||||
@ -0,0 +1,18 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang="cs"> |
||||
|
<head> |
||||
|
<meta charset="UTF-8"> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
|
<title>AI 01 - CSS Grid Galerie</title> |
||||
|
<link rel="stylesheet" href="style.css"> |
||||
|
</head> |
||||
|
<body> |
||||
|
<h1>Galerie Obrázků</h1> |
||||
|
|
||||
|
<!-- TODO: Zde začněte pracovat --> |
||||
|
<!-- Vytvořte kontejner s třídou "grid-container" --> |
||||
|
<!-- Vytvořte 9 divů s třídou "grid-item" --> |
||||
|
<!-- Každý div má obsahovat h3 s číslem a p s textem --> |
||||
|
|
||||
|
</body> |
||||
|
</html> |
||||
@ -0,0 +1,151 @@ |
|||||
|
/* AI 01 - CSS Grid & Responzivní Layout - ŘEŠENÍ */ |
||||
|
|
||||
|
/* Základní styly pro body */ |
||||
|
body { |
||||
|
font-family: Arial, Helvetica, sans-serif; |
||||
|
margin: 0; |
||||
|
padding: 20px; |
||||
|
background-color: #f0f0f0; |
||||
|
} |
||||
|
|
||||
|
h1 { |
||||
|
text-align: center; |
||||
|
color: #333; |
||||
|
margin-bottom: 30px; |
||||
|
} |
||||
|
|
||||
|
/* Grid kontejner */ |
||||
|
.grid-container { |
||||
|
/* Display grid aktivuje grid layout */ |
||||
|
display: grid; |
||||
|
|
||||
|
/* Vytvoří 3 sloupce se stejnou šířkou (1fr = 1 fraction/část) */ |
||||
|
grid-template-columns: 1fr 1fr 1fr; |
||||
|
/* Alternativně: repeat(3, 1fr) - opakuje 1fr třikrát */ |
||||
|
|
||||
|
/* Gap vytváří mezery mezi grid položkami */ |
||||
|
gap: 20px; |
||||
|
/* Alternativně: grid-gap: 20px; (starší syntax) */ |
||||
|
|
||||
|
/* Maximální šířka kontejneru */ |
||||
|
max-width: 1200px; |
||||
|
margin: 0 auto; |
||||
|
} |
||||
|
|
||||
|
/* Styling jednotlivých grid položek */ |
||||
|
.grid-item { |
||||
|
/* Základní styling */ |
||||
|
background-color: white; |
||||
|
padding: 30px; |
||||
|
border-radius: 10px; |
||||
|
text-align: center; |
||||
|
|
||||
|
/* Box shadow pro hezčí vzhled */ |
||||
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); |
||||
|
|
||||
|
/* Transition pro plynulé animace */ |
||||
|
transition: transform 0.3s ease, box-shadow 0.3s ease; |
||||
|
} |
||||
|
|
||||
|
.grid-item h3 { |
||||
|
margin-top: 0; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.grid-item p { |
||||
|
color: #666; |
||||
|
margin-bottom: 0; |
||||
|
} |
||||
|
|
||||
|
/* POKROČILÉ SELEKTORY */ |
||||
|
|
||||
|
/* nth-child(odd) = liché položky (1, 3, 5, 7, 9) */ |
||||
|
.grid-item:nth-child(odd) { |
||||
|
background-color: #e3f2fd; /* světle modrá */ |
||||
|
} |
||||
|
|
||||
|
/* nth-child(even) = sudé položky (2, 4, 6, 8) */ |
||||
|
.grid-item:nth-child(even) { |
||||
|
background-color: #e8f5e9; /* světle zelená */ |
||||
|
} |
||||
|
|
||||
|
/* first-child = první položka */ |
||||
|
.grid-item:first-child { |
||||
|
background-color: #ffd700; /* zlatá */ |
||||
|
/* Můžeme použít grid-column pro natažení přes 2 sloupce */ |
||||
|
grid-column: span 2; |
||||
|
} |
||||
|
|
||||
|
/* last-child = poslední položka */ |
||||
|
.grid-item:last-child { |
||||
|
background-color: #ffb6c1; /* růžová */ |
||||
|
} |
||||
|
|
||||
|
/* Hover efekt - při najetí myší */ |
||||
|
.grid-item:hover { |
||||
|
/* Transform scale zvětší element o 5% (1.05 = 105%) */ |
||||
|
transform: scale(1.05); |
||||
|
|
||||
|
/* Větší shadow při hover */ |
||||
|
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2); |
||||
|
|
||||
|
/* Můžeme také změnit kurzor */ |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
|
||||
|
/* MEDIA QUERIES - Responzivita */ |
||||
|
|
||||
|
/* Tablet - maximální šířka 768px */ |
||||
|
@media only screen and (max-width: 768px) { |
||||
|
.grid-container { |
||||
|
/* Na tabletu jen 2 sloupce */ |
||||
|
grid-template-columns: 1fr 1fr; |
||||
|
/* Alternativně: repeat(2, 1fr) */ |
||||
|
} |
||||
|
|
||||
|
/* První položka už nezabírá 2 sloupce na tabletu */ |
||||
|
.grid-item:first-child { |
||||
|
grid-column: span 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* Mobil - maximální šířka 480px */ |
||||
|
@media only screen and (max-width: 480px) { |
||||
|
body { |
||||
|
padding: 10px; |
||||
|
} |
||||
|
|
||||
|
.grid-container { |
||||
|
/* Na mobilu jen 1 sloupec */ |
||||
|
grid-template-columns: 1fr; |
||||
|
|
||||
|
/* Menší gap na mobilu */ |
||||
|
gap: 15px; |
||||
|
} |
||||
|
|
||||
|
.grid-item { |
||||
|
/* Menší padding na mobilu */ |
||||
|
padding: 20px; |
||||
|
} |
||||
|
|
||||
|
h1 { |
||||
|
font-size: 24px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* BONUS: Další užitečné selektory */ |
||||
|
|
||||
|
/* Každá třetí položka (3, 6, 9) */ |
||||
|
/* .grid-item:nth-child(3n) { |
||||
|
border: 3px solid #ff6b6b; |
||||
|
} */ |
||||
|
|
||||
|
/* Všechny položky kromě první */ |
||||
|
/* .grid-item:not(:first-child) { |
||||
|
opacity: 0.9; |
||||
|
} */ |
||||
|
|
||||
|
/* První 3 položky */ |
||||
|
/* .grid-item:nth-child(-n+3) { |
||||
|
font-weight: bold; |
||||
|
} */ |
||||
@ -0,0 +1,51 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang="cs"> |
||||
|
<head> |
||||
|
<meta charset="UTF-8"> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
|
<title>AI 01 - CSS Grid Galerie - ŘEŠENÍ</title> |
||||
|
<link rel="stylesheet" href="reseni.css"> |
||||
|
</head> |
||||
|
<body> |
||||
|
<h1>Galerie Obrázků</h1> |
||||
|
|
||||
|
<div class="grid-container"> |
||||
|
<div class="grid-item"> |
||||
|
<h3>Položka 1</h3> |
||||
|
<p>Obsah položky</p> |
||||
|
</div> |
||||
|
<div class="grid-item"> |
||||
|
<h3>Položka 2</h3> |
||||
|
<p>Obsah položky</p> |
||||
|
</div> |
||||
|
<div class="grid-item"> |
||||
|
<h3>Položka 3</h3> |
||||
|
<p>Obsah položky</p> |
||||
|
</div> |
||||
|
<div class="grid-item"> |
||||
|
<h3>Položka 4</h3> |
||||
|
<p>Obsah položky</p> |
||||
|
</div> |
||||
|
<div class="grid-item"> |
||||
|
<h3>Položka 5</h3> |
||||
|
<p>Obsah položky</p> |
||||
|
</div> |
||||
|
<div class="grid-item"> |
||||
|
<h3>Položka 6</h3> |
||||
|
<p>Obsah položky</p> |
||||
|
</div> |
||||
|
<div class="grid-item"> |
||||
|
<h3>Položka 7</h3> |
||||
|
<p>Obsah položky</p> |
||||
|
</div> |
||||
|
<div class="grid-item"> |
||||
|
<h3>Položka 8</h3> |
||||
|
<p>Obsah položky</p> |
||||
|
</div> |
||||
|
<div class="grid-item"> |
||||
|
<h3>Položka 9</h3> |
||||
|
<p>Obsah položky</p> |
||||
|
</div> |
||||
|
</div> |
||||
|
</body> |
||||
|
</html> |
||||
@ -0,0 +1,50 @@ |
|||||
|
/* AI 01 - CSS Grid & Responzivní Layout */ |
||||
|
|
||||
|
/* Základní styly pro body */ |
||||
|
body { |
||||
|
font-family: Arial, Helvetica, sans-serif; |
||||
|
margin: 0; |
||||
|
padding: 20px; |
||||
|
background-color: #f0f0f0; |
||||
|
} |
||||
|
|
||||
|
h1 { |
||||
|
text-align: center; |
||||
|
color: #333; |
||||
|
margin-bottom: 30px; |
||||
|
} |
||||
|
|
||||
|
/* TODO: Zde začněte pracovat s Grid */ |
||||
|
|
||||
|
/* 1. Vytvořte .grid-container s display: grid */ |
||||
|
|
||||
|
|
||||
|
/* 2. Nastavte grid-template-columns pro 3 sloupce (desktop) */ |
||||
|
|
||||
|
|
||||
|
/* 3. Nastavte gap (mezery) mezi položkami na 20px */ |
||||
|
|
||||
|
|
||||
|
/* 4. Nastylujte .grid-item */ |
||||
|
|
||||
|
|
||||
|
/* 5. Použijte pokročilé selektory pro obarvení */ |
||||
|
/* .grid-item:nth-child(odd) - liché položky */ |
||||
|
|
||||
|
|
||||
|
/* .grid-item:nth-child(even) - sudé položky */ |
||||
|
|
||||
|
|
||||
|
/* .grid-item:first-child - první položka */ |
||||
|
|
||||
|
|
||||
|
/* .grid-item:last-child - poslední položka */ |
||||
|
|
||||
|
|
||||
|
/* .grid-item:hover - hover efekt */ |
||||
|
|
||||
|
|
||||
|
/* 6. Media query pro tablet (max-width: 768px) - 2 sloupce */ |
||||
|
|
||||
|
|
||||
|
/* 7. Media query pro mobil (max-width: 480px) - 1 sloupec */ |
||||
@ -0,0 +1,221 @@ |
|||||
|
# AI 02 - Sémantické HTML5 & CSS Positioning |
||||
|
|
||||
|
## 🎯 Cíl projektu |
||||
|
|
||||
|
Naučit se používat **sémantické HTML5 elementy** a **CSS positioning** (absolute, relative, fixed, sticky), které jsou důležité pro moderní webový vývoj a Bootstrap. |
||||
|
|
||||
|
## 📋 Co se naučíte |
||||
|
|
||||
|
- **Sémantické HTML5 elementy** - `<nav>`, `<main>`, `<section>`, `<article>`, `<aside>`, `<footer>` |
||||
|
- **CSS Positioning** - `fixed`, `sticky`, `relative`, `absolute` |
||||
|
- **CSS jednotky** - `rem`, `em`, `vh`, `vw` (ne jen `px`!) |
||||
|
- **Strukturu moderní webové stránky** |
||||
|
- **Z-index** - vrstvení elementů |
||||
|
|
||||
|
## 📂 Soubory v projektu |
||||
|
|
||||
|
- **ZADANI.md** - detailní zadání projektu |
||||
|
- **index.html** - startovací HTML (s TODO komentáři) |
||||
|
- **style.css** - CSS šablona (s TODO komentáři) |
||||
|
- **reseni.html** - kompletní řešení |
||||
|
- **reseni.css** - CSS s podrobnými komentáři |
||||
|
|
||||
|
## 🚀 Jak pracovat s projektem |
||||
|
|
||||
|
### Krok 1: Přečtěte si ZADANI.md |
||||
|
- Pochopte, jaké elementy máte vytvořit |
||||
|
- Projděte si požadavky na positioning |
||||
|
|
||||
|
### Krok 2: Vytvořte strukturu stránky |
||||
|
Vytvořte **portfolio stránku** s těmito sekcemi: |
||||
|
1. **`<nav>`** - fixní navigace nahoře |
||||
|
2. **`<header>`** - úvodní sekce (hero) |
||||
|
3. **`<main>`** s `<section>` elementy: |
||||
|
- O mně |
||||
|
- Projekty (s `<article>` elementy) |
||||
|
- Kontakt |
||||
|
4. **`<aside>`** - sticky boční panel |
||||
|
5. **`<footer>`** - zápatí |
||||
|
|
||||
|
### Krok 3: Aplikujte CSS Positioning |
||||
|
|
||||
|
**Fixed navigace:** |
||||
|
```css |
||||
|
nav { |
||||
|
position: fixed; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
width: 100%; |
||||
|
z-index: 1000; /* nad ostatními elementy */ |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
**Sticky aside:** |
||||
|
```css |
||||
|
aside { |
||||
|
position: sticky; |
||||
|
top: 100px; /* od vrcholu po scrollování */ |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
**Absolute badge:** |
||||
|
```css |
||||
|
.project { |
||||
|
position: relative; /* kontejner */ |
||||
|
} |
||||
|
|
||||
|
.badge { |
||||
|
position: absolute; |
||||
|
top: 10px; |
||||
|
right: 10px; |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### Krok 4: Použijte moderní jednotky |
||||
|
```css |
||||
|
/* ❌ Staré (pouze px) */ |
||||
|
font-size: 16px; |
||||
|
padding: 20px; |
||||
|
height: 600px; |
||||
|
|
||||
|
/* ✅ Moderní (rem, em, vh) */ |
||||
|
font-size: 1rem; /* 16px (skaluje s root) */ |
||||
|
padding: 1.25rem; /* 20px */ |
||||
|
height: 60vh; /* 60% výšky viewportu */ |
||||
|
``` |
||||
|
|
||||
|
## 💡 Klíčové koncepty |
||||
|
|
||||
|
### Sémantické HTML5 elementy: |
||||
|
|
||||
|
```html |
||||
|
<!-- ✅ SPRÁVNĚ - sémantické --> |
||||
|
<nav> |
||||
|
<a href="#home">Domů</a> |
||||
|
</nav> |
||||
|
|
||||
|
<main> |
||||
|
<section id="about"> |
||||
|
<h2>O mně</h2> |
||||
|
<article>...</article> |
||||
|
</section> |
||||
|
</main> |
||||
|
|
||||
|
<aside>Boční panel</aside> |
||||
|
|
||||
|
<footer>© 2025</footer> |
||||
|
|
||||
|
<!-- ❌ ŠPATNĚ - nesémantické --> |
||||
|
<div class="nav"> |
||||
|
<a href="#home">Domů</a> |
||||
|
</div> |
||||
|
|
||||
|
<div class="main"> |
||||
|
<div class="section">...</div> |
||||
|
</div> |
||||
|
``` |
||||
|
|
||||
|
### CSS Positioning: |
||||
|
|
||||
|
**Static (výchozí):** |
||||
|
- Normální tok dokumentu |
||||
|
- Nelze posunout pomocí top/left/right/bottom |
||||
|
|
||||
|
**Relative:** |
||||
|
- Relativní k původní pozici |
||||
|
- Nezabírá místo v místě posunutí |
||||
|
```css |
||||
|
.element { |
||||
|
position: relative; |
||||
|
top: 20px; /* posune se 20px dolů od původní pozice */ |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
**Absolute:** |
||||
|
- Absolutní k nejbližšímu pozicovanému rodiči |
||||
|
- Vyjmut z normálního toku |
||||
|
```css |
||||
|
.parent { |
||||
|
position: relative; /* !! důležité !! */ |
||||
|
} |
||||
|
|
||||
|
.child { |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
right: 0; |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
**Fixed:** |
||||
|
- Fixní k viewportu |
||||
|
- Zůstává na místě při scrollování |
||||
|
```css |
||||
|
.navbar { |
||||
|
position: fixed; |
||||
|
top: 0; |
||||
|
width: 100%; |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
**Sticky:** |
||||
|
- Kombinace relative a fixed |
||||
|
- Sticky když dojedete na určitou pozici |
||||
|
```css |
||||
|
.sidebar { |
||||
|
position: sticky; |
||||
|
top: 20px; /* zůstane 20px od vrcholu */ |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### CSS Jednotky: |
||||
|
|
||||
|
| Jednotka | Popis | Použití | |
||||
|
|----------|-------|---------| |
||||
|
| `px` | Pixel (absolutní) | Borders, malé hodnoty | |
||||
|
| `rem` | Relativní k root font-size | Font sizes, paddingy | |
||||
|
| `em` | Relativní k parent font-size | Specifické případy | |
||||
|
| `%` | Procenta z parenta | Šířky, výšky | |
||||
|
| `vh` | 1% výšky viewportu | Full-screen sekce | |
||||
|
| `vw` | 1% šířky viewportu | Responzivní šířky | |
||||
|
|
||||
|
## ⚠️ Časté chyby |
||||
|
|
||||
|
❌ **Absolute element není kde má být** |
||||
|
→ Zapomněli jste `position: relative` na rodiče |
||||
|
|
||||
|
❌ **Fixed navigace překrývá obsah** |
||||
|
→ Přidejte `padding-top` na body nebo main |
||||
|
|
||||
|
❌ **Sticky nefunguje** |
||||
|
→ Zkontrolujte, že má rodič dostatek výšky na scrollování |
||||
|
|
||||
|
❌ **Z-index nefunguje** |
||||
|
→ Funguje pouze na pozicovaných elementech (ne static) |
||||
|
|
||||
|
## 🎓 Hodnocení (15 bodů) |
||||
|
|
||||
|
- **Sémantická struktura** (4b) - správné použití nav, main, section, article, aside, footer |
||||
|
- **Position: fixed, sticky** (3b) - fixní navigace, sticky aside |
||||
|
- **Position: relative, absolute** (3b) - badge na projektu |
||||
|
- **CSS jednotky (rem, em, vh)** (2b) - použití místo px |
||||
|
- **Responzivita** (3b) - aside zmizí na mobilu, funguje na všech zařízeních |
||||
|
|
||||
|
## 🔗 Užitečné odkazy |
||||
|
|
||||
|
- [HTML5 Semantic Elements - W3Schools](https://www.w3schools.com/html/html5_semantic_elements.asp) |
||||
|
- [CSS Position - CSS Tricks](https://css-tricks.com/almanac/properties/p/position/) |
||||
|
- [CSS Units - MDN](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Values_and_units) |
||||
|
|
||||
|
## ⏱️ Odhadovaný čas |
||||
|
|
||||
|
**60-90 minut** |
||||
|
|
||||
|
## 🎯 Po dokončení |
||||
|
|
||||
|
Po dokončení tohoto projektu budete rozumět: |
||||
|
- Sémantické HTML5 (důležité pro SEO a přístupnost) |
||||
|
- Všem typům CSS positioning |
||||
|
- Moderním CSS jednotkám (rem, vh, vw) |
||||
|
- Jak vytvořit sticky/fixed elementy |
||||
|
|
||||
|
➡️ **Další projekt:** AI 03 - Pokročilá Validace Formulářů s JavaScript |
||||
@ -0,0 +1,67 @@ |
|||||
|
# AI 02 - Sémantické HTML5 & CSS Positioning |
||||
|
|
||||
|
## 🎯 Cíl projektu |
||||
|
Naučit se používat sémantické HTML5 elementy a CSS positioning (absolute, relative, fixed, sticky), které jsou důležité pro moderní webový vývoj a Bootstrap. |
||||
|
|
||||
|
## 📝 Zadání |
||||
|
|
||||
|
Vytvořte jednoduchou osobní portfolio stránku s následujícími sekcemi: |
||||
|
|
||||
|
### HTML požadavky - Použijte SÉMANTICKÉ elementy: |
||||
|
|
||||
|
1. **`<nav>`** - Navigační menu (fixní nahoře) |
||||
|
- Logo (h2) |
||||
|
- 4 odkazy: Domů, O mně, Projekty, Kontakt |
||||
|
|
||||
|
2. **`<header>`** - Úvodní sekce |
||||
|
- Hlavní nadpis s vaším jménem (h1) |
||||
|
- Podnadpis s profesí (p) |
||||
|
- Tlačítko "Více o mně" |
||||
|
|
||||
|
3. **`<main>`** - Hlavní obsah stránky obsahující: |
||||
|
|
||||
|
**`<section id="about">`** - O mně |
||||
|
- Nadpis (h2) |
||||
|
- 2-3 odstavce textu (p) |
||||
|
- Obrázek (placeholder: https://via.placeholder.com/300) |
||||
|
|
||||
|
**`<section id="projects">`** - Projekty |
||||
|
- Nadpis (h2) |
||||
|
- 3x `<article>` s projekty |
||||
|
- Každý article má: h3 (název), p (popis), obrázek |
||||
|
|
||||
|
**`<section id="contact">`** - Kontakt |
||||
|
- Nadpis (h2) |
||||
|
- Formulář (jméno, email, zpráva, tlačítko) |
||||
|
|
||||
|
4. **`<footer>`** - Zápatí |
||||
|
- Copyright text |
||||
|
- Sociální sítě odkazy |
||||
|
|
||||
|
5. **`<aside>`** - Boční panel (sticky) |
||||
|
- "Rychlé info" |
||||
|
- Email, Telefon, Lokace |
||||
|
|
||||
|
### CSS Positioning požadavky: |
||||
|
|
||||
|
1. **`position: fixed`** - Navigace zůstává nahoře při scrollování |
||||
|
2. **`position: sticky`** - Aside zůstává viditelný při scrollování (od určitého bodu) |
||||
|
3. **`position: relative`** - Kontejner pro absolutně pozicované elementy |
||||
|
4. **`position: absolute`** - Badge "NOVÝ!" u jednoho projektu |
||||
|
|
||||
|
### Design požadavky: |
||||
|
- Použijte jednotky: rem, em, vh (ne jen px!) |
||||
|
- Přidejte barvy a odstupy pro přehlednost |
||||
|
- Responzivní (aside zmizí na mobilu) |
||||
|
|
||||
|
## 💡 Co se naučíte: |
||||
|
- Sémantické HTML5 elementy (nav, main, section, article, aside) |
||||
|
- CSS Positioning (fixed, sticky, relative, absolute) |
||||
|
- CSS jednotky (rem, em, vh, vw) |
||||
|
- Strukturu moderní webové stránky |
||||
|
|
||||
|
## 🔗 Užitečné odkazy: |
||||
|
- HTML5 Semantic Elements: https://www.w3schools.com/html/html5_semantic_elements.asp |
||||
|
- CSS Position: https://css-tricks.com/almanac/properties/p/position/ |
||||
|
|
||||
|
## ⏱️ Odhadovaný čas: 60-90 minut |
||||
@ -0,0 +1,36 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang="cs"> |
||||
|
<head> |
||||
|
<meta charset="UTF-8"> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
|
<title>AI 02 - Portfolio</title> |
||||
|
<link rel="stylesheet" href="style.css"> |
||||
|
</head> |
||||
|
<body> |
||||
|
<!-- TODO: Začněte zde --> |
||||
|
|
||||
|
<!-- 1. Vytvořte <nav> s navigací --> |
||||
|
|
||||
|
|
||||
|
<!-- 2. Vytvořte <header> s úvodní sekcí --> |
||||
|
|
||||
|
|
||||
|
<!-- 3. Vytvořte <main> s hlavním obsahem --> |
||||
|
|
||||
|
<!-- 3a. <section id="about"> - O mně --> |
||||
|
|
||||
|
|
||||
|
<!-- 3b. <section id="projects"> - Projekty (obsahuje 3x <article>) --> |
||||
|
|
||||
|
|
||||
|
<!-- 3c. <section id="contact"> - Kontakt s formulářem --> |
||||
|
|
||||
|
|
||||
|
<!-- 4. Vytvořte <aside> s rychlými informacemi (bude sticky) --> |
||||
|
|
||||
|
|
||||
|
<!-- 5. Vytvořte <footer> se zápatím --> |
||||
|
|
||||
|
|
||||
|
</body> |
||||
|
</html> |
||||
@ -0,0 +1,422 @@ |
|||||
|
/* AI 02 - Sémantické HTML5 & CSS Positioning - ŘEŠENÍ */ |
||||
|
|
||||
|
/* ============================================ |
||||
|
ZÁKLADNÍ RESET A NASTAVENÍ |
||||
|
============================================ */ |
||||
|
* { |
||||
|
margin: 0; |
||||
|
padding: 0; |
||||
|
box-sizing: border-box; |
||||
|
} |
||||
|
|
||||
|
/* Root font-size pro REM jednotky */ |
||||
|
html { |
||||
|
font-size: 16px; /* 1rem = 16px */ |
||||
|
scroll-behavior: smooth; /* Plynulé scrollování k kotevním odkazům */ |
||||
|
} |
||||
|
|
||||
|
body { |
||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
||||
|
line-height: 1.6; |
||||
|
color: #333; |
||||
|
/* Padding-top kvůli fixní navigaci */ |
||||
|
padding-top: 4rem; |
||||
|
} |
||||
|
|
||||
|
/* ============================================ |
||||
|
NAVIGACE - position: fixed |
||||
|
============================================ */ |
||||
|
nav { |
||||
|
/* FIXED positioning - zůstává na stejném místě při scrollování */ |
||||
|
position: fixed; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
width: 100%; |
||||
|
|
||||
|
background-color: #2c3e50; |
||||
|
padding: 1rem 0; |
||||
|
z-index: 1000; /* Vysoký z-index aby byl nav nad vším ostatním */ |
||||
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
|
||||
|
.nav-container { |
||||
|
max-width: 1200px; |
||||
|
margin: 0 auto; |
||||
|
padding: 0 2rem; |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.logo { |
||||
|
color: white; |
||||
|
font-size: 1.5rem; /* 24px (16px * 1.5) */ |
||||
|
margin: 0; |
||||
|
} |
||||
|
|
||||
|
.nav-links { |
||||
|
display: flex; |
||||
|
list-style: none; |
||||
|
gap: 2rem; /* Mezery mezi položkami */ |
||||
|
} |
||||
|
|
||||
|
.nav-links a { |
||||
|
color: white; |
||||
|
text-decoration: none; |
||||
|
font-size: 1rem; |
||||
|
transition: color 0.3s ease; |
||||
|
} |
||||
|
|
||||
|
.nav-links a:hover { |
||||
|
color: #3498db; |
||||
|
} |
||||
|
|
||||
|
/* ============================================ |
||||
|
HEADER - Úvodní sekce |
||||
|
============================================ */ |
||||
|
header { |
||||
|
/* vh jednotka = viewport height (výška viewportu) */ |
||||
|
height: 100vh; /* Celá výška obrazovky */ |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
text-align: center; |
||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
||||
|
color: white; |
||||
|
} |
||||
|
|
||||
|
header h1 { |
||||
|
font-size: 3rem; /* 48px */ |
||||
|
margin-bottom: 1rem; |
||||
|
animation: fadeInDown 1s ease; |
||||
|
} |
||||
|
|
||||
|
.subtitle { |
||||
|
font-size: 1.5rem; /* 24px */ |
||||
|
margin-bottom: 2rem; |
||||
|
opacity: 0.9; |
||||
|
} |
||||
|
|
||||
|
.btn { |
||||
|
display: inline-block; |
||||
|
padding: 0.75rem 2rem; |
||||
|
background-color: white; |
||||
|
color: #667eea; |
||||
|
text-decoration: none; |
||||
|
border-radius: 30px; |
||||
|
font-weight: bold; |
||||
|
transition: transform 0.3s ease, box-shadow 0.3s ease; |
||||
|
} |
||||
|
|
||||
|
.btn:hover { |
||||
|
transform: translateY(-3px); |
||||
|
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2); |
||||
|
} |
||||
|
|
||||
|
/* ============================================ |
||||
|
MAIN - Hlavní obsah |
||||
|
============================================ */ |
||||
|
main { |
||||
|
max-width: 1200px; |
||||
|
margin: 0 auto; |
||||
|
padding: 0 2rem; |
||||
|
/* Margin-right pro aside (boční panel) */ |
||||
|
margin-right: 320px; |
||||
|
} |
||||
|
|
||||
|
/* ============================================ |
||||
|
SECTION - Jednotlivé sekce |
||||
|
============================================ */ |
||||
|
section { |
||||
|
padding: 4rem 0; /* rem jednotka (relativní k root font-size) */ |
||||
|
border-bottom: 1px solid #eee; |
||||
|
} |
||||
|
|
||||
|
section h2 { |
||||
|
font-size: 2.5rem; /* 40px */ |
||||
|
margin-bottom: 2rem; |
||||
|
color: #2c3e50; |
||||
|
text-align: center; |
||||
|
} |
||||
|
|
||||
|
/* O MNĚ sekce */ |
||||
|
#about .about-content { |
||||
|
display: flex; |
||||
|
gap: 3rem; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
#about img { |
||||
|
border-radius: 10px; |
||||
|
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
|
||||
|
.about-text p { |
||||
|
margin-bottom: 1rem; |
||||
|
font-size: 1.1rem; |
||||
|
text-align: justify; |
||||
|
} |
||||
|
|
||||
|
/* PROJEKTY sekce */ |
||||
|
#projects .projects-grid { |
||||
|
display: grid; |
||||
|
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); |
||||
|
gap: 2rem; |
||||
|
margin-top: 2rem; |
||||
|
} |
||||
|
|
||||
|
/* ============================================ |
||||
|
ARTICLE - Projekt karty |
||||
|
============================================ */ |
||||
|
article.project-card { |
||||
|
background: white; |
||||
|
border-radius: 10px; |
||||
|
overflow: hidden; |
||||
|
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); |
||||
|
transition: transform 0.3s ease; |
||||
|
|
||||
|
/* RELATIVE positioning - vytváří kontext pro absolute positioning */ |
||||
|
position: relative; |
||||
|
} |
||||
|
|
||||
|
article.project-card:hover { |
||||
|
transform: translateY(-10px); |
||||
|
} |
||||
|
|
||||
|
article.project-card img { |
||||
|
width: 100%; |
||||
|
height: auto; |
||||
|
display: block; |
||||
|
} |
||||
|
|
||||
|
article.project-card h3 { |
||||
|
padding: 1rem; |
||||
|
font-size: 1.5rem; |
||||
|
color: #2c3e50; |
||||
|
} |
||||
|
|
||||
|
article.project-card p { |
||||
|
padding: 0 1rem 1.5rem; |
||||
|
color: #666; |
||||
|
line-height: 1.6; |
||||
|
} |
||||
|
|
||||
|
/* Badge "NOVÝ!" - position: absolute */ |
||||
|
.badge { |
||||
|
/* ABSOLUTE positioning - pozicuje se relativně k prvnímu parent elementu s position: relative */ |
||||
|
position: absolute; |
||||
|
top: 1rem; |
||||
|
right: 1rem; |
||||
|
|
||||
|
background-color: #e74c3c; |
||||
|
color: white; |
||||
|
padding: 0.5rem 1rem; |
||||
|
border-radius: 20px; |
||||
|
font-weight: bold; |
||||
|
font-size: 0.875rem; /* 14px */ |
||||
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); |
||||
|
} |
||||
|
|
||||
|
/* KONTAKT sekce */ |
||||
|
#contact { |
||||
|
max-width: 600px; |
||||
|
margin: 0 auto; |
||||
|
} |
||||
|
|
||||
|
.contact-form { |
||||
|
margin-top: 2rem; |
||||
|
} |
||||
|
|
||||
|
.form-group { |
||||
|
margin-bottom: 1.5rem; |
||||
|
} |
||||
|
|
||||
|
.form-group label { |
||||
|
display: block; |
||||
|
margin-bottom: 0.5rem; |
||||
|
font-weight: bold; |
||||
|
color: #2c3e50; |
||||
|
} |
||||
|
|
||||
|
.form-group input, |
||||
|
.form-group textarea { |
||||
|
width: 100%; |
||||
|
padding: 0.75rem; |
||||
|
border: 2px solid #ddd; |
||||
|
border-radius: 5px; |
||||
|
font-size: 1rem; |
||||
|
font-family: inherit; |
||||
|
transition: border-color 0.3s ease; |
||||
|
} |
||||
|
|
||||
|
.form-group input:focus, |
||||
|
.form-group textarea:focus { |
||||
|
outline: none; |
||||
|
border-color: #667eea; |
||||
|
} |
||||
|
|
||||
|
/* ============================================ |
||||
|
ASIDE - Boční panel, position: sticky |
||||
|
============================================ */ |
||||
|
aside { |
||||
|
/* STICKY positioning - chová se jako relative, dokud nedosáhne určitého bodu, pak se chová jako fixed */ |
||||
|
position: sticky; |
||||
|
top: 5rem; /* Odshora (pod navigací) */ |
||||
|
|
||||
|
/* Umístění vpravo */ |
||||
|
position: fixed; |
||||
|
right: 2rem; |
||||
|
top: 6rem; |
||||
|
width: 250px; |
||||
|
|
||||
|
background-color: #f8f9fa; |
||||
|
padding: 1.5rem; |
||||
|
border-radius: 10px; |
||||
|
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
|
||||
|
aside h3 { |
||||
|
color: #2c3e50; |
||||
|
margin-bottom: 1.5rem; |
||||
|
font-size: 1.25rem; |
||||
|
} |
||||
|
|
||||
|
.info-item { |
||||
|
margin-bottom: 1rem; |
||||
|
padding-bottom: 1rem; |
||||
|
border-bottom: 1px solid #ddd; |
||||
|
} |
||||
|
|
||||
|
.info-item:last-child { |
||||
|
border-bottom: none; |
||||
|
margin-bottom: 0; |
||||
|
padding-bottom: 0; |
||||
|
} |
||||
|
|
||||
|
.info-item strong { |
||||
|
display: block; |
||||
|
color: #667eea; |
||||
|
margin-bottom: 0.25rem; |
||||
|
font-size: 0.875rem; |
||||
|
} |
||||
|
|
||||
|
.info-item p { |
||||
|
color: #555; |
||||
|
margin: 0; |
||||
|
font-size: 0.875rem; |
||||
|
} |
||||
|
|
||||
|
/* ============================================ |
||||
|
FOOTER - Zápatí |
||||
|
============================================ */ |
||||
|
footer { |
||||
|
background-color: #2c3e50; |
||||
|
color: white; |
||||
|
text-align: center; |
||||
|
padding: 2rem; |
||||
|
margin-top: 4rem; |
||||
|
margin-right: 320px; /* Kvůli aside */ |
||||
|
} |
||||
|
|
||||
|
footer p { |
||||
|
margin-bottom: 1rem; |
||||
|
} |
||||
|
|
||||
|
.social-links { |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
gap: 2rem; |
||||
|
} |
||||
|
|
||||
|
.social-links a { |
||||
|
color: white; |
||||
|
text-decoration: none; |
||||
|
transition: color 0.3s ease; |
||||
|
} |
||||
|
|
||||
|
.social-links a:hover { |
||||
|
color: #3498db; |
||||
|
} |
||||
|
|
||||
|
/* ============================================ |
||||
|
ANIMACE |
||||
|
============================================ */ |
||||
|
@keyframes fadeInDown { |
||||
|
from { |
||||
|
opacity: 0; |
||||
|
transform: translateY(-20px); |
||||
|
} |
||||
|
to { |
||||
|
opacity: 1; |
||||
|
transform: translateY(0); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* ============================================ |
||||
|
MEDIA QUERIES - Responzivita |
||||
|
============================================ */ |
||||
|
@media screen and (max-width: 768px) { |
||||
|
/* Schovat aside na menších obrazovkách */ |
||||
|
aside { |
||||
|
display: none; |
||||
|
} |
||||
|
|
||||
|
/* Odstranit margin-right z main a footer */ |
||||
|
main, |
||||
|
footer { |
||||
|
margin-right: 0; |
||||
|
} |
||||
|
|
||||
|
/* Úprava navigace */ |
||||
|
.nav-container { |
||||
|
flex-direction: column; |
||||
|
gap: 1rem; |
||||
|
} |
||||
|
|
||||
|
.nav-links { |
||||
|
gap: 1rem; |
||||
|
} |
||||
|
|
||||
|
/* Zmenšit font sizes */ |
||||
|
header h1 { |
||||
|
font-size: 2rem; |
||||
|
} |
||||
|
|
||||
|
.subtitle { |
||||
|
font-size: 1.2rem; |
||||
|
} |
||||
|
|
||||
|
section h2 { |
||||
|
font-size: 2rem; |
||||
|
} |
||||
|
|
||||
|
/* O mně - stack na mobilu */ |
||||
|
#about .about-content { |
||||
|
flex-direction: column; |
||||
|
} |
||||
|
|
||||
|
/* Kontakt - celá šířka */ |
||||
|
#contact { |
||||
|
max-width: 100%; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@media screen and (max-width: 480px) { |
||||
|
body { |
||||
|
padding-top: 6rem; /* Více místa pro higher nav */ |
||||
|
} |
||||
|
|
||||
|
main { |
||||
|
padding: 0 1rem; |
||||
|
} |
||||
|
|
||||
|
section { |
||||
|
padding: 2rem 0; |
||||
|
} |
||||
|
|
||||
|
.projects-grid { |
||||
|
grid-template-columns: 1fr; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,119 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang="cs"> |
||||
|
<head> |
||||
|
<meta charset="UTF-8"> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
|
<title>AI 02 - Portfolio - ŘEŠENÍ</title> |
||||
|
<link rel="stylesheet" href="reseni.css"> |
||||
|
</head> |
||||
|
<body> |
||||
|
<!-- NAVIGACE - Sémantický element pro navigaci --> |
||||
|
<nav> |
||||
|
<div class="nav-container"> |
||||
|
<h2 class="logo">MůjWeb</h2> |
||||
|
<ul class="nav-links"> |
||||
|
<li><a href="#home">Domů</a></li> |
||||
|
<li><a href="#about">O mně</a></li> |
||||
|
<li><a href="#projects">Projekty</a></li> |
||||
|
<li><a href="#contact">Kontakt</a></li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
</nav> |
||||
|
|
||||
|
<!-- HEADER - Úvodní hlavička stránky --> |
||||
|
<header id="home"> |
||||
|
<h1>Jan Novák</h1> |
||||
|
<p class="subtitle">Web Developer & Designer</p> |
||||
|
<a href="#about" class="btn">Více o mně</a> |
||||
|
</header> |
||||
|
|
||||
|
<!-- MAIN - Hlavní obsah stránky --> |
||||
|
<main> |
||||
|
<!-- SECTION - Sémantický element pro sekci stránky --> |
||||
|
<section id="about"> |
||||
|
<h2>O mně</h2> |
||||
|
<div class="about-content"> |
||||
|
<img src="https://via.placeholder.com/300" alt="Profilový obrázek"> |
||||
|
<div class="about-text"> |
||||
|
<p>Jsem web developer s vášní pro vytváření moderních a responzivních webových stránek. Specializuji se na frontend technologie jako HTML, CSS, JavaScript a Bootstrap.</p> |
||||
|
<p>Ve volném čase se rád věnuji učení nových technologií a pracuji na open-source projektech. Mým cílem je vytvářet webové aplikace, které jsou nejen funkční, ale i esteticky příjemné.</p> |
||||
|
<p>Pokud hledáte někoho, kdo vašemu projektu dodá profesionální vzhled a špičkovou funkcionalitu, jste na správném místě!</p> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<!-- SECTION - Projekty --> |
||||
|
<section id="projects"> |
||||
|
<h2>Moje Projekty</h2> |
||||
|
<div class="projects-grid"> |
||||
|
<!-- ARTICLE - Sémantický element pro samostatný obsah --> |
||||
|
<article class="project-card"> |
||||
|
<span class="badge">NOVÝ!</span> |
||||
|
<img src="https://via.placeholder.com/350x200" alt="Projekt 1"> |
||||
|
<h3>E-shop s oblečením</h3> |
||||
|
<p>Moderní e-commerce stránka s košíkem, filtrováním produktů a responzivním designem. Použité technologie: HTML, CSS, JavaScript.</p> |
||||
|
</article> |
||||
|
|
||||
|
<article class="project-card"> |
||||
|
<img src="https://via.placeholder.com/350x200" alt="Projekt 2"> |
||||
|
<h3>Portfolio fotografie</h3> |
||||
|
<p>Elegantní portfolio pro fotografa s galerií, lightboxem a plynulými animacemi. Optimalizováno pro mobily.</p> |
||||
|
</article> |
||||
|
|
||||
|
<article class="project-card"> |
||||
|
<img src="https://via.placeholder.com/350x200" alt="Projekt 3"> |
||||
|
<h3>Firemní web</h3> |
||||
|
<p>Profesionální prezentace firmy s informacemi o službách, referencemi a kontaktním formulářem.</p> |
||||
|
</article> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<!-- SECTION - Kontakt --> |
||||
|
<section id="contact"> |
||||
|
<h2>Kontaktujte mě</h2> |
||||
|
<form class="contact-form"> |
||||
|
<div class="form-group"> |
||||
|
<label for="name">Jméno:</label> |
||||
|
<input type="text" id="name" name="name" required> |
||||
|
</div> |
||||
|
<div class="form-group"> |
||||
|
<label for="email">Email:</label> |
||||
|
<input type="email" id="email" name="email" required> |
||||
|
</div> |
||||
|
<div class="form-group"> |
||||
|
<label for="message">Zpráva:</label> |
||||
|
<textarea id="message" name="message" rows="5" required></textarea> |
||||
|
</div> |
||||
|
<button type="submit" class="btn">Odeslat zprávu</button> |
||||
|
</form> |
||||
|
</section> |
||||
|
</main> |
||||
|
|
||||
|
<!-- ASIDE - Boční panel s rychlými informacemi --> |
||||
|
<aside> |
||||
|
<h3>Rychlé info</h3> |
||||
|
<div class="info-item"> |
||||
|
<strong>Email:</strong> |
||||
|
<p>jan.novak@email.cz</p> |
||||
|
</div> |
||||
|
<div class="info-item"> |
||||
|
<strong>Telefon:</strong> |
||||
|
<p>+420 123 456 789</p> |
||||
|
</div> |
||||
|
<div class="info-item"> |
||||
|
<strong>Lokace:</strong> |
||||
|
<p>Praha, Česká republika</p> |
||||
|
</div> |
||||
|
</aside> |
||||
|
|
||||
|
<!-- FOOTER - Zápatí stránky --> |
||||
|
<footer> |
||||
|
<p>© 2025 Jan Novák. Všechna práva vyhrazena.</p> |
||||
|
<div class="social-links"> |
||||
|
<a href="#">Facebook</a> |
||||
|
<a href="#">LinkedIn</a> |
||||
|
<a href="#">GitHub</a> |
||||
|
</div> |
||||
|
</footer> |
||||
|
</body> |
||||
|
</html> |
||||
@ -0,0 +1,54 @@ |
|||||
|
/* AI 02 - Sémantické HTML5 & CSS Positioning */ |
||||
|
|
||||
|
/* Základní reset a nastavení */ |
||||
|
* { |
||||
|
margin: 0; |
||||
|
padding: 0; |
||||
|
box-sizing: border-box; |
||||
|
} |
||||
|
|
||||
|
/* TODO: Nastavte root font-size pro rem jednotky */ |
||||
|
html { |
||||
|
/* 1rem = 16px (výchozí) */ |
||||
|
} |
||||
|
|
||||
|
body { |
||||
|
font-family: Arial, Helvetica, sans-serif; |
||||
|
line-height: 1.6; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
/* TODO: NAVIGACE - position: fixed */ |
||||
|
/* nav {} */ |
||||
|
|
||||
|
|
||||
|
/* TODO: HEADER - úvodní sekce */ |
||||
|
/* header {} */ |
||||
|
|
||||
|
|
||||
|
/* TODO: MAIN - hlavní obsah */ |
||||
|
/* main {} */ |
||||
|
|
||||
|
|
||||
|
/* TODO: SECTION - jednotlivé sekce */ |
||||
|
/* section {} */ |
||||
|
|
||||
|
|
||||
|
/* TODO: ARTICLE - projekty */ |
||||
|
/* article {} */ |
||||
|
|
||||
|
|
||||
|
/* TODO: ASIDE - boční panel, position: sticky */ |
||||
|
/* aside {} */ |
||||
|
|
||||
|
|
||||
|
/* TODO: FOOTER - zápatí */ |
||||
|
/* footer {} */ |
||||
|
|
||||
|
|
||||
|
/* TODO: POSITIONING */ |
||||
|
/* Vytvořte badge "NOVÝ!" s position: absolute */ |
||||
|
|
||||
|
|
||||
|
/* TODO: MEDIA QUERIES */ |
||||
|
/* Na mobilu (max-width: 768px) schovat aside */ |
||||
@ -0,0 +1,301 @@ |
|||||
|
# AI 03 - Pokročilá Validace Formulářů s JavaScript |
||||
|
|
||||
|
## 🎯 Cíl projektu |
||||
|
|
||||
|
Naučit se **pokročilou práci s formuláři** a **JavaScript validací**. Bootstrap má vestavěné validační třídy, takže je důležité rozumět, jak validace funguje "pod kapotou". |
||||
|
|
||||
|
## 📋 Co se naučíte |
||||
|
|
||||
|
- **addEventListener** - moderní přístup místo inline `onclick` |
||||
|
- **Form validation** v JavaScriptu |
||||
|
- **RegEx** (regulární výrazy) pro validaci formátů |
||||
|
- **DOM manipulace** - `classList`, `innerHTML`, `textContent` |
||||
|
- **Event handling** - `input`, `submit` events |
||||
|
- **Real-time feedback** - validace při psaní |
||||
|
- **Visual feedback** - červená/zelená border, ikony, zprávy |
||||
|
|
||||
|
## 📂 Soubory v projektu |
||||
|
|
||||
|
- **ZADANI.md** - detailní zadání s požadavky na validaci |
||||
|
- **index.html** - startovací HTML (s TODO komentáři) |
||||
|
- **style.css** - CSS šablona |
||||
|
- **script.js** - JavaScript šablona (s TODO komentáři) |
||||
|
- **reseni.html** - kompletní řešení |
||||
|
- **reseni.css** - CSS s komentáři |
||||
|
- **reseni.js** - JavaScript s podrobnými komentáři |
||||
|
|
||||
|
## 🚀 Jak pracovat s projektem |
||||
|
|
||||
|
### Krok 1: Přečtěte si ZADANI.md |
||||
|
- Pochopte požadavky na validaci každého pole |
||||
|
- Projděte si příklady kódu |
||||
|
|
||||
|
### Krok 2: Vytvořte registrační formulář |
||||
|
|
||||
|
Formulář obsahuje: |
||||
|
1. **Jméno** - min 3 znaky, jen písmena |
||||
|
2. **Email** - validní formát |
||||
|
3. **Heslo** - min 8 znaků, velké písmeno, číslo, speciální znak |
||||
|
4. **Potvrzení hesla** - musí se shodovat |
||||
|
5. **Věk** - 18-120 |
||||
|
6. **Souhlas s podmínkami** - checkbox |
||||
|
|
||||
|
### Krok 3: Implementujte addEventListener |
||||
|
|
||||
|
❌ **STARÝ způsob (inline):** |
||||
|
```html |
||||
|
<input type="text" onchange="validateName()"> |
||||
|
``` |
||||
|
|
||||
|
✅ **NOVÝ způsob (addEventListener):** |
||||
|
```html |
||||
|
<input type="text" id="jmeno"> |
||||
|
``` |
||||
|
|
||||
|
```javascript |
||||
|
document.getElementById("jmeno").addEventListener("input", validateName); |
||||
|
``` |
||||
|
|
||||
|
### Krok 4: Vytvořte validační funkce |
||||
|
|
||||
|
```javascript |
||||
|
// Validace jména |
||||
|
function validateName() { |
||||
|
const nameInput = document.getElementById("jmeno"); |
||||
|
const nameValue = nameInput.value; |
||||
|
const errorElement = document.getElementById("jmeno-error"); |
||||
|
|
||||
|
// Kontrola délky |
||||
|
if (nameValue.length < 3) { |
||||
|
showError(nameInput, errorElement, "Jméno musí mít alespoň 3 znaky"); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// Kontrola pouze písmen (RegEx) |
||||
|
const nameRegex = /^[a-zA-ZáčďéěíňóřšťúůýžÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ\s]+$/; |
||||
|
if (!nameRegex.test(nameValue)) { |
||||
|
showError(nameInput, errorElement, "Jméno může obsahovat pouze písmena"); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// Vše OK |
||||
|
showSuccess(nameInput, errorElement); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
// Helper funkce |
||||
|
function showError(input, errorElement, message) { |
||||
|
input.classList.add("error"); |
||||
|
input.classList.remove("success"); |
||||
|
errorElement.textContent = message; |
||||
|
errorElement.style.display = "block"; |
||||
|
} |
||||
|
|
||||
|
function showSuccess(input, errorElement) { |
||||
|
input.classList.remove("error"); |
||||
|
input.classList.add("success"); |
||||
|
errorElement.style.display = "none"; |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## 💡 Klíčové koncepty |
||||
|
|
||||
|
### addEventListener vs onclick: |
||||
|
|
||||
|
```javascript |
||||
|
// ✅ MODERNÍ - addEventListener (doporučený) |
||||
|
const button = document.getElementById("myButton"); |
||||
|
button.addEventListener("click", function() { |
||||
|
console.log("Kliknuto!"); |
||||
|
}); |
||||
|
|
||||
|
// ❌ STARŠÍ - inline onclick |
||||
|
// <button onclick="handleClick()">Click</button> |
||||
|
``` |
||||
|
|
||||
|
**Výhody addEventListener:** |
||||
|
- Více listenerů na jeden element |
||||
|
- Oddělení HTML a JavaScriptu |
||||
|
- Možnost odstranit listener |
||||
|
- Lepší čitelnost |
||||
|
|
||||
|
### RegEx příklady: |
||||
|
|
||||
|
```javascript |
||||
|
// Email |
||||
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; |
||||
|
emailRegex.test("user@email.com"); // true |
||||
|
|
||||
|
// Heslo (min 8 znaků, velké, malé, číslo, spec. znak) |
||||
|
const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/; |
||||
|
|
||||
|
// Pouze písmena (včetně diakritiky) |
||||
|
const nameRegex = /^[a-zA-ZáčďéěíňóřšťúůýžÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ\s]+$/; |
||||
|
|
||||
|
// Telefon (CZ formát) |
||||
|
const phoneRegex = /^(\+420)?[0-9]{9}$/; |
||||
|
``` |
||||
|
|
||||
|
### classList manipulace: |
||||
|
|
||||
|
```javascript |
||||
|
// Přidání třídy |
||||
|
element.classList.add("error"); |
||||
|
|
||||
|
// Odebrání třídy |
||||
|
element.classList.remove("error"); |
||||
|
|
||||
|
// Toggle (přidá/odebere) |
||||
|
element.classList.toggle("active"); |
||||
|
|
||||
|
// Kontrola existence |
||||
|
if (element.classList.contains("error")) { |
||||
|
// ... |
||||
|
} |
||||
|
|
||||
|
// Více tříd najednou |
||||
|
element.classList.add("error", "shake"); |
||||
|
``` |
||||
|
|
||||
|
### Event types: |
||||
|
|
||||
|
```javascript |
||||
|
// Při každé změně (real-time) |
||||
|
input.addEventListener("input", validateName); |
||||
|
|
||||
|
// Po opuštění pole |
||||
|
input.addEventListener("blur", validateName); |
||||
|
|
||||
|
// Při odeslání formuláře |
||||
|
form.addEventListener("submit", function(e) { |
||||
|
e.preventDefault(); // zabrání odeslání |
||||
|
// validace... |
||||
|
}); |
||||
|
``` |
||||
|
|
||||
|
## 🎯 Úplný příklad - Email validace |
||||
|
|
||||
|
```html |
||||
|
<!-- HTML --> |
||||
|
<div class="form-group"> |
||||
|
<label for="email">Email:</label> |
||||
|
<input type="email" id="email" class="form-control"> |
||||
|
<span class="error-message" id="email-error"></span> |
||||
|
</div> |
||||
|
``` |
||||
|
|
||||
|
```javascript |
||||
|
// JavaScript |
||||
|
document.getElementById("email").addEventListener("input", validateEmail); |
||||
|
|
||||
|
function validateEmail() { |
||||
|
const emailInput = document.getElementById("email"); |
||||
|
const emailValue = emailInput.value; |
||||
|
const errorElement = document.getElementById("email-error"); |
||||
|
|
||||
|
// Email regex |
||||
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; |
||||
|
|
||||
|
if (!emailRegex.test(emailValue)) { |
||||
|
emailInput.classList.add("error"); |
||||
|
emailInput.classList.remove("success"); |
||||
|
errorElement.textContent = "Zadejte platný email"; |
||||
|
errorElement.style.display = "block"; |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
emailInput.classList.remove("error"); |
||||
|
emailInput.classList.add("success"); |
||||
|
errorElement.style.display = "none"; |
||||
|
return true; |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
```css |
||||
|
/* CSS */ |
||||
|
.form-control { |
||||
|
border: 2px solid #ccc; |
||||
|
transition: border-color 0.3s; |
||||
|
} |
||||
|
|
||||
|
.form-control.error { |
||||
|
border-color: #dc3545; /* červená */ |
||||
|
} |
||||
|
|
||||
|
.form-control.success { |
||||
|
border-color: #28a745; /* zelená */ |
||||
|
} |
||||
|
|
||||
|
.error-message { |
||||
|
color: #dc3545; |
||||
|
font-size: 0.875rem; |
||||
|
display: none; |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## ⚠️ Časté chyby |
||||
|
|
||||
|
❌ **addEventListener s závorkami** |
||||
|
```javascript |
||||
|
// ŠPATNĚ |
||||
|
input.addEventListener("input", validateName()); // volá funkci hned |
||||
|
|
||||
|
// SPRÁVNĚ |
||||
|
input.addEventListener("input", validateName); // předá referenci |
||||
|
``` |
||||
|
|
||||
|
❌ **Zapomenutý e.preventDefault()** |
||||
|
```javascript |
||||
|
form.addEventListener("submit", function(e) { |
||||
|
e.preventDefault(); // MUSÍ BÝT! Jinak se formulář odešle |
||||
|
// validace... |
||||
|
}); |
||||
|
``` |
||||
|
|
||||
|
❌ **RegEx bez escape znaků** |
||||
|
```javascript |
||||
|
// ŠPATNĚ |
||||
|
const regex = /^user@email.com$/; // . = jakýkoliv znak |
||||
|
|
||||
|
// SPRÁVNĚ |
||||
|
const regex = /^user@email\.com$/; // \. = literální tečka |
||||
|
``` |
||||
|
|
||||
|
## 🎓 Hodnocení (20 bodů) |
||||
|
|
||||
|
- **addEventListener použití** (4b) - všechny eventy přes addEventListener |
||||
|
- **Validační funkce** (6b) - 6 funkčních validací |
||||
|
- **RegEx implementace** (3b) - správné regex pro email, heslo, jméno |
||||
|
- **Visual feedback** (4b) - červená/zelená border, chybové zprávy |
||||
|
- **Submit handling** (3b) - kontrola všech polí, preventDefault |
||||
|
|
||||
|
## 🔗 Užitečné odkazy |
||||
|
|
||||
|
- [Form Validation - W3Schools](https://www.w3schools.com/js/js_validation.asp) |
||||
|
- [RegEx Tutorial - RegExr](https://regexr.com/) |
||||
|
- [addEventListener - MDN](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener) |
||||
|
- [RegEx Tester - Regex101](https://regex101.com/) |
||||
|
|
||||
|
## ⏱️ Odhadovaný čas |
||||
|
|
||||
|
**90-120 minut** |
||||
|
|
||||
|
## 💪 Bonus výzvy |
||||
|
|
||||
|
1. **Password strength meter** - slabé/střední/silné heslo |
||||
|
2. **Show/hide password** - ikona oka pro zobrazení hesla |
||||
|
3. **Email suggestion** - když napíšu `@gmal.com` → návrh `@gmail.com` |
||||
|
4. **Character counter** - zobrazí 3/20 znaků |
||||
|
5. **Shake animation** - třese se při chybě |
||||
|
|
||||
|
## 🎯 Po dokončení |
||||
|
|
||||
|
Po dokončení tohoto projektu budete rozumět: |
||||
|
- Modernímu event handlingu (addEventListener) |
||||
|
- Form validation v JavaScriptu |
||||
|
- RegEx pro validaci formátů |
||||
|
- Visual feedback a UX best practices |
||||
|
|
||||
|
Bootstrap form validation vám pak bude dávat smysl, protože budete vědět, co se děje "pod kapotou"! |
||||
|
|
||||
|
➡️ **Další projekt:** AI 04 - Komplexní Bootstrap Prep Projekt |
||||
@ -0,0 +1,93 @@ |
|||||
|
# AI 03 - Pokročilá Validace Formulářů s JavaScript |
||||
|
|
||||
|
## 🎯 Cíl projektu |
||||
|
Naučit se pokročilou práci s formuláři a JavaScript validací. Bootstrap má vestavěné validační třídy, takže je důležité rozumět, jak validace funguje. |
||||
|
|
||||
|
## 📝 Zadání |
||||
|
|
||||
|
Vytvořte registrační formulář s real-time validací pomocí JavaScriptu. |
||||
|
|
||||
|
### HTML požadavky - Formulářové pole: |
||||
|
|
||||
|
1. **Jméno** (text input) |
||||
|
- Min. 3 znaky, max. 20 znaků |
||||
|
- Pouze písmena |
||||
|
|
||||
|
2. **Email** (email input) |
||||
|
- Musí obsahovat @ a . |
||||
|
- Validní formát emailu |
||||
|
|
||||
|
3. **Heslo** (password input) |
||||
|
- Min. 8 znaků |
||||
|
- Alespoň 1 velké písmeno |
||||
|
- Alespoň 1 číslo |
||||
|
- Alespoň 1 speciální znak |
||||
|
|
||||
|
4. **Potvrzení hesla** (password input) |
||||
|
- Musí se shodovat s heslem |
||||
|
|
||||
|
5. **Věk** (number input) |
||||
|
- Min. 18, max. 120 |
||||
|
|
||||
|
6. **Souhlas s podmínkami** (checkbox) |
||||
|
- Musí být zaškrtnutý |
||||
|
|
||||
|
7. **Tlačítko Registrovat** (button) |
||||
|
|
||||
|
### JavaScript požadavky: |
||||
|
|
||||
|
#### 1. Použijte `addEventListener` místo inline onclick! |
||||
|
```javascript |
||||
|
document.getElementById("jmeno").addEventListener("input", validateName); |
||||
|
``` |
||||
|
|
||||
|
#### 2. Real-time validace |
||||
|
- Validujte při každé změně (event: "input") |
||||
|
- Zobrazujte chybové zprávy pod každým polem |
||||
|
- Měňte border color (červená = chyba, zelená = OK) |
||||
|
|
||||
|
#### 3. Validační funkce |
||||
|
Vytvořte separátní funkce pro validaci: |
||||
|
- `validateName()` |
||||
|
- `validateEmail()` |
||||
|
- `validatePassword()` |
||||
|
- `validatePasswordMatch()` |
||||
|
- `validateAge()` |
||||
|
- `validateCheckbox()` |
||||
|
|
||||
|
#### 4. Submit validace |
||||
|
- Při kliknutí na "Registrovat" zkontrolujte všechna pole |
||||
|
- Pokud je něco špatně, zobrazte alert a zabraňte odeslání |
||||
|
- Pokud je vše OK, zobrazte success zprávu |
||||
|
|
||||
|
#### 5. Visual feedback |
||||
|
- Červený border + chybová zpráva = chyba |
||||
|
- Zelený border = OK |
||||
|
- Ikona checkmark (✓) u správných polí |
||||
|
- Ikona X (✗) u chybných polí |
||||
|
|
||||
|
### CSS požadavky: |
||||
|
- Hezký design formuláře |
||||
|
- Responzivní |
||||
|
- Animace při chybách (shake effect) |
||||
|
|
||||
|
## 💡 Co se naučíte: |
||||
|
- addEventListener vs inline events |
||||
|
- Form validation v JavaScriptu |
||||
|
- RegEx (regulární výrazy) pro validaci |
||||
|
- DOM manipulace (classList, innerHTML) |
||||
|
- Event handling |
||||
|
- Podmínky a logické operátory |
||||
|
|
||||
|
## 🔗 Užitečné odkazy: |
||||
|
- Form Validation: https://www.w3schools.com/js/js_validation.asp |
||||
|
- RegEx Tutorial: https://regexr.com/ |
||||
|
- addEventListener: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener |
||||
|
|
||||
|
## ⏱️ Odhadovaný čas: 90-120 minut |
||||
|
|
||||
|
## 💪 Bonus výzvy: |
||||
|
1. Password strength meter (slabé/střední/silné heslo) |
||||
|
2. Show/hide password tlačítko |
||||
|
3. Email suggestion (když napíšu @gmal.com → návrd @gmail.com) |
||||
|
4. Character counter pro jméno |
||||
@ -0,0 +1,50 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang="cs"> |
||||
|
<head> |
||||
|
<meta charset="UTF-8"> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
|
<title>AI 03 - Validace Formulářů</title> |
||||
|
<link rel="stylesheet" href="style.css"> |
||||
|
</head> |
||||
|
<body> |
||||
|
<div class="container"> |
||||
|
<h1>Registrace</h1> |
||||
|
|
||||
|
<form id="registrationForm"> |
||||
|
<!-- TODO: Vytvořte formulářová pole podle zadání --> |
||||
|
|
||||
|
<!-- Příklad struktury pro jedno pole: --> |
||||
|
<!-- |
||||
|
<div class="form-group"> |
||||
|
<label for="jmeno">Jméno:</label> |
||||
|
<input type="text" id="jmeno" name="jmeno"> |
||||
|
<span class="error-message" id="jmenoError"></span> |
||||
|
</div> |
||||
|
--> |
||||
|
|
||||
|
<!-- 1. Pole pro jméno --> |
||||
|
|
||||
|
|
||||
|
<!-- 2. Pole pro email --> |
||||
|
|
||||
|
|
||||
|
<!-- 3. Pole pro heslo --> |
||||
|
|
||||
|
|
||||
|
<!-- 4. Pole pro potvrzení hesla --> |
||||
|
|
||||
|
|
||||
|
<!-- 5. Pole pro věk --> |
||||
|
|
||||
|
|
||||
|
<!-- 6. Checkbox pro souhlas s podmínkami --> |
||||
|
|
||||
|
|
||||
|
<!-- 7. Tlačítko --> |
||||
|
|
||||
|
</form> |
||||
|
</div> |
||||
|
|
||||
|
<script src="script.js"></script> |
||||
|
</body> |
||||
|
</html> |
||||
@ -0,0 +1,256 @@ |
|||||
|
/* AI 03 - Validace Formulářů - ŘEŠENÍ */ |
||||
|
|
||||
|
* { |
||||
|
margin: 0; |
||||
|
padding: 0; |
||||
|
box-sizing: border-box; |
||||
|
} |
||||
|
|
||||
|
body { |
||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
||||
|
min-height: 100vh; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
padding: 20px; |
||||
|
} |
||||
|
|
||||
|
.container { |
||||
|
background: white; |
||||
|
padding: 40px; |
||||
|
border-radius: 10px; |
||||
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); |
||||
|
width: 100%; |
||||
|
max-width: 500px; |
||||
|
} |
||||
|
|
||||
|
h1 { |
||||
|
text-align: center; |
||||
|
color: #333; |
||||
|
margin-bottom: 30px; |
||||
|
font-size: 2rem; |
||||
|
} |
||||
|
|
||||
|
/* ======================================== |
||||
|
FORM STYLING |
||||
|
======================================== */ |
||||
|
|
||||
|
.form-group { |
||||
|
margin-bottom: 20px; |
||||
|
position: relative; |
||||
|
} |
||||
|
|
||||
|
.form-group label { |
||||
|
display: block; |
||||
|
margin-bottom: 8px; |
||||
|
color: #333; |
||||
|
font-weight: 600; |
||||
|
font-size: 0.95rem; |
||||
|
} |
||||
|
|
||||
|
.form-group input { |
||||
|
width: 100%; |
||||
|
padding: 12px 15px; |
||||
|
border: 2px solid #ddd; |
||||
|
border-radius: 5px; |
||||
|
font-size: 1rem; |
||||
|
transition: all 0.3s ease; |
||||
|
font-family: inherit; |
||||
|
} |
||||
|
|
||||
|
.form-group input:focus { |
||||
|
outline: none; |
||||
|
border-color: #667eea; |
||||
|
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); |
||||
|
} |
||||
|
|
||||
|
.form-group small { |
||||
|
display: block; |
||||
|
margin-top: 5px; |
||||
|
color: #999; |
||||
|
font-size: 0.85rem; |
||||
|
} |
||||
|
|
||||
|
/* ======================================== |
||||
|
VALIDATION STATES |
||||
|
======================================== */ |
||||
|
|
||||
|
/* SUCCESS - zelený border */ |
||||
|
.form-group.success input { |
||||
|
border-color: #2ecc71; |
||||
|
} |
||||
|
|
||||
|
/* Přidá checkmark ikonu */ |
||||
|
.form-group.success input { |
||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cpath fill='%232ecc71' d='M7.629 14.566l-4.586-4.586-1.457 1.457 6.043 6.043 12.957-12.957-1.457-1.457z'/%3E%3C/svg%3E"); |
||||
|
background-repeat: no-repeat; |
||||
|
background-position: right 12px center; |
||||
|
background-size: 20px; |
||||
|
padding-right: 40px; |
||||
|
} |
||||
|
|
||||
|
/* ERROR - červený border */ |
||||
|
.form-group.error input { |
||||
|
border-color: #e74c3c; |
||||
|
} |
||||
|
|
||||
|
/* Přidá X ikonu */ |
||||
|
.form-group.error input { |
||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cpath fill='%23e74c3c' d='M10 8.586l-7.071-7.071-1.414 1.414 7.071 7.071-7.071 7.071 1.414 1.414 7.071-7.071 7.071 7.071 1.414-1.414-7.071-7.071 7.071-7.071-1.414-1.414z'/%3E%3C/svg%3E"); |
||||
|
background-repeat: no-repeat; |
||||
|
background-position: right 12px center; |
||||
|
background-size: 16px; |
||||
|
padding-right: 40px; |
||||
|
} |
||||
|
|
||||
|
/* ======================================== |
||||
|
ERROR MESSAGE |
||||
|
======================================== */ |
||||
|
|
||||
|
.error-message { |
||||
|
display: block; |
||||
|
color: #e74c3c; |
||||
|
font-size: 0.875rem; |
||||
|
margin-top: 5px; |
||||
|
min-height: 20px; |
||||
|
font-weight: 500; |
||||
|
} |
||||
|
|
||||
|
/* ======================================== |
||||
|
CHECKBOX GROUP |
||||
|
======================================== */ |
||||
|
|
||||
|
.checkbox-group { |
||||
|
margin-bottom: 25px; |
||||
|
} |
||||
|
|
||||
|
.checkbox-group label { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
font-weight: normal; |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
|
||||
|
.checkbox-group input[type="checkbox"] { |
||||
|
width: auto; |
||||
|
margin-right: 10px; |
||||
|
cursor: pointer; |
||||
|
width: 18px; |
||||
|
height: 18px; |
||||
|
} |
||||
|
|
||||
|
/* ======================================== |
||||
|
SUBMIT BUTTON |
||||
|
======================================== */ |
||||
|
|
||||
|
.btn-submit { |
||||
|
width: 100%; |
||||
|
padding: 15px; |
||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
||||
|
color: white; |
||||
|
border: none; |
||||
|
border-radius: 5px; |
||||
|
font-size: 1.1rem; |
||||
|
font-weight: bold; |
||||
|
cursor: pointer; |
||||
|
transition: transform 0.3s ease, box-shadow 0.3s ease; |
||||
|
} |
||||
|
|
||||
|
.btn-submit:hover { |
||||
|
transform: translateY(-2px); |
||||
|
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4); |
||||
|
} |
||||
|
|
||||
|
.btn-submit:active { |
||||
|
transform: translateY(0); |
||||
|
} |
||||
|
|
||||
|
.btn-submit:disabled { |
||||
|
background: #ccc; |
||||
|
cursor: not-allowed; |
||||
|
transform: none; |
||||
|
} |
||||
|
|
||||
|
/* ======================================== |
||||
|
SUCCESS MESSAGE |
||||
|
======================================== */ |
||||
|
|
||||
|
.success-box { |
||||
|
text-align: center; |
||||
|
padding: 30px; |
||||
|
background-color: #d4edda; |
||||
|
border: 2px solid #28a745; |
||||
|
border-radius: 10px; |
||||
|
margin-top: 20px; |
||||
|
} |
||||
|
|
||||
|
.success-box h2 { |
||||
|
color: #155724; |
||||
|
margin-bottom: 10px; |
||||
|
} |
||||
|
|
||||
|
.success-box p { |
||||
|
color: #155724; |
||||
|
} |
||||
|
|
||||
|
/* ======================================== |
||||
|
ANIMATIONS |
||||
|
======================================== */ |
||||
|
|
||||
|
/* Shake animace pro chyby */ |
||||
|
@keyframes shake { |
||||
|
0%, 100% { |
||||
|
transform: translateX(0); |
||||
|
} |
||||
|
10%, 30%, 50%, 70%, 90% { |
||||
|
transform: translateX(-10px); |
||||
|
} |
||||
|
20%, 40%, 60%, 80% { |
||||
|
transform: translateX(10px); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.form-group.error { |
||||
|
animation: shake 0.5s ease; |
||||
|
} |
||||
|
|
||||
|
/* Fade in animace */ |
||||
|
@keyframes fadeIn { |
||||
|
from { |
||||
|
opacity: 0; |
||||
|
transform: translateY(-20px); |
||||
|
} |
||||
|
to { |
||||
|
opacity: 1; |
||||
|
transform: translateY(0); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.success-box { |
||||
|
animation: fadeIn 0.5s ease; |
||||
|
} |
||||
|
|
||||
|
/* ======================================== |
||||
|
RESPONSIVE |
||||
|
======================================== */ |
||||
|
|
||||
|
@media screen and (max-width: 480px) { |
||||
|
.container { |
||||
|
padding: 25px; |
||||
|
} |
||||
|
|
||||
|
h1 { |
||||
|
font-size: 1.5rem; |
||||
|
} |
||||
|
|
||||
|
.form-group input { |
||||
|
padding: 10px 12px; |
||||
|
font-size: 0.95rem; |
||||
|
} |
||||
|
|
||||
|
.btn-submit { |
||||
|
padding: 12px; |
||||
|
font-size: 1rem; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,76 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang="cs"> |
||||
|
<head> |
||||
|
<meta charset="UTF-8"> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
|
<title>AI 03 - Validace Formulářů - ŘEŠENÍ</title> |
||||
|
<link rel="stylesheet" href="reseni.css"> |
||||
|
</head> |
||||
|
<body> |
||||
|
<div class="container"> |
||||
|
<h1>Registrace</h1> |
||||
|
|
||||
|
<form id="registrationForm"> |
||||
|
<!-- Jméno --> |
||||
|
<div class="form-group"> |
||||
|
<label for="jmeno">Jméno:</label> |
||||
|
<input type="text" id="jmeno" name="jmeno" placeholder="Jan Novák"> |
||||
|
<span class="error-message" id="jmenoError"></span> |
||||
|
<small>Min. 3 znaky, max. 20 znaků, pouze písmena</small> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Email --> |
||||
|
<div class="form-group"> |
||||
|
<label for="email">Email:</label> |
||||
|
<input type="email" id="email" name="email" placeholder="jan@email.cz"> |
||||
|
<span class="error-message" id="emailError"></span> |
||||
|
<small>Zadejte platný email</small> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Heslo --> |
||||
|
<div class="form-group"> |
||||
|
<label for="heslo">Heslo:</label> |
||||
|
<input type="password" id="heslo" name="heslo" placeholder="••••••••"> |
||||
|
<span class="error-message" id="hesloError"></span> |
||||
|
<small>Min. 8 znaků, 1 velké písmeno, 1 číslo, 1 speciální znak</small> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Potvrzení hesla --> |
||||
|
<div class="form-group"> |
||||
|
<label for="hesloPotvrzeni">Potvrzení hesla:</label> |
||||
|
<input type="password" id="hesloPotvrzeni" name="hesloPotvrzeni" placeholder="••••••••"> |
||||
|
<span class="error-message" id="hesloPotvrzeniError"></span> |
||||
|
<small>Hesla se musí shodovat</small> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Věk --> |
||||
|
<div class="form-group"> |
||||
|
<label for="vek">Věk:</label> |
||||
|
<input type="number" id="vek" name="vek" placeholder="18"> |
||||
|
<span class="error-message" id="vekError"></span> |
||||
|
<small>Min. 18 let</small> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Checkbox --> |
||||
|
<div class="form-group checkbox-group"> |
||||
|
<label> |
||||
|
<input type="checkbox" id="souhlas" name="souhlas"> |
||||
|
Souhlasím s podmínkami používání |
||||
|
</label> |
||||
|
<span class="error-message" id="souhlasError"></span> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Submit button --> |
||||
|
<button type="submit" class="btn-submit">Registrovat</button> |
||||
|
</form> |
||||
|
|
||||
|
<!-- Success message (skrytá) --> |
||||
|
<div id="successMessage" class="success-box" style="display: none;"> |
||||
|
<h2>✓ Registrace úspěšná!</h2> |
||||
|
<p>Váš účet byl úspěšně vytvořen.</p> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<script src="reseni.js"></script> |
||||
|
</body> |
||||
|
</html> |
||||
@ -0,0 +1,376 @@ |
|||||
|
// AI 03 - Validace Formulářů - ŘEŠENÍ
|
||||
|
|
||||
|
console.log("Script načten"); |
||||
|
|
||||
|
// ========================================
|
||||
|
// ZÍSKÁNÍ REFERENCÍ NA ELEMENTY
|
||||
|
// ========================================
|
||||
|
|
||||
|
const jmenoInput = document.getElementById("jmeno"); |
||||
|
const emailInput = document.getElementById("email"); |
||||
|
const hesloInput = document.getElementById("heslo"); |
||||
|
const hesloPotvrzeniInput = document.getElementById("hesloPotvrzeni"); |
||||
|
const vekInput = document.getElementById("vek"); |
||||
|
const souhlasInput = document.getElementById("souhlas"); |
||||
|
const form = document.getElementById("registrationForm"); |
||||
|
const successMessage = document.getElementById("successMessage"); |
||||
|
|
||||
|
// ========================================
|
||||
|
// EVENT LISTENERS - REAL-TIME VALIDACE
|
||||
|
// ========================================
|
||||
|
|
||||
|
// Důležité: Používáme addEventListener místo inline onclick!
|
||||
|
// Výhody:
|
||||
|
// 1. Oddělení HTML a JavaScriptu
|
||||
|
// 2. Můžeme přidat více listenerů na jeden element
|
||||
|
// 3. Lepší kontrola nad event objektem
|
||||
|
|
||||
|
jmenoInput.addEventListener("input", validateName); |
||||
|
emailInput.addEventListener("input", validateEmail); |
||||
|
hesloInput.addEventListener("input", validatePassword); |
||||
|
hesloPotvrzeniInput.addEventListener("input", validatePasswordMatch); |
||||
|
vekInput.addEventListener("input", validateAge); |
||||
|
souhlasInput.addEventListener("change", validateCheckbox); |
||||
|
|
||||
|
// Event listener pro submit formuláře
|
||||
|
form.addEventListener("submit", handleSubmit); |
||||
|
|
||||
|
// ========================================
|
||||
|
// VALIDAČNÍ FUNKCE
|
||||
|
// ========================================
|
||||
|
|
||||
|
/** |
||||
|
* Validace jména |
||||
|
* Požadavky: |
||||
|
* - Min. 3 znaky |
||||
|
* - Max. 20 znaků |
||||
|
* - Pouze písmena (včetně diakritiky) |
||||
|
*/ |
||||
|
function validateName() { |
||||
|
const jmeno = jmenoInput.value.trim(); |
||||
|
|
||||
|
// Kontrola prázdného pole
|
||||
|
if (jmeno === "") { |
||||
|
showError(jmenoInput, "Jméno je povinné"); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// Kontrola délky
|
||||
|
if (jmeno.length < 3) { |
||||
|
showError(jmenoInput, "Jméno musí mít alespoň 3 znaky"); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
if (jmeno.length > 20) { |
||||
|
showError(jmenoInput, "Jméno může mít maximálně 20 znaků"); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// RegEx pro kontrolu, že obsahuje pouze písmena (včetně české diakritiky)
|
||||
|
// ^ = začátek stringu
|
||||
|
// [a-zA-Z...] = povolené znaky
|
||||
|
// \s = mezera (povolíme mezery mezi jménem a příjmením)
|
||||
|
// + = jeden nebo více
|
||||
|
// $ = konec stringu
|
||||
|
const nameRegex = /^[a-zA-ZáčďéěíňóřšťúůýžÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ\s]+$/; |
||||
|
|
||||
|
if (!nameRegex.test(jmeno)) { |
||||
|
showError(jmenoInput, "Jméno může obsahovat pouze písmena"); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// Vše OK!
|
||||
|
showSuccess(jmenoInput); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Validace emailu |
||||
|
* Požadavek: Validní formát emailu (obsahuje @ a .) |
||||
|
*/ |
||||
|
function validateEmail() { |
||||
|
const email = emailInput.value.trim(); |
||||
|
|
||||
|
if (email === "") { |
||||
|
showError(emailInput, "Email je povinný"); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// RegEx pro email validaci
|
||||
|
// [^\s@]+ = jeden nebo více znaků, které nejsou mezera nebo @
|
||||
|
// @ = musí obsahovat @
|
||||
|
// [^\s@]+ = doména
|
||||
|
// \. = musí obsahovat tečku
|
||||
|
// [^\s@]+ = koncovka (.cz, .com, atd.)
|
||||
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; |
||||
|
|
||||
|
if (!emailRegex.test(email)) { |
||||
|
showError(emailInput, "Zadejte platný email"); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
showSuccess(emailInput); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Validace hesla |
||||
|
* Požadavky: |
||||
|
* - Min. 8 znaků |
||||
|
* - Alespoň 1 velké písmeno |
||||
|
* - Alespoň 1 číslo |
||||
|
* - Alespoň 1 speciální znak |
||||
|
*/ |
||||
|
function validatePassword() { |
||||
|
const heslo = hesloInput.value; |
||||
|
|
||||
|
if (heslo === "") { |
||||
|
showError(hesloInput, "Heslo je povinné"); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// Kontrola délky
|
||||
|
if (heslo.length < 8) { |
||||
|
showError(hesloInput, "Heslo musí mít alespoň 8 znaků"); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// Kontrola velkého písmena
|
||||
|
// [A-Z] = obsahuje alespoň jedno velké písmeno
|
||||
|
if (!/[A-Z]/.test(heslo)) { |
||||
|
showError(hesloInput, "Heslo musí obsahovat alespoň jedno velké písmeno"); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// Kontrola čísla
|
||||
|
// [0-9] = obsahuje alespoň jedno číslo
|
||||
|
if (!/[0-9]/.test(heslo)) { |
||||
|
showError(hesloInput, "Heslo musí obsahovat alespoň jedno číslo"); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// Kontrola speciálního znaku
|
||||
|
// [!@#$%^&*] = obsahuje alespoň jeden speciální znak
|
||||
|
if (!/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(heslo)) { |
||||
|
showError(hesloInput, "Heslo musí obsahovat alespoň jeden speciální znak"); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
showSuccess(hesloInput); |
||||
|
|
||||
|
// Pokud je heslo validní, zkontroluj i potvrzení hesla
|
||||
|
if (hesloPotvrzeniInput.value !== "") { |
||||
|
validatePasswordMatch(); |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Validace shody hesel |
||||
|
*/ |
||||
|
function validatePasswordMatch() { |
||||
|
const heslo = hesloInput.value; |
||||
|
const hesloPotvrzeni = hesloPotvrzeniInput.value; |
||||
|
|
||||
|
if (hesloPotvrzeni === "") { |
||||
|
showError(hesloPotvrzeniInput, "Potvrzení hesla je povinné"); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// Porovnání hesel
|
||||
|
if (heslo !== hesloPotvrzeni) { |
||||
|
showError(hesloPotvrzeniInput, "Hesla se neshodují"); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
showSuccess(hesloPotvrzeniInput); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Validace věku |
||||
|
* Požadavek: Min. 18 let, max. 120 let |
||||
|
*/ |
||||
|
function validateAge() { |
||||
|
const vek = parseInt(vekInput.value); |
||||
|
|
||||
|
if (isNaN(vek) || vekInput.value === "") { |
||||
|
showError(vekInput, "Věk je povinný"); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
if (vek < 18) { |
||||
|
showError(vekInput, "Musíte být starší 18 let"); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
if (vek > 120) { |
||||
|
showError(vekInput, "Zadejte reálný věk"); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
showSuccess(vekInput); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Validace checkboxu |
||||
|
* Požadavek: Musí být zaškrtnutý |
||||
|
*/ |
||||
|
function validateCheckbox() { |
||||
|
// .checked vrací true/false podle toho, jestli je checkbox zaškrtnutý
|
||||
|
if (!souhlasInput.checked) { |
||||
|
showError(souhlasInput, "Musíte souhlasit s podmínkami"); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
showSuccess(souhlasInput); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
// ========================================
|
||||
|
// HELPER FUNKCE
|
||||
|
// ========================================
|
||||
|
|
||||
|
/** |
||||
|
* Zobrazí chybovou zprávu a nastaví error stav |
||||
|
* @param {HTMLElement} inputElement - Input element |
||||
|
* @param {string} message - Chybová zpráva |
||||
|
*/ |
||||
|
function showError(inputElement, message) { |
||||
|
// Najdi parent element (.form-group)
|
||||
|
// .parentElement vrací rodičovský element
|
||||
|
const formGroup = inputElement.parentElement; |
||||
|
|
||||
|
// Přidej třídu "error" pro styling
|
||||
|
formGroup.classList.add("error"); |
||||
|
|
||||
|
// Odeber třídu "success" (pokud tam byla)
|
||||
|
formGroup.classList.remove("success"); |
||||
|
|
||||
|
// Najdi element pro chybovou zprávu
|
||||
|
// Předpokládáme, že má id ve formátu: inputId + "Error"
|
||||
|
const errorElement = formGroup.querySelector(".error-message"); |
||||
|
|
||||
|
// Nastav text chybové zprávy
|
||||
|
if (errorElement) { |
||||
|
errorElement.textContent = message; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Zobrazí success stav (zelený border, checkmark) |
||||
|
* @param {HTMLElement} inputElement - Input element |
||||
|
*/ |
||||
|
function showSuccess(inputElement) { |
||||
|
const formGroup = inputElement.parentElement; |
||||
|
|
||||
|
// Přidej třídu "success"
|
||||
|
formGroup.classList.add("success"); |
||||
|
|
||||
|
// Odeber třídu "error"
|
||||
|
formGroup.classList.remove("error"); |
||||
|
|
||||
|
// Vyčisti chybovou zprávu
|
||||
|
const errorElement = formGroup.querySelector(".error-message"); |
||||
|
if (errorElement) { |
||||
|
errorElement.textContent = ""; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// ========================================
|
||||
|
// SUBMIT HANDLER
|
||||
|
// ========================================
|
||||
|
|
||||
|
/** |
||||
|
* Handler pro submit formuláře |
||||
|
* @param {Event} e - Event objekt |
||||
|
*/ |
||||
|
function handleSubmit(e) { |
||||
|
// Zabraň výchozímu chování (odeslání formuláře a reload stránky)
|
||||
|
e.preventDefault(); |
||||
|
|
||||
|
console.log("Formulář odeslán"); |
||||
|
|
||||
|
// Zavolej všechny validační funkce
|
||||
|
const isNameValid = validateName(); |
||||
|
const isEmailValid = validateEmail(); |
||||
|
const isPasswordValid = validatePassword(); |
||||
|
const isPasswordMatchValid = validatePasswordMatch(); |
||||
|
const isAgeValid = validateAge(); |
||||
|
const isCheckboxValid = validateCheckbox(); |
||||
|
|
||||
|
// Zkontroluj, jestli jsou všechna pole validní
|
||||
|
// Logický AND (&&) - vrací true pouze pokud jsou všechny hodnoty true
|
||||
|
const isFormValid = |
||||
|
isNameValid && |
||||
|
isEmailValid && |
||||
|
isPasswordValid && |
||||
|
isPasswordMatchValid && |
||||
|
isAgeValid && |
||||
|
isCheckboxValid; |
||||
|
|
||||
|
if (isFormValid) { |
||||
|
// Vše OK - zobraz success zprávu
|
||||
|
console.log("✓ Formulář je validní!"); |
||||
|
|
||||
|
// Schovat formulář
|
||||
|
form.style.display = "none"; |
||||
|
|
||||
|
// Zobrazit success zprávu
|
||||
|
successMessage.style.display = "block"; |
||||
|
|
||||
|
// V reálné aplikaci bychom zde odeslali data na server:
|
||||
|
// fetch("/api/register", {
|
||||
|
// method: "POST",
|
||||
|
// body: JSON.stringify({
|
||||
|
// jmeno: jmenoInput.value,
|
||||
|
// email: emailInput.value,
|
||||
|
// ...
|
||||
|
// })
|
||||
|
// });
|
||||
|
|
||||
|
} else { |
||||
|
// Něco je špatně - zobraz alert
|
||||
|
console.log("✗ Formulář obsahuje chyby!"); |
||||
|
alert("Prosím opravte chyby ve formuláři"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// ========================================
|
||||
|
// BONUS FUNKCE
|
||||
|
// ========================================
|
||||
|
|
||||
|
/** |
||||
|
* BONUS: Password strength meter |
||||
|
* Můžete přidat indikátor síly hesla |
||||
|
*/ |
||||
|
function checkPasswordStrength(password) { |
||||
|
let strength = 0; |
||||
|
|
||||
|
if (password.length >= 8) strength++; |
||||
|
if (password.length >= 12) strength++; |
||||
|
if (/[a-z]/.test(password)) strength++; |
||||
|
if (/[A-Z]/.test(password)) strength++; |
||||
|
if (/[0-9]/.test(password)) strength++; |
||||
|
if (/[^a-zA-Z0-9]/.test(password)) strength++; |
||||
|
|
||||
|
if (strength <= 2) return "Slabé"; |
||||
|
if (strength <= 4) return "Střední"; |
||||
|
return "Silné"; |
||||
|
} |
||||
|
|
||||
|
// ========================================
|
||||
|
// DEBUG INFO
|
||||
|
// ========================================
|
||||
|
|
||||
|
console.log("Validační skrip připraven"); |
||||
|
console.log("Event listenery přidány na:", { |
||||
|
jmeno: jmenoInput, |
||||
|
email: emailInput, |
||||
|
heslo: hesloInput, |
||||
|
hesloPotvrzeni: hesloPotvrzeniInput, |
||||
|
vek: vekInput, |
||||
|
souhlas: souhlasInput |
||||
|
}); |
||||
@ -0,0 +1,96 @@ |
|||||
|
// AI 03 - Validace Formulářů - JavaScript
|
||||
|
|
||||
|
console.log("Script načten"); |
||||
|
|
||||
|
// TODO: Získejte reference na elementy
|
||||
|
// const jmenoInput = document.getElementById("jmeno");
|
||||
|
// const emailInput = document.getElementById("email");
|
||||
|
// ... atd
|
||||
|
|
||||
|
|
||||
|
// TODO: Přidejte event listenery pro real-time validaci
|
||||
|
// jmenoInput.addEventListener("input", validateName);
|
||||
|
// emailInput.addEventListener("input", validateEmail);
|
||||
|
// ... atd
|
||||
|
|
||||
|
|
||||
|
// TODO: Event listener pro submit formuláře
|
||||
|
// document.getElementById("registrationForm").addEventListener("submit", handleSubmit);
|
||||
|
|
||||
|
|
||||
|
// ========================================
|
||||
|
// VALIDAČNÍ FUNKCE
|
||||
|
// ========================================
|
||||
|
|
||||
|
// TODO: Funkce pro validaci jména
|
||||
|
function validateName() { |
||||
|
// 1. Získej hodnotu z inputu
|
||||
|
// 2. Zkontroluj délku (min 3, max 20)
|
||||
|
// 3. Zkontroluj, že obsahuje jen písmena (použij RegEx: /^[a-zA-ZáčďéěíňóřšťúůýžÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ\s]+$/)
|
||||
|
// 4. Zobraz chybu nebo success
|
||||
|
} |
||||
|
|
||||
|
// TODO: Funkce pro validaci emailu
|
||||
|
function validateEmail() { |
||||
|
// 1. Získej hodnotu
|
||||
|
// 2. Zkontroluj formát emailu (RegEx: /^[^\s@]+@[^\s@]+\.[^\s@]+$/)
|
||||
|
// 3. Zobraz výsledek
|
||||
|
} |
||||
|
|
||||
|
// TODO: Funkce pro validaci hesla
|
||||
|
function validatePassword() { |
||||
|
// Musí obsahovat:
|
||||
|
// - Min. 8 znaků
|
||||
|
// - Alespoň 1 velké písmeno (RegEx: /[A-Z]/)
|
||||
|
// - Alespoň 1 číslo (RegEx: /[0-9]/)
|
||||
|
// - Alespoň 1 speciální znak (RegEx: /[!@#$%^&*]/)
|
||||
|
} |
||||
|
|
||||
|
// TODO: Funkce pro kontrolu shody hesel
|
||||
|
function validatePasswordMatch() { |
||||
|
// Porovnej hodnoty hesla a potvrzení hesla
|
||||
|
} |
||||
|
|
||||
|
// TODO: Funkce pro validaci věku
|
||||
|
function validateAge() { |
||||
|
// Zkontroluj, že věk je číslo mezi 18 a 120
|
||||
|
} |
||||
|
|
||||
|
// TODO: Funkce pro validaci checkboxu
|
||||
|
function validateCheckbox() { |
||||
|
// Zkontroluj, že checkbox je zaškrtnutý (.checked)
|
||||
|
} |
||||
|
|
||||
|
// ========================================
|
||||
|
// HELPER FUNKCE
|
||||
|
// ========================================
|
||||
|
|
||||
|
// TODO: Funkce pro zobrazení chyby
|
||||
|
function showError(inputElement, message) { |
||||
|
// 1. Najdi parent element (.form-group)
|
||||
|
// 2. Přidej třídu "error"
|
||||
|
// 3. Odeber třídu "success"
|
||||
|
// 4. Najdi .error-message a nastav text
|
||||
|
// 5. Přidej shake animaci
|
||||
|
} |
||||
|
|
||||
|
// TODO: Funkce pro zobrazení success
|
||||
|
function showSuccess(inputElement) { |
||||
|
// 1. Najdi parent element
|
||||
|
// 2. Přidej třídu "success"
|
||||
|
// 3. Odeber třídu "error"
|
||||
|
// 4. Vyčisti error message
|
||||
|
} |
||||
|
|
||||
|
// ========================================
|
||||
|
// SUBMIT HANDLER
|
||||
|
// ========================================
|
||||
|
|
||||
|
function handleSubmit(e) { |
||||
|
e.preventDefault(); // Zabraň odeslání formuláře
|
||||
|
|
||||
|
// TODO: Zavolej všechny validační funkce
|
||||
|
// TODO: Zkontroluj, jestli jsou všechna pole validní
|
||||
|
// TODO: Pokud ano, zobraz success zprávu
|
||||
|
// TODO: Pokud ne, zobraz alert s chybou
|
||||
|
} |
||||
@ -0,0 +1,59 @@ |
|||||
|
/* AI 03 - Validace Formulářů */ |
||||
|
|
||||
|
* { |
||||
|
margin: 0; |
||||
|
padding: 0; |
||||
|
box-sizing: border-box; |
||||
|
} |
||||
|
|
||||
|
body { |
||||
|
font-family: Arial, Helvetica, sans-serif; |
||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
||||
|
min-height: 100vh; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
padding: 20px; |
||||
|
} |
||||
|
|
||||
|
.container { |
||||
|
background: white; |
||||
|
padding: 40px; |
||||
|
border-radius: 10px; |
||||
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); |
||||
|
width: 100%; |
||||
|
max-width: 500px; |
||||
|
} |
||||
|
|
||||
|
h1 { |
||||
|
text-align: center; |
||||
|
color: #333; |
||||
|
margin-bottom: 30px; |
||||
|
} |
||||
|
|
||||
|
/* TODO: Nastylujte .form-group */ |
||||
|
|
||||
|
|
||||
|
/* TODO: Nastylujte label */ |
||||
|
|
||||
|
|
||||
|
/* TODO: Nastylujte input */ |
||||
|
|
||||
|
|
||||
|
/* TODO: Přidejte třídy pro validaci */ |
||||
|
/* .form-group.success input - zelený border */ |
||||
|
|
||||
|
|
||||
|
/* .form-group.error input - červený border */ |
||||
|
|
||||
|
|
||||
|
/* TODO: Nastylujte .error-message */ |
||||
|
|
||||
|
|
||||
|
/* TODO: Nastylujte button */ |
||||
|
|
||||
|
|
||||
|
/* TODO: Přidejte shake animaci pro chyby */ |
||||
|
@keyframes shake { |
||||
|
/* Vytvořte shake efekt pomocí translateX */ |
||||
|
} |
||||
@ -0,0 +1,377 @@ |
|||||
|
# AI 04 - Komplexní Bootstrap Prep Projekt |
||||
|
|
||||
|
## 🎯 Cíl projektu |
||||
|
|
||||
|
**Finální komplexní projekt** kombinující **VŠECHNY naučené koncepty**. Po dokončení tohoto projektu budete plně připraveni na Bootstrap a budete rozumět, proč a jak framework funguje! |
||||
|
|
||||
|
## 📋 Co tento projekt procvičuje |
||||
|
|
||||
|
### HTML: |
||||
|
✅ Sémantické elementy (nav, main, section, article, aside, footer) |
||||
|
✅ Formuláře |
||||
|
✅ Strukturu moderní webové stránky |
||||
|
|
||||
|
### CSS: |
||||
|
✅ **Grid & Flexbox** - komplexní layouty |
||||
|
✅ **Positioning** - všechny typy (fixed, sticky, relative, absolute) |
||||
|
✅ **Media queries** - plná responzivita |
||||
|
✅ **CSS proměnné** - custom properties |
||||
|
✅ **Moderní jednotky** - rem, em, vh, vw |
||||
|
✅ **Gradienty, shadows, transitions** |
||||
|
✅ **Animations** - fadeIn, slideIn |
||||
|
✅ **Pokročilé selektory** - nth-child, ::before, ::after |
||||
|
|
||||
|
### JavaScript: |
||||
|
✅ **addEventListener** |
||||
|
✅ **DOM manipulace** |
||||
|
✅ **Form validation** |
||||
|
✅ **Event handling** |
||||
|
✅ **Scroll efekty** |
||||
|
✅ **Modals, accordions, toggles** |
||||
|
|
||||
|
## 📂 Soubory v projektu |
||||
|
|
||||
|
- **ZADANI.md** - kompletní zadání projektu |
||||
|
- **index.html** - startovací HTML (s TODO komentáři) |
||||
|
- **css/style.css** - hlavní CSS (s TODO komentáři) |
||||
|
- **css/animations.css** - CSS animace |
||||
|
- **js/main.js** - hlavní JavaScript |
||||
|
- **js/validation.js** - form validace |
||||
|
- **js/scroll.js** - scroll efekty |
||||
|
- **reseni/** - kompletní řešení se všemi soubory |
||||
|
|
||||
|
## 🚀 Co budete vytvářet |
||||
|
|
||||
|
**Responzivní Landing Page** pro fiktivní produkt (fitness app, e-learning, SaaS, atd.) |
||||
|
|
||||
|
### Struktura stránky: |
||||
|
|
||||
|
``` |
||||
|
┌─────────────────────────────────────┐ |
||||
|
│ FIXNÍ NAVIGACE (position: fixed) │ |
||||
|
├─────────────────────────────────────┤ |
||||
|
│ │ |
||||
|
│ HERO SEKCE (header) │ |
||||
|
│ Call-to-Action tlačítko │ |
||||
|
│ │ |
||||
|
├──────────────┬──────────────────────┤ |
||||
|
│ STICKY │ │ |
||||
|
│ ASIDE │ FEATURES (grid) │ |
||||
|
│ (boční) │ │ |
||||
|
│ │ PRICING CARDS │ |
||||
|
│ │ (grid) │ |
||||
|
│ │ │ |
||||
|
│ │ TESTIMONIALS │ |
||||
|
│ │ (flex) │ |
||||
|
│ │ │ |
||||
|
│ │ CONTACT FORM │ |
||||
|
│ │ (validation) │ |
||||
|
├──────────────┴──────────────────────┤ |
||||
|
│ FOOTER │ |
||||
|
└─────────────────────────────────────┘ |
||||
|
``` |
||||
|
|
||||
|
## 💡 Klíčové techniky |
||||
|
|
||||
|
### 1. CSS Grid Layout |
||||
|
|
||||
|
**Features sekce:** |
||||
|
```css |
||||
|
.features { |
||||
|
display: grid; |
||||
|
grid-template-columns: repeat(3, 1fr); /* 3 sloupce */ |
||||
|
gap: 2rem; |
||||
|
} |
||||
|
|
||||
|
@media (max-width: 768px) { |
||||
|
.features { |
||||
|
grid-template-columns: repeat(2, 1fr); /* 2 na tabletu */ |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@media (max-width: 480px) { |
||||
|
.features { |
||||
|
grid-template-columns: 1fr; /* 1 na mobilu */ |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### 2. CSS Proměnné (Custom Properties) |
||||
|
|
||||
|
```css |
||||
|
:root { |
||||
|
--primary-color: #667eea; |
||||
|
--secondary-color: #764ba2; |
||||
|
--text-color: #333; |
||||
|
--bg-color: #f8f9fa; |
||||
|
--spacing-unit: 1rem; |
||||
|
--border-radius: 8px; |
||||
|
} |
||||
|
|
||||
|
.button { |
||||
|
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); |
||||
|
padding: calc(var(--spacing-unit) * 0.75) calc(var(--spacing-unit) * 1.5); |
||||
|
border-radius: var(--border-radius); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### 3. Pricing Toggle (JavaScript) |
||||
|
|
||||
|
```javascript |
||||
|
const toggle = document.getElementById("pricing-toggle"); |
||||
|
const monthlyPrices = document.querySelectorAll(".monthly-price"); |
||||
|
const yearlyPrices = document.querySelectorAll(".yearly-price"); |
||||
|
|
||||
|
toggle.addEventListener("change", function() { |
||||
|
if (this.checked) { |
||||
|
// Zobraz roční ceny |
||||
|
monthlyPrices.forEach(el => el.style.display = "none"); |
||||
|
yearlyPrices.forEach(el => el.style.display = "block"); |
||||
|
} else { |
||||
|
// Zobraz měsíční ceny |
||||
|
monthlyPrices.forEach(el => el.style.display = "block"); |
||||
|
yearlyPrices.forEach(el => el.style.display = "none"); |
||||
|
} |
||||
|
}); |
||||
|
``` |
||||
|
|
||||
|
### 4. Smooth Scroll k sekcím |
||||
|
|
||||
|
```javascript |
||||
|
document.querySelectorAll('a[href^="#"]').forEach(anchor => { |
||||
|
anchor.addEventListener("click", function(e) { |
||||
|
e.preventDefault(); |
||||
|
const target = document.querySelector(this.getAttribute("href")); |
||||
|
target.scrollIntoView({ |
||||
|
behavior: "smooth", |
||||
|
block: "start" |
||||
|
}); |
||||
|
}); |
||||
|
}); |
||||
|
``` |
||||
|
|
||||
|
### 5. Scroll to Top tlačítko |
||||
|
|
||||
|
```javascript |
||||
|
const scrollTopBtn = document.getElementById("scroll-top"); |
||||
|
|
||||
|
window.addEventListener("scroll", function() { |
||||
|
if (window.pageYOffset > 300) { |
||||
|
scrollTopBtn.classList.add("show"); |
||||
|
} else { |
||||
|
scrollTopBtn.classList.remove("show"); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
scrollTopBtn.addEventListener("click", function() { |
||||
|
window.scrollTo({ |
||||
|
top: 0, |
||||
|
behavior: "smooth" |
||||
|
}); |
||||
|
}); |
||||
|
``` |
||||
|
|
||||
|
### 6. Modal okno |
||||
|
|
||||
|
```javascript |
||||
|
const modal = document.getElementById("myModal"); |
||||
|
const openBtn = document.getElementById("openModal"); |
||||
|
const closeBtn = document.querySelector(".close"); |
||||
|
|
||||
|
openBtn.addEventListener("click", function() { |
||||
|
modal.style.display = "block"; |
||||
|
document.body.style.overflow = "hidden"; // zakáže scrollování |
||||
|
}); |
||||
|
|
||||
|
closeBtn.addEventListener("click", function() { |
||||
|
modal.style.display = "none"; |
||||
|
document.body.style.overflow = "auto"; |
||||
|
}); |
||||
|
|
||||
|
// Zavření kliknutím mimo modal |
||||
|
window.addEventListener("click", function(e) { |
||||
|
if (e.target === modal) { |
||||
|
modal.style.display = "none"; |
||||
|
document.body.style.overflow = "auto"; |
||||
|
} |
||||
|
}); |
||||
|
``` |
||||
|
|
||||
|
### 7. FAQ Accordion |
||||
|
|
||||
|
```javascript |
||||
|
const accordionHeaders = document.querySelectorAll(".accordion-header"); |
||||
|
|
||||
|
accordionHeaders.forEach(header => { |
||||
|
header.addEventListener("click", function() { |
||||
|
const content = this.nextElementSibling; |
||||
|
const isActive = this.classList.contains("active"); |
||||
|
|
||||
|
// Zavři všechny ostatní |
||||
|
accordionHeaders.forEach(h => { |
||||
|
h.classList.remove("active"); |
||||
|
h.nextElementSibling.style.maxHeight = null; |
||||
|
}); |
||||
|
|
||||
|
// Toggle aktuální |
||||
|
if (!isActive) { |
||||
|
this.classList.add("active"); |
||||
|
content.style.maxHeight = content.scrollHeight + "px"; |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
``` |
||||
|
|
||||
|
### 8. Změna barvy navigace při scrollování |
||||
|
|
||||
|
```javascript |
||||
|
const navbar = document.querySelector("nav"); |
||||
|
|
||||
|
window.addEventListener("scroll", function() { |
||||
|
if (window.pageYOffset > 100) { |
||||
|
navbar.classList.add("scrolled"); |
||||
|
} else { |
||||
|
navbar.classList.remove("scrolled"); |
||||
|
} |
||||
|
}); |
||||
|
``` |
||||
|
|
||||
|
```css |
||||
|
nav { |
||||
|
background-color: transparent; |
||||
|
transition: background-color 0.3s ease; |
||||
|
} |
||||
|
|
||||
|
nav.scrolled { |
||||
|
background-color: rgba(255, 255, 255, 0.95); |
||||
|
box-shadow: 0 2px 10px rgba(0,0,0,0.1); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## 🎨 CSS Animations |
||||
|
|
||||
|
```css |
||||
|
/* Fade In */ |
||||
|
@keyframes fadeIn { |
||||
|
from { |
||||
|
opacity: 0; |
||||
|
transform: translateY(20px); |
||||
|
} |
||||
|
to { |
||||
|
opacity: 1; |
||||
|
transform: translateY(0); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.fade-in { |
||||
|
animation: fadeIn 0.6s ease-out; |
||||
|
} |
||||
|
|
||||
|
/* Slide In */ |
||||
|
@keyframes slideIn { |
||||
|
from { |
||||
|
transform: translateX(-100%); |
||||
|
} |
||||
|
to { |
||||
|
transform: translateX(0); |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## ⚠️ Časté chyby a řešení |
||||
|
|
||||
|
❌ **Fixed navigace překrývá obsah** |
||||
|
```css |
||||
|
body { |
||||
|
padding-top: 80px; /* výška navigace */ |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
❌ **Smooth scroll nefunguje** |
||||
|
```css |
||||
|
html { |
||||
|
scroll-behavior: smooth; /* CSS alternativa */ |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
❌ **Modal scrolluje pozadí** |
||||
|
```javascript |
||||
|
// Při otevření modalu |
||||
|
document.body.style.overflow = "hidden"; |
||||
|
|
||||
|
// Při zavření |
||||
|
document.body.style.overflow = "auto"; |
||||
|
``` |
||||
|
|
||||
|
## 🎓 Hodnocení (30 bodů) |
||||
|
|
||||
|
- **HTML struktura** (5b) - sémantické elementy, správná struktura |
||||
|
- **CSS Grid & Flexbox** (6b) - grid pro features/pricing, flex pro navigaci |
||||
|
- **Positioning** (4b) - fixed nav, sticky aside, absolute badge |
||||
|
- **JavaScript interaktivita** (6b) - validace, scroll, modal, accordion |
||||
|
- **Responzivita** (4b) - funguje na mobilu, tabletu, desktopu |
||||
|
- **Design & UX** (3b) - hezký design, dobré barvy, transitions |
||||
|
- **Clean code** (2b) - čitelný, okomentovaný kód |
||||
|
|
||||
|
## 🔗 Design inspirace |
||||
|
|
||||
|
- [Awwwards](https://www.awwwards.com/) |
||||
|
- [Dribbble](https://dribbble.com/) |
||||
|
- [Land-book](https://land-book.com/) |
||||
|
|
||||
|
## ⏱️ Odhadovaný čas |
||||
|
|
||||
|
**4-6 hodin** (pracujte postupně, ne vše najednou!) |
||||
|
|
||||
|
## 💪 Bonus úkoly (nepovinné) |
||||
|
|
||||
|
1. **Counter animace** - číselné statistiky se animují od 0 |
||||
|
2. **Lazy loading obrázků** - obrázky se načtou až při scrollování |
||||
|
3. **Dark mode toggle** - přepínač světlý/tmavý režim |
||||
|
4. **Carousel/Slider** - pro testimonials (bez knihoven!) |
||||
|
5. **Newsletter signup** - s fake AJAX requestem |
||||
|
6. **AOS (Animate On Scroll)** - prvky plynule přijíždějí při scrollování |
||||
|
|
||||
|
## 🎯 Po dokončení projektu |
||||
|
|
||||
|
### Co budete rozumět při učení Bootstrapu: |
||||
|
|
||||
|
✅ **Grid systém** - víte, jak funguje pod kapotou |
||||
|
✅ **Utility třídy** - `.m-2`, `.p-3` → víte, co dělají |
||||
|
✅ **Komponenty** - modals, accordions → umíte je vytvořit sami |
||||
|
✅ **JavaScript plugins** - rozumíte, jak fungují |
||||
|
✅ **Responzivita** - mobile-first přístup dává smysl |
||||
|
|
||||
|
### Bootstrap vám ušetří čas: |
||||
|
|
||||
|
```html |
||||
|
<!-- VÁŠE ŘEŠENÍ (40 řádků CSS) --> |
||||
|
<div class="pricing-card"> |
||||
|
<h3>Basic</h3> |
||||
|
<p class="price">$9/mo</p> |
||||
|
<button class="btn">Subscribe</button> |
||||
|
</div> |
||||
|
|
||||
|
<!-- BOOTSTRAP (využívá hotové třídy) --> |
||||
|
<div class="card text-center"> |
||||
|
<div class="card-body"> |
||||
|
<h3 class="card-title">Basic</h3> |
||||
|
<p class="display-4">$9/mo</p> |
||||
|
<button class="btn btn-primary">Subscribe</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
``` |
||||
|
|
||||
|
**Ale díky tomuto projektu víte, co se děje pod kapotou!** 🎉 |
||||
|
|
||||
|
## 🏆 Gratulace! |
||||
|
|
||||
|
Po dokončení všech AI projektů (01-04) jste připraveni na Bootstrap! |
||||
|
|
||||
|
Jste schopni: |
||||
|
- ✅ Vytvořit kompletní responzivní web bez frameworku |
||||
|
- ✅ Rozumět, jak frameworky fungují interně |
||||
|
- ✅ Přizpůsobit Bootstrap vlastním potřebám |
||||
|
- ✅ Debugovat problémy v Bootstrapu |
||||
|
|
||||
|
➡️ **Další krok:** Lekce 13 - Úvod do Bootstrapu |
||||
@ -0,0 +1,173 @@ |
|||||
|
# Příklad řešení - AI 04 |
||||
|
|
||||
|
Toto je **zjednodušený příklad** řešení. Vaše implementace by měla být completnější! |
||||
|
|
||||
|
## Struktura HTML (příklad hero sekce) |
||||
|
|
||||
|
```html |
||||
|
<header id="hero"> |
||||
|
<div class="hero-content"> |
||||
|
<h1 class="hero-title">FitPro</h1> |
||||
|
<p class="hero-subtitle">Vaše cesta k lepší kondici začíná zde</p> |
||||
|
<a href="#contact" class="btn btn-primary">Začít zdarma</a> |
||||
|
</div> |
||||
|
</header> |
||||
|
``` |
||||
|
|
||||
|
## CSS Grid příklad (features) |
||||
|
|
||||
|
```css |
||||
|
.features-grid { |
||||
|
display: grid; |
||||
|
grid-template-columns: 1fr; /* Mobil - 1 sloupec */ |
||||
|
gap: 2rem; |
||||
|
padding: 4rem 2rem; |
||||
|
} |
||||
|
|
||||
|
@media (min-width: 768px) { |
||||
|
.features-grid { |
||||
|
grid-template-columns: repeat(2, 1fr); /* Tablet - 2 sloupce */ |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@media (min-width: 1024px) { |
||||
|
.features-grid { |
||||
|
grid-template-columns: repeat(3, 1fr); /* Desktop - 3 sloupce */ |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## JavaScript smooth scroll příklad |
||||
|
|
||||
|
```javascript |
||||
|
document.querySelectorAll('a[href^="#"]').forEach(anchor => { |
||||
|
anchor.addEventListener('click', function (e) { |
||||
|
e.preventDefault(); |
||||
|
const target = document.querySelector(this.getAttribute('href')); |
||||
|
target.scrollIntoView({ |
||||
|
behavior: 'smooth', |
||||
|
block: 'start' |
||||
|
}); |
||||
|
}); |
||||
|
}); |
||||
|
``` |
||||
|
|
||||
|
## Positioning příklad (badge) |
||||
|
|
||||
|
```css |
||||
|
.pricing-card { |
||||
|
position: relative; /* Vytvoří kontext pro absolute */ |
||||
|
} |
||||
|
|
||||
|
.badge { |
||||
|
position: absolute; |
||||
|
top: -10px; |
||||
|
right: -10px; |
||||
|
background: var(--danger-color); |
||||
|
color: white; |
||||
|
padding: 0.5rem 1rem; |
||||
|
border-radius: 20px; |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## CSS Proměnné použití |
||||
|
|
||||
|
```css |
||||
|
.btn-primary { |
||||
|
background: linear-gradient( |
||||
|
135deg, |
||||
|
var(--primary-color), |
||||
|
var(--secondary-color) |
||||
|
); |
||||
|
color: var(--white); |
||||
|
padding: var(--spacing-sm) var(--spacing-md); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## Tips & Tricks |
||||
|
|
||||
|
### 1. Responzivní typografie s clamp() |
||||
|
```css |
||||
|
h1 { |
||||
|
font-size: clamp(2rem, 5vw, 4rem); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### 2. Aspect ratio pro obrázky |
||||
|
```css |
||||
|
.image-container { |
||||
|
aspect-ratio: 16/9; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### 3. Flexbox centering |
||||
|
```css |
||||
|
.centered { |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
min-height: 100vh; |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### 4. Grid auto-fit |
||||
|
```css |
||||
|
.grid { |
||||
|
display: grid; |
||||
|
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); |
||||
|
gap: 2rem; |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## Checklist před odevzdáním |
||||
|
|
||||
|
- [ ] Všechny sémantické HTML5 elementy použity |
||||
|
- [ ] CSS Grid pro alespoň 2 sekce |
||||
|
- [ ] Flexbox pro alespoň 2 sekce |
||||
|
- [ ] Všechny 4 typy positioning použity |
||||
|
- [ ] Minimálně 3 media queries |
||||
|
- [ ] Formulář s funkční validací |
||||
|
- [ ] Smooth scroll implementován |
||||
|
- [ ] Scroll to top button funguje |
||||
|
- [ ] Použity CSS proměnné |
||||
|
- [ ] Použity rem/em jednotky (ne jen px) |
||||
|
- [ ] Hover efekty na interaktivních prvcích |
||||
|
- [ ] Animace při načtení stránky |
||||
|
- [ ] Responzivní na všech zařízeních |
||||
|
- [ ] Kód je komentovaný |
||||
|
- [ ] Žádné console errory |
||||
|
|
||||
|
## Hodnocení |
||||
|
|
||||
|
### Základní požadavky (70 bodů) |
||||
|
- HTML struktura (10 bodů) |
||||
|
- CSS Grid (10 bodů) |
||||
|
- Flexbox (10 bodů) |
||||
|
- Positioning (10 bodů) |
||||
|
- Media queries (10 bodů) |
||||
|
- JavaScript validace (10 bodů) |
||||
|
- Scroll efekty (10 bodů) |
||||
|
|
||||
|
### Pokročilé funkce (20 bodů) |
||||
|
- CSS proměnné (5 bodů) |
||||
|
- Animace (5 bodů) |
||||
|
- Pokročilé selektory (5 bodů) |
||||
|
- Clean code (5 bodů) |
||||
|
|
||||
|
### Bonus (10 bodů) |
||||
|
- Extra funkce (modal, accordion, atd.) |
||||
|
- Výjimečný design |
||||
|
- Originální nápady |
||||
|
|
||||
|
**Celkem: 100 bodů (+10 bonus)** |
||||
|
|
||||
|
## Užitečné zdroje |
||||
|
|
||||
|
- MDN Web Docs: https://developer.mozilla.org/ |
||||
|
- CSS Tricks: https://css-tricks.com/ |
||||
|
- Can I Use: https://caniuse.com/ |
||||
|
- Coolors (palety barev): https://coolors.co/ |
||||
|
- Google Fonts: https://fonts.google.com/ |
||||
|
|
||||
|
Hodně štěstí! 🚀 |
||||
@ -0,0 +1,166 @@ |
|||||
|
# AI 04 - Komplexní Bootstrap Prep Projekt |
||||
|
|
||||
|
## 🎯 Cíl projektu |
||||
|
Komplexní projekt kombinující VŠECHNY naučené koncepty. Po dokončení tohoto projektu budete připraveni na Bootstrap! |
||||
|
|
||||
|
## 📝 Zadání |
||||
|
|
||||
|
Vytvořte **Responzivní Landing Page pro fiktivní produkt** (např. fitness aplikace, e-learning platforma, nebo jakýkoliv produkt dle vlastního výběru). |
||||
|
|
||||
|
### Požadavky na projekt: |
||||
|
|
||||
|
## 1️⃣ HTML - Sémantická struktura |
||||
|
|
||||
|
Použijte tyto sémantické elementy: |
||||
|
- `<nav>` - Fixní navigace s logem a menu |
||||
|
- `<header>` - Hero sekce s call-to-action |
||||
|
- `<main>` obsahující: |
||||
|
- `<section id="features">` - 3-6 features (ikony + text) |
||||
|
- `<section id="pricing">` - 3 cenové karty |
||||
|
- `<section id="testimonials">` - 3 recenze zákazníků |
||||
|
- `<section id="contact">` - Kontaktní formulář |
||||
|
- `<aside>` - Boční panel s quick links (sticky) |
||||
|
- `<footer>` - Zápatí s odkazy |
||||
|
|
||||
|
## 2️⃣ CSS - Layout & Design |
||||
|
|
||||
|
### Grid Layout: |
||||
|
- Features sekce: CSS Grid, 3 sloupce (desktop), 2 (tablet), 1 (mobil) |
||||
|
- Pricing karty: CSS Grid, 3 sloupce s gap 2rem |
||||
|
|
||||
|
### Flexbox: |
||||
|
- Navigace: flex s space-between |
||||
|
- Footer: flex s justify-content: space-around |
||||
|
- Testimonials: flex s flex-wrap |
||||
|
|
||||
|
### Positioning: |
||||
|
- `position: fixed` - Navigace |
||||
|
- `position: sticky` - Aside |
||||
|
- `position: absolute` - Badge "NEJLEPŠÍ VOLBA!" na jedné cenové kartě |
||||
|
- `position: relative` - Kontejner pro badge |
||||
|
|
||||
|
### Responzivita: |
||||
|
- Mobile first approach! |
||||
|
- Breakpoints: 480px, 768px, 1024px |
||||
|
- Media queries pro všechny sekce |
||||
|
|
||||
|
### Design: |
||||
|
- Použijte CSS proměnné (custom properties): |
||||
|
```css |
||||
|
:root { |
||||
|
--primary-color: #667eea; |
||||
|
--secondary-color: #764ba2; |
||||
|
--text-color: #333; |
||||
|
--bg-color: #f8f9fa; |
||||
|
} |
||||
|
``` |
||||
|
- Jednotky: rem, em, vh, vw (ne jen px!) |
||||
|
- Gradienty na pozadí |
||||
|
- Box shadows pro depth |
||||
|
- Hover efekty s transitions |
||||
|
- Animations (fadeIn, slideIn) |
||||
|
|
||||
|
## 3️⃣ JavaScript - Interaktivita |
||||
|
|
||||
|
### Formulář s validací: |
||||
|
- Jméno (min 3 znaky) |
||||
|
- Email (validní formát) |
||||
|
- Zpráva (min 10 znaků) |
||||
|
- Real-time validace pomocí `addEventListener` |
||||
|
- Visual feedback (červená/zelená) |
||||
|
|
||||
|
### Scroll efekty: |
||||
|
- Smooth scroll k sekcím při kliknutí na menu |
||||
|
- "Scroll to top" tlačítko (zobrazí se po scrollování) |
||||
|
- Změna barvy navigace při scrollování |
||||
|
|
||||
|
### Interaktivní features: |
||||
|
- Pricing toggle: měsíční/roční ceny (switch button) |
||||
|
- FAQ accordion (kliknutí → otevře/zavře odpověď) |
||||
|
- Modal okno pro "Více informací" tlačítko |
||||
|
|
||||
|
### Bonus: |
||||
|
- Counter animace (číselné statistiky se animují od 0) |
||||
|
- Lazy loading obrázků |
||||
|
- Dark mode toggle |
||||
|
|
||||
|
## 4️⃣ Pokročilé CSS selektory |
||||
|
|
||||
|
Použijte minimálně: |
||||
|
- `:nth-child()` - styling každé druhé karty |
||||
|
- `:hover`, `:focus`, `:active` |
||||
|
- `:first-child`, `:last-child` |
||||
|
- `::before`, `::after` - pro dekorativní elementy |
||||
|
- Attribute selectors - `[href^="http"]` pro externí odkazy |
||||
|
|
||||
|
## 📐 Struktura projektu |
||||
|
|
||||
|
``` |
||||
|
ai_04_prep_bootstrap/ |
||||
|
├── index.html |
||||
|
├── css/ |
||||
|
│ ├── style.css |
||||
|
│ └── animations.css |
||||
|
├── js/ |
||||
|
│ ├── main.js |
||||
|
│ ├── validation.js |
||||
|
│ └── scroll.js |
||||
|
└── images/ |
||||
|
└── (vaše obrázky) |
||||
|
``` |
||||
|
|
||||
|
## 💡 Co tento projekt procvičuje: |
||||
|
|
||||
|
### HTML: |
||||
|
✓ Sémantické elementy |
||||
|
✓ Formuláře |
||||
|
✓ Strukturu moderní webové stránky |
||||
|
|
||||
|
### CSS: |
||||
|
✓ Grid & Flexbox |
||||
|
✓ Positioning (všechny typy) |
||||
|
✓ Media queries |
||||
|
✓ CSS proměnné |
||||
|
✓ Jednotky (rem, em, vh, vw) |
||||
|
✓ Gradienty, shadows, transitions |
||||
|
✓ Animations |
||||
|
✓ Pokročilé selektory |
||||
|
|
||||
|
### JavaScript: |
||||
|
✓ addEventListener |
||||
|
✓ DOM manipulace |
||||
|
✓ Form validation |
||||
|
✓ Event handling |
||||
|
✓ Podmínky a cykly |
||||
|
✓ Funkce |
||||
|
|
||||
|
## 🎨 Design inspirace: |
||||
|
- https://www.awwwards.com/ |
||||
|
- https://dribbble.com/ |
||||
|
- Bootstrap examples (ale NEPOUŽÍVEJTE Bootstrap!) |
||||
|
|
||||
|
## ⏱️ Odhadovaný čas: 4-6 hodin |
||||
|
|
||||
|
## 🏆 Bonus úkoly (nepovinné): |
||||
|
1. Přidejte animaci AOS (Animate On Scroll) - při scrollování prvky plynule přijíždějí |
||||
|
2. Implementujte newsletter signup s AJAX (fake API endpoint) |
||||
|
3. Vytvořte carousel/slider pro testimonials (bez knihoven!) |
||||
|
4. Přidejte Easter egg (např. Konami kód) |
||||
|
5. PWA features (manifest.json, service worker) |
||||
|
|
||||
|
## 📚 Co se naučíte pro Bootstrap: |
||||
|
Po dokončení tohoto projektu budete rozumět: |
||||
|
- Jak funguje responzivní grid systém → Bootstrap grid |
||||
|
- Jak fungují utility třídy → Bootstrap utilities |
||||
|
- Jak se stylují komponenty → Bootstrap components |
||||
|
- Jak funguje JavaScript interaktivita → Bootstrap JS |
||||
|
- Mobile-first přístup → Bootstrap philosophy |
||||
|
|
||||
|
## 🚀 Po dokončení: |
||||
|
Gratulujeme! Jste připraveni na Bootstrap. Bootstrap vám nyní ušetří čas tím, že poskytuje: |
||||
|
- Předpřipravený grid systém (místo vašeho CSS Grid/Flexbox) |
||||
|
- Předpřipravené komponenty (tlačítka, karty, navigace) |
||||
|
- Předpřipravené utility třídy (margin, padding, colors) |
||||
|
- Responzivitu "out of the box" |
||||
|
|
||||
|
Ale díky tomuto projektu rozumíte, co se děje "pod kapotou"! |
||||
@ -0,0 +1,42 @@ |
|||||
|
/* AI 04 - CSS Animace */ |
||||
|
|
||||
|
/* ============================================ |
||||
|
KEYFRAME ANIMATIONS |
||||
|
============================================ */ |
||||
|
|
||||
|
/* TODO: FadeIn animace */ |
||||
|
@keyframes fadeIn { |
||||
|
/* from { opacity: 0; } */ |
||||
|
/* to { opacity: 1; } */ |
||||
|
} |
||||
|
|
||||
|
/* TODO: SlideIn animace */ |
||||
|
@keyframes slideInUp { |
||||
|
/* from { transform: translateY(50px); opacity: 0; } */ |
||||
|
/* to { transform: translateY(0); opacity: 1; } */ |
||||
|
} |
||||
|
|
||||
|
/* TODO: Bounce animace */ |
||||
|
@keyframes bounce { |
||||
|
/* 0%, 20%, 50%, 80%, 100% { transform: translateY(0); } */ |
||||
|
/* 40% { transform: translateY(-20px); } */ |
||||
|
/* 60% { transform: translateY(-10px); } */ |
||||
|
} |
||||
|
|
||||
|
/* TODO: Pulse animace (pro tlačítka) */ |
||||
|
@keyframes pulse { |
||||
|
/* 0% { transform: scale(1); } */ |
||||
|
/* 50% { transform: scale(1.05); } */ |
||||
|
/* 100% { transform: scale(1); } */ |
||||
|
} |
||||
|
|
||||
|
/* TODO: Shake animace (pro chyby) */ |
||||
|
@keyframes shake { |
||||
|
|
||||
|
} |
||||
|
|
||||
|
/* ============================================ |
||||
|
UTILITY TŘÍDY PRO ANIMACE |
||||
|
============================================ */ |
||||
|
|
||||
|
/* TODO: Přidejte třídy jako .fade-in, .slide-in, atd. */ |
||||
@ -0,0 +1,131 @@ |
|||||
|
/* AI 04 - Komplexní Bootstrap Prep Projekt */ |
||||
|
|
||||
|
/* ============================================ |
||||
|
CSS PROMĚNNÉ (Custom Properties) |
||||
|
============================================ */ |
||||
|
:root { |
||||
|
/* Barvy */ |
||||
|
--primary-color: #667eea; |
||||
|
--secondary-color: #764ba2; |
||||
|
--success-color: #2ecc71; |
||||
|
--danger-color: #e74c3c; |
||||
|
--text-color: #333; |
||||
|
--text-light: #666; |
||||
|
--bg-color: #f8f9fa; |
||||
|
--white: #ffffff; |
||||
|
|
||||
|
/* Spacing */ |
||||
|
--spacing-xs: 0.5rem; |
||||
|
--spacing-sm: 1rem; |
||||
|
--spacing-md: 2rem; |
||||
|
--spacing-lg: 4rem; |
||||
|
--spacing-xl: 6rem; |
||||
|
|
||||
|
/* Typography */ |
||||
|
--font-primary: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
||||
|
--font-size-base: 1rem; |
||||
|
--font-size-lg: 1.25rem; |
||||
|
--font-size-xl: 2rem; |
||||
|
--font-size-xxl: 3rem; |
||||
|
|
||||
|
/* Breakpoints (pro JavaScript) */ |
||||
|
--breakpoint-sm: 480px; |
||||
|
--breakpoint-md: 768px; |
||||
|
--breakpoint-lg: 1024px; |
||||
|
} |
||||
|
|
||||
|
/* ============================================ |
||||
|
RESET & BASE STYLES |
||||
|
============================================ */ |
||||
|
/* TODO: Přidejte reset styly */ |
||||
|
|
||||
|
|
||||
|
/* ============================================ |
||||
|
NAVIGACE (position: fixed) |
||||
|
============================================ */ |
||||
|
/* TODO: Vytvořte fixní navigaci nahoře */ |
||||
|
|
||||
|
|
||||
|
/* ============================================ |
||||
|
HEADER / HERO SEKCE |
||||
|
============================================ */ |
||||
|
/* TODO: Hero sekce s gradientem, vycentrovaný text */ |
||||
|
|
||||
|
|
||||
|
/* ============================================ |
||||
|
FEATURES SEKCE (CSS GRID) |
||||
|
============================================ */ |
||||
|
/* TODO: Grid layout pro features */ |
||||
|
/* Desktop: 3 sloupce */ |
||||
|
/* Tablet: 2 sloupce */ |
||||
|
/* Mobil: 1 sloupec */ |
||||
|
|
||||
|
|
||||
|
/* ============================================ |
||||
|
PRICING SEKCE (CSS GRID + POSITIONING) |
||||
|
============================================ */ |
||||
|
/* TODO: Grid layout pro cenové karty */ |
||||
|
/* Použijte position: relative pro kontejner */ |
||||
|
/* Použijte position: absolute pro "NEJLEPŠÍ VOLBA!" badge */ |
||||
|
|
||||
|
|
||||
|
/* ============================================ |
||||
|
TESTIMONIALS SEKCE (FLEXBOX) |
||||
|
============================================ */ |
||||
|
/* TODO: Flex layout s flex-wrap */ |
||||
|
|
||||
|
|
||||
|
/* ============================================ |
||||
|
CONTACT SEKCE (FORMULÁŘ) |
||||
|
============================================ */ |
||||
|
/* TODO: Styling formuláře s validačními stavy */ |
||||
|
|
||||
|
|
||||
|
/* ============================================ |
||||
|
ASIDE (position: sticky) |
||||
|
============================================ */ |
||||
|
/* TODO: Sticky sidebar */ |
||||
|
|
||||
|
|
||||
|
/* ============================================ |
||||
|
FOOTER |
||||
|
============================================ */ |
||||
|
/* TODO: Footer s flexboxem */ |
||||
|
|
||||
|
|
||||
|
/* ============================================ |
||||
|
SCROLL TO TOP BUTTON |
||||
|
============================================ */ |
||||
|
/* TODO: Button pro scroll nahoru (position: fixed) */ |
||||
|
|
||||
|
|
||||
|
/* ============================================ |
||||
|
POKROČILÉ SELEKTORY |
||||
|
============================================ */ |
||||
|
|
||||
|
/* TODO: Použijte nth-child pro alternativní styling */ |
||||
|
/* .feature-card:nth-child(even) {} */ |
||||
|
|
||||
|
|
||||
|
/* TODO: Použijte ::before a ::after pro dekoraci */ |
||||
|
|
||||
|
|
||||
|
/* TODO: Styling externích odkazů */ |
||||
|
/* a[href^="http"] {} */ |
||||
|
|
||||
|
|
||||
|
/* ============================================ |
||||
|
MEDIA QUERIES - RESPONZIVITA |
||||
|
============================================ */ |
||||
|
|
||||
|
/* Mobile First! */ |
||||
|
|
||||
|
/* Tablet - 768px */ |
||||
|
@media screen and (min-width: 768px) { |
||||
|
/* TODO */ |
||||
|
} |
||||
|
|
||||
|
/* Desktop - 1024px */ |
||||
|
@media screen and (min-width: 1024px) { |
||||
|
/* TODO */ |
||||
|
} |
||||
@ -0,0 +1,61 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang="cs"> |
||||
|
<head> |
||||
|
<meta charset="UTF-8"> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
|
<meta name="description" content="FitPro - Vaše cesta k lepší kondici"> |
||||
|
<title>FitPro - Fitness Aplikace</title> |
||||
|
<link rel="stylesheet" href="css/style.css"> |
||||
|
<link rel="stylesheet" href="css/animations.css"> |
||||
|
</head> |
||||
|
<body> |
||||
|
<!-- TODO: Začněte zde vytvářet landing page --> |
||||
|
|
||||
|
<!-- 1. NAVIGACE (position: fixed) --> |
||||
|
<!-- nav#navbar --> |
||||
|
|
||||
|
|
||||
|
<!-- 2. HEADER / HERO SEKCE --> |
||||
|
<!-- header#hero --> |
||||
|
|
||||
|
|
||||
|
<!-- 3. MAIN - Hlavní obsah --> |
||||
|
<!-- <main> --> |
||||
|
|
||||
|
<!-- 3a. FEATURES SEKCE (CSS Grid) --> |
||||
|
<!-- section#features --> |
||||
|
|
||||
|
|
||||
|
<!-- 3b. PRICING SEKCE (CSS Grid) --> |
||||
|
<!-- section#pricing --> |
||||
|
|
||||
|
|
||||
|
<!-- 3c. TESTIMONIALS SEKCE (Flexbox) --> |
||||
|
<!-- section#testimonials --> |
||||
|
|
||||
|
|
||||
|
<!-- 3d. KONTAKT SEKCE (Formulář s validací) --> |
||||
|
<!-- section#contact --> |
||||
|
|
||||
|
|
||||
|
<!-- </main> --> |
||||
|
|
||||
|
|
||||
|
<!-- 4. ASIDE (position: sticky) --> |
||||
|
<!-- aside#quicklinks --> |
||||
|
|
||||
|
|
||||
|
<!-- 5. FOOTER --> |
||||
|
<!-- footer --> |
||||
|
|
||||
|
|
||||
|
<!-- 6. SCROLL TO TOP BUTTON (skrytý, zobrazí se při scrollování) --> |
||||
|
<!-- <button id="scrollToTop" class="scroll-top-btn">↑</button> --> |
||||
|
|
||||
|
|
||||
|
<!-- JavaScript soubory --> |
||||
|
<script src="js/validation.js"></script> |
||||
|
<script src="js/scroll.js"></script> |
||||
|
<script src="js/main.js"></script> |
||||
|
</body> |
||||
|
</html> |
||||
@ -0,0 +1,37 @@ |
|||||
|
// AI 04 - Main JavaScript
|
||||
|
|
||||
|
console.log("Main.js načten"); |
||||
|
|
||||
|
// ========================================
|
||||
|
// PRICING TOGGLE (měsíční/roční)
|
||||
|
// ========================================
|
||||
|
|
||||
|
// TODO: Implementujte přepínání mezi měsíčními a ročními cenami
|
||||
|
|
||||
|
|
||||
|
// ========================================
|
||||
|
// FAQ ACCORDION
|
||||
|
// ========================================
|
||||
|
|
||||
|
// TODO: Implementujte accordion (kliknutí otevře/zavře odpověď)
|
||||
|
|
||||
|
|
||||
|
// ========================================
|
||||
|
// MODAL OKNO
|
||||
|
// ========================================
|
||||
|
|
||||
|
// TODO: Implementujte modal okno pro "Více informací"
|
||||
|
|
||||
|
|
||||
|
// ========================================
|
||||
|
// COUNTER ANIMACE
|
||||
|
// ========================================
|
||||
|
|
||||
|
// TODO: Animujte čísla od 0 do cílové hodnoty
|
||||
|
|
||||
|
|
||||
|
// ========================================
|
||||
|
// DARK MODE TOGGLE (BONUS)
|
||||
|
// ========================================
|
||||
|
|
||||
|
// TODO: Implementujte přepínání mezi světlým a tmavým režimem
|
||||
@ -0,0 +1,36 @@ |
|||||
|
// AI 04 - Scroll efekty
|
||||
|
|
||||
|
console.log("Scroll.js načten"); |
||||
|
|
||||
|
// ========================================
|
||||
|
// SMOOTH SCROLL K SEKCÍM
|
||||
|
// ========================================
|
||||
|
|
||||
|
// TODO: Implementujte smooth scroll při kliknutí na menu linky
|
||||
|
// document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
||||
|
// anchor.addEventListener('click', function (e) {
|
||||
|
// e.preventDefault();
|
||||
|
// // ... smooth scroll
|
||||
|
// });
|
||||
|
// });
|
||||
|
|
||||
|
|
||||
|
// ========================================
|
||||
|
// SCROLL TO TOP BUTTON
|
||||
|
// ========================================
|
||||
|
|
||||
|
// TODO: Zobrazit/skrýt "Scroll to top" tlačítko při scrollování
|
||||
|
|
||||
|
|
||||
|
// ========================================
|
||||
|
// ZMĚNA NAVIGACE PŘI SCROLLOVÁNÍ
|
||||
|
// ========================================
|
||||
|
|
||||
|
// TODO: Změnit barvu/styl navigace když uživatel scrolluje dolů
|
||||
|
|
||||
|
|
||||
|
// ========================================
|
||||
|
// ANIMATE ON SCROLL (BONUS)
|
||||
|
// ========================================
|
||||
|
|
||||
|
// TODO: Přidejte třídu "visible" elementům když se dostanou do viewportu
|
||||
@ -0,0 +1,48 @@ |
|||||
|
// AI 04 - Validace formuláře
|
||||
|
|
||||
|
console.log("Validation.js načten"); |
||||
|
|
||||
|
// TODO: Implementujte validaci kontaktního formuláře
|
||||
|
// Požadavky:
|
||||
|
// - Jméno: min 3 znaky
|
||||
|
// - Email: validní formát
|
||||
|
// - Zpráva: min 10 znaků
|
||||
|
// - Real-time validace pomocí addEventListener
|
||||
|
// - Visual feedback (červená/zelená border)
|
||||
|
|
||||
|
// ========================================
|
||||
|
// ZÍSKÁNÍ REFERENCÍ NA ELEMENTY
|
||||
|
// ========================================
|
||||
|
|
||||
|
// TODO: const contactForm = document.getElementById("contactForm");
|
||||
|
|
||||
|
|
||||
|
// ========================================
|
||||
|
// EVENT LISTENERS
|
||||
|
// ========================================
|
||||
|
|
||||
|
// TODO: Přidejte event listenery pro input eventy
|
||||
|
|
||||
|
|
||||
|
// ========================================
|
||||
|
// VALIDAČNÍ FUNKCE
|
||||
|
// ========================================
|
||||
|
|
||||
|
// TODO: function validateContactName() {}
|
||||
|
// TODO: function validateContactEmail() {}
|
||||
|
// TODO: function validateContactMessage() {}
|
||||
|
|
||||
|
|
||||
|
// ========================================
|
||||
|
// HELPER FUNKCE
|
||||
|
// ========================================
|
||||
|
|
||||
|
// TODO: function showError(element, message) {}
|
||||
|
// TODO: function showSuccess(element) {}
|
||||
|
|
||||
|
|
||||
|
// ========================================
|
||||
|
// SUBMIT HANDLER
|
||||
|
// ========================================
|
||||
|
|
||||
|
// TODO: function handleContactSubmit(e) {}
|
||||
@ -0,0 +1,177 @@ |
|||||
|
# Nový Bootstrap Projekt |
||||
|
|
||||
|
## 🎯 Účel projektu |
||||
|
|
||||
|
Tento projekt slouží jako **prázdná šablona** pro vytváření nových Bootstrap projektů. Obsahuje základní připojení Bootstrap CSS a JS. |
||||
|
|
||||
|
## 📚 Co projekt obsahuje |
||||
|
|
||||
|
- **Bootstrap 5.x** - lokální soubory CSS a JS |
||||
|
- **Základní HTML struktura** - připravená pro vývoj |
||||
|
- **Viewport meta tag** - pro responzivitu |
||||
|
|
||||
|
## 📂 Soubory v projektu |
||||
|
|
||||
|
- `index.html` - základní HTML s připojeným Bootstrapem |
||||
|
- `css/bootstrap.css` - Bootstrap CSS (lokální soubor) |
||||
|
- `js/bootstrap.js` - Bootstrap JavaScript (lokální soubor) |
||||
|
|
||||
|
## 🚀 Jak použít tento projekt |
||||
|
|
||||
|
### Krok 1: Zkopírujte složku |
||||
|
```bash |
||||
|
# Zkopírujte celou složku novyprojekt_bootstrap |
||||
|
# Přejmenujte na název vašeho projektu, např.: |
||||
|
- muj_novy_projekt/ |
||||
|
- portfolio_stranky/ |
||||
|
- eshop_projekt/ |
||||
|
``` |
||||
|
|
||||
|
### Krok 2: Upravte index.html |
||||
|
```html |
||||
|
<!DOCTYPE html> |
||||
|
<html lang="cs"> <!-- změňte jazyk na cs --> |
||||
|
<head> |
||||
|
<meta charset="UTF-8"> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
|
<title>Můj projekt</title> <!-- změňte title --> |
||||
|
<link rel="stylesheet" href="css/bootstrap.css"> |
||||
|
</head> |
||||
|
<body> |
||||
|
<div class="container"> |
||||
|
<h1 class="display-1">Hello World with Bootstrap!</h1> |
||||
|
<!-- Začněte psát váš kód zde --> |
||||
|
</div> |
||||
|
|
||||
|
<script src="js/bootstrap.js"></script> |
||||
|
</body> |
||||
|
</html> |
||||
|
``` |
||||
|
|
||||
|
### Krok 3: Začněte vyvíjet |
||||
|
|
||||
|
**Přidejte Bootstrap komponenty:** |
||||
|
```html |
||||
|
<!-- Navigace --> |
||||
|
<nav class="navbar navbar-expand-lg navbar-light bg-light"> |
||||
|
<div class="container"> |
||||
|
<a class="navbar-brand" href="#">Logo</a> |
||||
|
</div> |
||||
|
</nav> |
||||
|
|
||||
|
<!-- Grid --> |
||||
|
<div class="container"> |
||||
|
<div class="row"> |
||||
|
<div class="col-md-6">Sloupec 1</div> |
||||
|
<div class="col-md-6">Sloupec 2</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Tlačítka --> |
||||
|
<button class="btn btn-primary">Primary</button> |
||||
|
<button class="btn btn-success">Success</button> |
||||
|
|
||||
|
<!-- Karty --> |
||||
|
<div class="card" style="width: 18rem;"> |
||||
|
<div class="card-body"> |
||||
|
<h5 class="card-title">Název karty</h5> |
||||
|
<p class="card-text">Text karty</p> |
||||
|
<a href="#" class="btn btn-primary">Tlačítko</a> |
||||
|
</div> |
||||
|
</div> |
||||
|
``` |
||||
|
|
||||
|
## 💡 Užitečné Bootstrap třídy |
||||
|
|
||||
|
### Container a Grid: |
||||
|
```html |
||||
|
<div class="container"> <!-- Fixní šířka, centrovaný --> |
||||
|
<div class="container-fluid"> <!-- 100% šířka --> |
||||
|
|
||||
|
<div class="row"> <!-- Řádek --> |
||||
|
<div class="col-md-6"> <!-- 50% na tabletu+ --> |
||||
|
``` |
||||
|
|
||||
|
### Utility třídy: |
||||
|
```html |
||||
|
<!-- Margin a Padding --> |
||||
|
m-3 = margin všude |
||||
|
mt-2 = margin-top |
||||
|
px-4 = padding vlevo a vpravo |
||||
|
|
||||
|
<!-- Barvy --> |
||||
|
text-primary = modrý text |
||||
|
bg-danger = červené pozadí |
||||
|
|
||||
|
<!-- Display --> |
||||
|
d-flex = display: flex |
||||
|
d-none d-md-block = skryto na mobilu, zobrazeno na tabletu+ |
||||
|
``` |
||||
|
|
||||
|
### Komponenty: |
||||
|
```html |
||||
|
<!-- Buttons --> |
||||
|
<button class="btn btn-primary">Primary</button> |
||||
|
|
||||
|
<!-- Alerts --> |
||||
|
<div class="alert alert-success">Success!</div> |
||||
|
|
||||
|
<!-- Cards --> |
||||
|
<div class="card">...</div> |
||||
|
|
||||
|
<!-- Modal --> |
||||
|
<button data-bs-toggle="modal" data-bs-target="#myModal"> |
||||
|
Open Modal |
||||
|
</button> |
||||
|
``` |
||||
|
|
||||
|
## 🔗 Bootstrap dokumentace |
||||
|
|
||||
|
- **Oficiální dokumentace:** https://getbootstrap.com/docs/5.3/ |
||||
|
- **Bootstrap Icons:** https://icons.getbootstrap.com/ |
||||
|
- **Bootstrap Examples:** https://getbootstrap.com/docs/5.3/examples/ |
||||
|
|
||||
|
## 📚 Související lekce |
||||
|
|
||||
|
Před použitím tohoto projektu se ujistěte, že jste prošli: |
||||
|
- **13_bootstrap_intro** - úvod do Bootstrapu |
||||
|
- **14_bootstrap_layout** - Bootstrap layout |
||||
|
- **15_bootstrap_components** - Bootstrap komponenty |
||||
|
|
||||
|
## 💡 Tip pro studenty |
||||
|
|
||||
|
**Kdy použít tento projekt:** |
||||
|
- ✅ Nový Bootstrap projekt od nuly |
||||
|
- ✅ Prototypování webové stránky |
||||
|
- ✅ Cvičení Bootstrap komponent |
||||
|
- ✅ Školní projekty |
||||
|
|
||||
|
**Alternativa - CDN (online Bootstrap):** |
||||
|
```html |
||||
|
<!-- Místo lokálních souborů můžete použít CDN --> |
||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> |
||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script> |
||||
|
``` |
||||
|
|
||||
|
**Výhody lokálních souborů:** |
||||
|
- Funguje offline |
||||
|
- Rychlejší (není potřeba stahovat z internetu) |
||||
|
|
||||
|
**Výhody CDN:** |
||||
|
- Menší velikost projektu |
||||
|
- Možná už v cache prohlížeče |
||||
|
|
||||
|
## ⚠️ Důležité poznámky |
||||
|
|
||||
|
1. **Bootstrap.js vyžaduje** - carousel, modal, dropdown fungují jen s připojeným JS |
||||
|
2. **Viewport meta tag je povinný** - bez něj responzivita nebude fungovat |
||||
|
3. **jQuery není potřeba** - Bootstrap 5 už jQuery nepotřebuje |
||||
|
|
||||
|
## 🎯 Další kroky |
||||
|
|
||||
|
1. Prozkoumejte [Bootstrap dokumentaci](https://getbootstrap.com/) |
||||
|
2. Vyzkoušejte různé komponenty |
||||
|
3. Vytvořte vlastní projekt pomocí této šablony |
||||
|
4. Experimentujte s utility třídami |
||||
|
|
||||
|
Hodně štěstí s vaším projektem! 🚀 |
||||
Loading…
Reference in new issue