Skip to content
IRC-CodingIRC-Coding
Основы веб-разработкиHTML CSS JavaScriptDOM ManipulationАдаптивный дизайнFrontend

Основы веб-разработки: HTML, CSS, JavaScript

HTML5, CSS3, JavaScript ES6+, манипуляция DOM и адаптивный дизайн. Современные технологии с практическими примерами.

S

schutzgeist

21 min read
Основы веб-разработки: HTML, CSS, JavaScript

Основы веб-разработки: HTML, CSS, JavaScript, манипуляция DOM и адаптивный дизайн

Этот материал представляет собой полное введение в основы веб-разработки с примерами HTML5, CSS3, JavaScript ES6+, манипуляцией DOM и адаптивным дизайном.

Суть

Веб-разработка опирается на три ключевых технологии: HTML для структуры, CSS для оформления и JavaScript для интерактивности. Адаптивный дизайн обеспечивает оптимальное отображение на всех устройствах.

Определение

Веб-разработка это создание веб-приложений путём сочетания языков разметки, стилей и программирования.

Ключевые технологии:

HTML5 (HyperText Markup Language)

  • Структура: семантическая разметка содержимого
  • Элементы: header, nav, main, section, article, footer
  • Формы: типы input, валидация, доступность
  • Мультимедиа: audio, video, canvas, SVG

CSS3 (Cascading Style Sheets)

  • Стилизация: цвета, шрифты, макет, анимации
  • Макет: flexbox, grid, позиционирование, адаптивный дизайн
  • Псевдоклассы: :hover, :active, :focus, :nth-child
  • Media queries: адаптивный дизайн, контрольные точки

JavaScript ES6+

  • Интерактивность: манипуляция DOM, обработка событий
  • Возможности: стрелочные функции, шаблонные строки, деструктуризация
  • DOM API: выбор элементов, их изменение, события
  • Асинхронность: promises, async/await, Fetch API

Ключевые моменты

  • HTML5: семантический язык разметки для веб-контента
  • CSS3: язык стилей для дизайна и макета
  • JavaScript: язык программирования для веб-интерактивности
  • DOM: Document Object Model для динамических изменений
  • Адаптивный дизайн: адаптация под разные размеры экранов
  • Media queries: CSS-условия для свойств устройств
  • Flexbox/Grid: современные техники макета
  • Основа: необходимы для всех профессий в веб-разработке

Основные компоненты

  1. HTML5: семантическая структура и содержимое
  2. CSS3: визуальное оформление и макет
  3. JavaScript: динамическая функциональность
  4. DOM: программный интерфейс
  5. Адаптивный дизайн: совместимость со всеми устройствами
  6. Доступность: разработка без барьеров
  7. Производительность: скорость загрузки и оптимизация
  8. Best practices: современные стандарты разработки

Практические примеры

1. Базовая структура HTML5

<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="Moderne Webanwendung mit HTML5, CSS3 und JavaScript">
    <title>Webentwicklung Demo</title>
    
    <!-- CSS einbinden -->
    <link rel="stylesheet" href="styles.css">
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&display=swap" rel="stylesheet">
    
    <!-- Favicon -->
    <link rel="icon" type="image/svg+xml" href="favicon.svg">
    
    <!-- Open Graph Meta Tags -->
    <meta property="og:title" content="Webentwicklung Demo">
    <meta property="og:description" content="Moderne Webanwendung">
    <meta property="og:type" content="website">
</head>
<body>
    <!-- Skip Navigation für Accessibility -->
    <a href="#main-content" class="skip-link">Zum Hauptinhalt springen</a>
    
    <!-- Header mit Navigation -->
    <header class="header">
        <nav class="nav" aria-label="Hauptnavigation">
            <div class="nav-container">
                <div class="nav-brand">
                    <a href="#" class="brand-link">
                        <img src="logo.svg" alt="Logo" class="brand-logo">
                        <span class="brand-text">WebDev</span>
                    </a>
                </div>
                
                <button class="nav-toggle" aria-expanded="false" aria-controls="nav-menu">
                    <span class="hamburger"></span>
                    <span class="visually-hidden">Menü</span>
                </button>
                
                <ul id="nav-menu" class="nav-menu">
                    <li><a href="#home" class="nav-link">Home</a></li>
                    <li><a href="#features" class="nav-link">Features</a></li>
                    <li><a href="#contact" class="nav-link">Kontakt</a></li>
                </ul>
            </div>
        </nav>
    </header>
    
    <!-- Hauptinhalt -->
    <main id="main-content" class="main">
        <!-- Hero Section -->
        <section class="hero" id="home">
            <div class="container">
                <h1 class="hero-title">Moderne Webentwicklung</h1>
                <p class="hero-subtitle">HTML5, CSS3 und JavaScript in Aktion</p>
                
                <div class="hero-actions">
                    <button class="btn btn-primary" data-action="start">Starten</button>
                    <a href="#features" class="btn btn-secondary">Mehr erfahren</a>
                </div>
                
                <!-- Interactive Demo -->
                <div class="demo-section">
                    <div class="demo-container">
                        <canvas id="demo-canvas" width="400" height="200"></canvas>
                        <div class="demo-controls">
                            <button class="demo-btn" data-action="clear">Löschen</button>
                            <button class="demo-btn" data-action="animate">Animieren</button>
                            <input type="color" id="color-picker" value="#3b82f6">
                        </div>
                    </div>
                </div>
            </div>
        </section>
        
        <!-- Features Section -->
        <section class="features" id="features">
            <div class="container">
                <h2 class="section-title">Features</h2>
                
                <div class="features-grid">
                    <article class="feature-card">
                        <div class="feature-icon">
                            <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                <path d="M12 2L2 7v10c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V7l-10-5z"/>
                            </svg>
                        </div>
                        <h3 class="feature-title">Sicherheit</h3>
                        <p class="feature-description">Moderne Sicherheitsstandards und Best Practices</p>
                    </article>
                    
                    <article class="feature-card">
                        <div class="feature-icon">
                            <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                <rect x="2" y="3" width="20" height="14" rx="2" ry="2"/>
                                <line x1="8" y1="21" x2="16" y2="21"/>
                                <line x1="12" y1="17" x2="12" y2="21"/>
                            </svg>
                        </div>
                        <h3 class="feature-title">Responsive</h3>
                        <p class="feature-description">Optimale Darstellung auf allen Geräten</p>
                    </article>
                    
                    <article class="feature-card">
                        <div class="feature-icon">
                            <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                <path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/>
                            </svg>
                        </div>
                        <h3 class="feature-title">Performance</h3>
                        <p class="feature-description">Schnelle Ladezeiten und reibungslose Interaktion</p>
                    </article>
                </div>
            </div>
        </section>
        
        <!-- Kontakt Formular -->
        <section class="contact" id="contact">
            <div class="container">
                <h2 class="section-title">Kontakt</h2>
                
                <form class="contact-form" novalidate>
                    <div class="form-group">
                        <label for="name" class="form-label">Name *</label>
                        <input type="text" id="name" name="name" class="form-input" required>
                        <span class="form-error" id="name-error">Bitte geben Sie Ihren Namen ein</span>
                    </div>
                    
                    <div class="form-group">
                        <label for="email" class="form-label">E-Mail *</label>
                        <input type="email" id="email" name="email" class="form-input" required>
                        <span class="form-error" id="email-error">Bitte geben Sie eine gültige E-Mail ein</span>
                    </div>
                    
                    <div class="form-group">
                        <label for="message" class="form-label">Nachricht</label>
                        <textarea id="message" name="message" class="form-textarea" rows="4"></textarea>
                    </div>
                    
                    <div class="form-actions">
                        <button type="submit" class="btn btn-primary">Senden</button>
                        <button type="reset" class="btn btn-secondary">Zurücksetzen</button>
                    </div>
                </form>
            </div>
        </section>
    </main>
    
    <!-- Footer -->
    <footer class="footer">
        <div class="container">
            <p>&copy; 2024 Webentwicklung Demo. Alle Rechte vorbehalten.</p>
            <div class="footer-links">
                <a href="#impressum">Impressum</a>
                <a href="#datenschutz">Datenschutz</a>
            </div>
        </div>
    </footer>
    
    <!-- JavaScript einbinden -->
    <script src="script.js"></script>
</body>
</html>

2. CSS3 с Responsive Design

/* CSS Reset и базовые стили */
*,
*::before,
*::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

/* CSS Custom Properties (переменные) */
:root {
    /* Цвета */
    --color-primary: #3b82f6;
    --color-primary-dark: #2563eb;
    --color-primary-light: #60a5fa;
    --color-secondary: #64748b;
    --color-success: #10b981;
    --color-error: #ef4444;
    --color-warning: #f59e0b;
    
    /* Нейтральные */
    --color-white: #ffffff;
    --color-gray-50: #f8fafc;
    --color-gray-100: #f1f5f9;
    --color-gray-900: #0f172a;
    
    /* Типография */
    --font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
    --font-size-xs: 0.75rem;
    --font-size-sm: 0.875rem;
    --font-size-base: 1rem;
    --font-size-lg: 1.125rem;
    --font-size-xl: 1.25rem;
    --font-size-2xl: 1.5rem;
    --font-size-3xl: 1.875rem;
    --font-size-4xl: 2.25rem;
    
    /* Отступы */
    --spacing-xs: 0.25rem;
    --spacing-sm: 0.5rem;
    --spacing-md: 1rem;
    --spacing-lg: 1.5rem;
    --spacing-xl: 2rem;
    --spacing-2xl: 3rem;
    
    /* Радиус границ */
    --radius-sm: 0.25rem;
    --radius-md: 0.5rem;
    --radius-lg: 1rem;
    --radius-full: 9999px;
    
    /* Тени */
    --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
    --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1);
    --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1);
    --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1);
    
    /* Переходы */
    --transition-fast: 150ms ease-in-out;
    --transition-normal: 300ms ease-in-out;
    --transition-slow: 500ms ease-in-out;
}

/* Базовые стили */
html {
    font-size: 16px;
    scroll-behavior: smooth;
}

body {
    font-family: var(--font-family);
    font-size: var(--font-size-base);
    line-height: 1.6;
    color: var(--color-gray-900);
    background-color: var(--color-white);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

/* Утилиты */
.container {
    max-width: 1200px;
    margin: 0 auto;
    padding: 0 var(--spacing-md);
}

.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

.skip-link {
    position: absolute;
    top: -40px;
    left: 0;
    background: var(--color-primary);
    color: var(--color-white);
    padding: var(--spacing-sm) var(--spacing-md);
    text-decoration: none;
    border-radius: var(--radius-md);
    z-index: 100;
    transition: top var(--transition-fast);
}

.skip-link:focus {
    top: var(--spacing-sm);
}

/* Шапка и навигация */
.header {
    background-color: var(--color-white);
    box-shadow: var(--shadow-sm);
    position: sticky;
    top: 0;
    z-index: 50;
}

.nav-container {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--spacing-md) 0;
}

.brand-link {
    display: flex;
    align-items: center;
    gap: var(--spacing-sm);
    text-decoration: none;
    color: var(--color-gray-900);
    font-weight: 600;
    transition: color var(--transition-fast);
}

.brand-link:hover {
    color: var(--color-primary);
}

.brand-logo {
    width: 32px;
    height: 32px;
}

.nav-toggle {
    display: none;
    background: none;
    border: none;
    padding: var(--spacing-sm);
    cursor: pointer;
}

.hamburger {
    display: block;
    width: 24px;
    height: 2px;
    background-color: var(--color-gray-900);
    position: relative;
    transition: background-color var(--transition-fast);
}

.hamburger::before,
.hamburger::after {
    content: '';
    position: absolute;
    width: 24px;
    height: 2px;
    background-color: var(--color-gray-900);
    transition: transform var(--transition-fast);
}

.hamburger::before {
    top: -8px;
}

.hamburger::after {
    top: 8px;
}

.nav-toggle[aria-expanded="true"] .hamburger {
    background-color: transparent;
}

.nav-toggle[aria-expanded="true"] .hamburger::before {
    transform: rotate(45deg);
    top: 0;
}

.nav-toggle[aria-expanded="true"] .hamburger::after {
    transform: rotate(-45deg);
    top: 0;
}

.nav-menu {
    display: flex;
    list-style: none;
    gap: var(--spacing-lg);
}

.nav-link {
    text-decoration: none;
    color: var(--color-gray-700);
    font-weight: 500;
    padding: var(--spacing-sm) var(--spacing-md);
    border-radius: var(--radius-md);
    transition: all var(--transition-fast);
}

.nav-link:hover {
    color: var(--color-primary);
    background-color: var(--color-gray-50);
}

/* Секция Hero */
.hero {
    background: linear-gradient(135deg, var(--color-gray-50) 0%, var(--color-white) 100%);
    padding: var(--spacing-2xl) 0;
    text-align: center;
}

.hero-title {
    font-size: var(--font-size-4xl);
    font-weight: 700;
    color: var(--color-gray-900);
    margin-bottom: var(--spacing-md);
    line-height: 1.2;
}

.hero-subtitle {
    font-size: var(--font-size-xl);
    color: var(--color-secondary);
    margin-bottom: var(--spacing-xl);
    max-width: 600px;
    margin-left: auto;
    margin-right: auto;
}

.hero-actions {
    display: flex;
    gap: var(--spacing-md);
    justify-content: center;
    margin-bottom: var(--spacing-2xl);
}

/* Кнопки */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: var(--spacing-md) var(--spacing-lg);
    font-size: var(--font-size-base);
    font-weight: 600;
    text-decoration: none;
    border: none;
    border-radius: var(--radius-md);
    cursor: pointer;
    transition: all var(--transition-fast);
    min-width: 120px;
}

.btn-primary {
    background-color: var(--color-primary);
    color: var(--color-white);
}

.btn-primary:hover {
    background-color: var(--color-primary-dark);
    transform: translateY(-2px);
    box-shadow: var(--shadow-lg);
}

.btn-secondary {
    background-color: var(--color-gray-100);
    color: var(--color-gray-900);
}

.btn-secondary:hover {
    background-color: var(--color-gray-200);
    transform: translateY(-2px);
}

/* Демо-секция */
.demo-section {
    margin-top: var(--spacing-2xl);
}

.demo-container {
    background-color: var(--color-white);
    border-radius: var(--radius-lg);
    padding: var(--spacing-lg);
    box-shadow: var(--shadow-md);
    max-width: 500px;
    margin: 0 auto;
}

#demo-canvas {
    border: 2px solid var(--color-gray-200);
    border-radius: var(--radius-md);
    width: 100%;
    height: auto;
    display: block;
    margin-bottom: var(--spacing-md);
}

.demo-controls {
    display: flex;
    gap: var(--spacing-sm);
    flex-wrap: wrap;
    align-items: center;
}

.demo-btn {
    padding: var(--spacing-sm) var(--spacing-md);
    background-color: var(--color-gray-100);
    border: none;
    border-radius: var(--radius-sm);
    cursor: pointer;
    font-size: var(--font-size-sm);
    transition: all var(--transition-fast);
}

.demo-btn:hover {
    background-color: var(--color-gray-200);
}

#color-picker {
    width: 50px;
    height: 36px;
    border: none;
    border-radius: var(--radius-sm);
    cursor: pointer;
}

/* Секция возможностей */
.features {
    padding: var(--spacing-2xl) 0;
    background-color: var(--color-gray-50);
}

.section-title {
    font-size: var(--font-size-3xl);
    font-weight: 700;
    text-align: center;
    margin-bottom: var(--spacing-2xl);
    color: var(--color-gray-900);
}

.features-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: var(--spacing-xl);
}

.feature-card {
    background-color: var(--color-white);
    padding: var(--spacing-xl);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    text-align: center;
    transition: transform var(--transition-normal), box-shadow var(--transition-normal);
}

.feature-card:hover {
    transform: translateY(-4px);
    box-shadow: var(--shadow-xl);
}

.feature-icon {
    display: flex;
    justify-content: center;
    margin-bottom: var(--spacing-lg);
    color: var(--color-primary);
}

.feature-title {
    font-size: var(--font-size-xl);
    font-weight: 600;
    margin-bottom: var(--spacing-md);
    color: var(--color-gray-900);
}

.feature-description {
    color: var(--color-secondary);
    line-height: 1.6;
}

/* Форма обратной связи */
.contact {
    padding: var(--spacing-2xl) 0;
}

.contact-form {
    max-width: 600px;
    margin: 0 auto;
}

.form-group {
    margin-bottom: var(--spacing-lg);
}

.form-label {
    display: block;
    font-weight: 600;
    margin-bottom: var(--spacing-sm);
    color: var(--color-gray-900);
}

.form-input,
.form-textarea {
    width: 100%;
    padding: var(--spacing-md);
    border: 2px solid var(--color-gray-200);
    border-radius: var(--radius-md);
    font-size: var(--font-size-base);
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}

.form-input:focus,
.form-textarea:focus {
    outline: none;
    border-color: var(--color-primary);
    box-shadow: 0 0 0 3px rgb(59 130 246 / 0.1);
}

.form-input:invalid,
.form-textarea:invalid {
    border-color: var(--color-error);
}

.form-error {
    display: none;
    color: var(--color-error);
    font-size: var(--font-size-sm);
    margin-top: var(--spacing-xs);
}

.form-input:invalid ~ .form-error {
    display: block;
}

.form-actions {
    display: flex;
    gap: var(--spacing-md);
    justify-content: flex-end;
}

/* Подвал */
.footer {
    background-color: var(--color-gray-900);
    color: var(--color-gray-100);
    padding: var(--spacing-xl) 0;
    text-align: center;
}

.footer-links {
    margin-top: var(--spacing-md);
    display: flex;
    gap: var(--spacing-lg);
    justify-content: center;
}

.footer-links a {
    color: var(--color-gray-400);
    text-decoration: none;
    transition: color var(--transition-fast);
}

.footer-links a:hover {
    color: var(--color-white);
}

/* Responsive Design */

/* Подход Mobile First */
/* Малые планшеты и большие телефоны */
@media (min-width: 640px) {
    .container {
        padding: 0 var(--spacing-lg);
    }
    
    .hero-title {
        font-size: var(--font-size-5xl);
    }
    
    .features-grid {
        grid-template-columns: repeat(2, 1fr);
    }
}

/* Планшеты */
@media (min-width: 768px) {
    .nav-toggle {
        display: none;
    }
    
    .nav-menu {
        display: flex;
    }
    
    .hero-actions {
        flex-direction: row;
    }
    
    .demo-controls {
        justify-content: center;
    }
}

/* Десктоп */
@media (min-width: 1024px) {
    .hero {
        padding: var(--spacing-3xl) 0;
    }
    
    .features-grid {
        grid-template-columns: repeat(3, 1fr);
    }
    
    .form-actions {
        justify-content: flex-start;
    }
}

/* Широкий десктоп */
@media (min-width: 1280px) {
    .container {
        padding: 0 var(--spacing-xl);
    }
    
    .hero-title {
        font-size: var(--font-size-6xl);
    }
}

/* Поддержка тёмного режима */
@media (prefers-color-scheme: dark) {
    :root {
        --color-white: #0f172a;
        --color-gray-50: #1e293b;
        --color-gray-100: #334155;
        --color-gray-900: #f8fafc;
        --color-gray-700: #cbd5e1;
        --color-gray-200: #475569;
    }
    
    .header {
        background-color: var(--color-gray-50);
    }
    
    .hero {
        background: linear-gradient(135deg, var(--color-gray-50) 0%, var(--color-white) 100%);
    }
    
    .features {
        background-color: var(--color-white);
    }
    
    .btn-secondary {
        background-color: var(--color-gray-200);
        color: var(--color-gray-900);
    }
    
    .btn-secondary:hover {
        background-color: var(--color-gray-100);
    }
}

/* Стили для печати */
@media print {
    .nav-toggle,
    .hero-actions,
    .demo-section,
    .contact-form {
        display: none;
    }
    
    .hero {
        padding: var(--spacing-lg) 0;
    }
    
    body {
        font-size: 12pt;
        line-height: 1.4;
    }
    
    h1, h2, h3 {
        page-break-after: avoid;
    }
    
    .feature-card {
        break-inside: avoid;
    }
}

/* Анимации */
@keyframes fadeIn {
    from {
        opacity: 0;
        transform: translateY(20px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

.fade-in {
    animation: fadeIn 0.6s ease-out;
}

@keyframes pulse {
    0%, 100% {
        transform: scale(1);
    }
    50% {
        transform: scale(1.05);
    }
}

.pulse {
    animation: pulse 2s infinite;
}

/* Стили фокуса для доступности */
.btn:focus,
.nav-link:focus,
.form-input:focus,
.form-textarea:focus {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

/* Режим высокой контрастности */
@media (prefers-contrast: high) {
    :root {
        --color-primary: #0000ff;
        --color-secondary: #000000;
        --color-gray-900: #000000;
        --color-gray-100: #ffffff;
    }
    
    .btn {
        border: 2px solid currentColor;
    }
    
    .form-input,
    .form-textarea {
        border-width: 3px;
    }
}

/* Сокращённые анимации */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

3. JavaScript ES6+ с манипуляцией DOM

// Современный JavaScript с возможностями ES6+

// Выбор элементов DOM
const elements = {
    // Навигация
    navToggle: document.querySelector('.nav-toggle'),
    navMenu: document.querySelector('#nav-menu'),
    navLinks: document.querySelectorAll('.nav-link'),
    
    // Hero секция
    heroTitle: document.querySelector('.hero-title'),
    heroActions: document.querySelector('.hero-actions'),
    
    // Демо
    canvas: document.querySelector('#demo-canvas'),
    demoButtons: document.querySelectorAll('.demo-btn'),
    colorPicker: document.querySelector('#color-picker'),
    
    // Форма
    contactForm: document.querySelector('.contact-form'),
    formInputs: document.querySelectorAll('.form-input'),
    
    // Функции
    featureCards: document.querySelectorAll('.feature-card')
};

// State Management
const state = {
    isNavOpen: false,
    currentColor: '#3b82f6',
    isDrawing: false,
    lastX: 0,
    lastY: 0
};

// Утилиты
const utils = {
    // Debounce для производительности
    debounce(func, wait) {
        let timeout;
        return function executedFunction(...args) {
            const later = () => {
                clearTimeout(timeout);
                func(...args);
            };
            clearTimeout(timeout);
            timeout = setTimeout(later, wait);
        };
    },
    
    // Throttle для событий
    throttle(func, limit) {
        let inThrottle;
        return function() {
            const args = arguments;
            const context = this;
            if (!inThrottle) {
                func.apply(context, args);
                inThrottle = true;
                setTimeout(() => inThrottle = false, limit);
            }
        };
    },
    
    // Появление элемента с анимацией
    animateIn(element, animation = 'fade-in') {
        element.style.opacity = '0';
        element.classList.add(animation);
        
        // Force reflow
        element.offsetHeight;
        
        element.style.opacity = '1';
        
        setTimeout(() => {
            element.classList.remove(animation);
            element.style.opacity = '';
        }, 600);
    },
    
    // Плавная прокрутка
    smoothScroll(target) {
        const element = document.querySelector(target);
        if (element) {
            element.scrollIntoView({
                behavior: 'smooth',
                block: 'start'
            });
        }
    },
    
    // Локальное хранилище
    saveToLocalStorage(key, data) {
        try {
            localStorage.setItem(key, JSON.stringify(data));
        } catch (error) {
            console.warn('LocalStorage недоступен:', error);
        }
    },
    
    getFromLocalStorage(key) {
        try {
            const data = localStorage.getItem(key);
            return data ? JSON.parse(data) : null;
        } catch (error) {
            console.warn('Ошибка чтения LocalStorage:', error);
            return null;
        }
    }
};

// Навигация
class Navigation {
    constructor() {
        this.init();
    }
    
    init() {
        // Переключение мобильной навигации
        if (elements.navToggle) {
            elements.navToggle.addEventListener('click', this.toggleNav.bind(this));
        }
        
        // Ссылки навигации
        elements.navLinks.forEach(link => {
            link.addEventListener('click', this.handleNavClick.bind(this));
        });
        
        // Закрытие по Escape
        document.addEventListener('keydown', (e) => {
            if (e.key === 'Escape' && state.isNavOpen) {
                this.closeNav();
            }
        });
        
        // Закрытие при клике вне меню
        document.addEventListener('click', (e) => {
            if (state.isNavOpen && !elements.navMenu.contains(e.target) && !elements.navToggle.contains(e.target)) {
                this.closeNav();
            }
        });
    }
    
    toggleNav() {
        state.isNavOpen = !state.isNavOpen;
        this.updateNavUI();
    }
    
    openNav() {
        state.isNavOpen = true;
        this.updateNavUI();
    }
    
    closeNav() {
        state.isNavOpen = false;
        this.updateNavUI();
    }
    
    updateNavUI() {
        elements.navToggle.setAttribute('aria-expanded', state.isNavOpen);
        elements.navMenu.classList.toggle('nav-open', state.isNavOpen);
        
        // Блокировка прокрутки body
        document.body.style.overflow = state.isNavOpen ? 'hidden' : '';
    }
    
    handleNavClick(e) {
        e.preventDefault();
        const href = e.target.getAttribute('href');
        
        if (href && href.startsWith('#')) {
            utils.smoothScroll(href);
            this.closeNav();
        }
    }
}

// Canvas демо
class CanvasDemo {
    constructor(canvas) {
        this.canvas = canvas;
        this.ctx = canvas.getContext('2d');
        this.init();
    }
    
    init() {
        this.setupCanvas();
        this.bindEvents();
        this.drawInitialContent();
    }
    
    setupCanvas() {
        // Поддержка High DPI
        const dpr = window.devicePixelRatio || 1;
        const rect = this.canvas.getBoundingClientRect();
        
        this.canvas.width = rect.width * dpr;
        this.canvas.height = rect.height * dpr;
        
        this.ctx.scale(dpr, dpr);
        
        this.canvas.style.width = rect.width + 'px';
        this.canvas.style.height = rect.height + 'px';
    }
    
    bindEvents() {
        // События мыши
        this.canvas.addEventListener('mousedown', this.startDrawing.bind(this));
        this.canvas.addEventListener('mousemove', this.draw.bind(this));
        this.canvas.addEventListener('mouseup', this.stopDrawing.bind(this));
        this.canvas.addEventListener('mouseout', this.stopDrawing.bind(this));
        
        // События касания
        this.canvas.addEventListener('touchstart', this.handleTouch.bind(this));
        this.canvas.addEventListener('touchmove', this.handleTouch.bind(this));
        this.canvas.addEventListener('touchend', this.stopDrawing.bind(this));
        
        // Кнопки демо
        elements.demoButtons.forEach(btn => {
            btn.addEventListener('click', this.handleDemoAction.bind(this));
        });
        
        // Выбор цвета
        elements.colorPicker.addEventListener('change', (e) => {
            state.currentColor = e.target.value;
        });
        
        // Изменение размера окна
        window.addEventListener('resize', utils.debounce(() => {
            this.setupCanvas();
            this.drawInitialContent();
        }, 250));
    }
    
    startDrawing(e) {
        state.isDrawing = true;
        const rect = this.canvas.getBoundingClientRect();
        state.lastX = e.clientX - rect.left;
        state.lastY = e.clientY - rect.top;
    }
    
    draw(e) {
        if (!state.isDrawing) return;
        
        const rect = this.canvas.getBoundingClientRect();
        const x = e.clientX - rect.left;
        const y = e.clientY - rect.top;
        
        this.ctx.beginPath();
        this.ctx.moveTo(state.lastX, state.lastY);
        this.ctx.lineTo(x, y);
        this.ctx.strokeStyle = state.currentColor;
        this.ctx.lineWidth = 3;
        this.ctx.lineCap = 'round';
        this.ctx.stroke();
        
        state.lastX = x;
        state.lastY = y;
    }
    
    stopDrawing() {
        state.isDrawing = false;
    }
    
    handleTouch(e) {
        e.preventDefault();
        const touch = e.touches[0];
        const mouseEvent = new MouseEvent(e.type === 'touchstart' ? 'mousedown' : 
                                         e.type === 'touchmove' ? 'mousemove' : 'mouseup', {
            clientX: touch.clientX,
            clientY: touch.clientY
        });
        this.canvas.dispatchEvent(mouseEvent);
    }
    
    handleDemoAction(e) {
        const action = e.target.dataset.action;
        
        switch (action) {
            case 'clear':
                this.clearCanvas();
                break;
            case 'animate':
                this.animateShapes();
                break;
        }
    }
    
    clearCanvas() {
        const rect = this.canvas.getBoundingClientRect();
        this.ctx.clearRect(0, 0, rect.width, rect.height);
        this.drawInitialContent();
    }
    
    drawInitialContent() {
        const rect = this.canvas.getBoundingClientRect();
        const centerX = rect.width / 2;
        const centerY = rect.height / 2;
        
        // Приветственный текст
        this.ctx.font = '20px Inter';
        this.ctx.fillStyle = state.currentColor;
        this.ctx.textAlign = 'center';
        this.ctx.fillText('Рисуйте здесь!', centerX, centerY);
    }
    
    animateShapes() {
        const rect = this.canvas.getBoundingClientRect();
        let x = 0;
        let y = rect.height / 2;
        let dx = 2;
        let dy = 1;
        let hue = 0;
        
        const animate = () => {
            // Стирание следов
            this.ctx.fillStyle = 'rgba(255, 255, 255, 0.1)';
            this.ctx.fillRect(0, 0, rect.width, rect.height);
            
            // Рисование круга
            this.ctx.beginPath();
            this.ctx.arc(x, y, 20, 0, Math.PI * 2);
            this.ctx.fillStyle = `hsl(${hue}, 70%, 50%)`;
            this.ctx.fill();
            
            // Обновление позиции
            x += dx;
            y += dy;
            hue += 2;
            
            // Отскок от стен
            if (x > rect.width - 20 || x < 20) dx = -dx;
            if (y > rect.height - 20 || y < 20) dy = -dy;
            
            // Продолжение анимации
            if (hue < 360) {
                requestAnimationFrame(animate);
            }
        };
        
        animate();
    }
}

// Валидация форм
class FormValidator {
    constructor(form) {
        this.form = form;
        this.init();
    }
    
    init() {
        this.form.addEventListener('submit', this.handleSubmit.bind(this));
        
        // Проверка в реальном времени
        elements.formInputs.forEach(input => {
            input.addEventListener('blur', () => this.validateField(input));
            input.addEventListener('input', () => this.clearError(input));
        });
    }
    
    handleSubmit(e) {
        e.preventDefault();
        
        if (this.validateForm()) {
            this.submitForm();
        } else {
            this.showFirstError();
        }
    }
    
    validateForm() {
        let isValid = true;
        
        elements.formInputs.forEach(input => {
            if (!this.validateField(input)) {
                isValid = false;
            }
        });
        
        return isValid;
    }
    
    validateField(field) {
        const value = field.value.trim();
        const type = field.type;
        const isRequired = field.hasAttribute('required');
        let isValid = true;
        
        // Проверка обязательного поля
        if (isRequired && !value) {
            this.showError(field, 'Это поле обязательно');
            isValid = false;
        }
        
        // Проверка email
        if (type === 'email' && value) {
            const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
            if (!emailRegex.test(value)) {
                this.showError(field, 'Введите корректный адрес электронной почты');
                isValid = false;
            }
        }
        
        // Проверка имени
        if (field.id === 'name' && value) {
            if (value.length < 2) {
                this.showError(field, 'Имя должно быть не менее 2 символов');
                isValid = false;
            }
        }
        
        if (isValid) {
            this.clearError(field);
        }
        
        return isValid;
    }
    
    showError(field, message) {
        field.classList.add('error');
        const errorElement = document.getElementById(`${field.id}-error`);
        if (errorElement) {
            errorElement.textContent = message;
            errorElement.style.display = 'block';
        }
    }
    
    clearError(field) {
        field.classList.remove('error');
        const errorElement = document.getElementById(`${field.id}-error`);
        if (errorElement) {
            errorElement.style.display = 'none';
        }
    }
    
    showFirstError() {
        const firstError = this.form.querySelector('.form-input.error');
        if (firstError) {
            firstError.focus();
            firstError.scrollIntoView({ behavior: 'smooth', block: 'center' });
        }
    }
    
    submitForm() {
        const formData = new FormData(this.form);
        const data = Object.fromEntries(formData);
        
        // Имитация API запроса
        this.showLoading();
        
        setTimeout(() => {
            this.showSuccess();
            this.form.reset();
        }, 1500);
    }
    
    showLoading() {
        const submitBtn = this.form.querySelector('button[type="submit"]');
        submitBtn.disabled = true;
        submitBtn.textContent = 'Отправляется...';
    }
    
    showSuccess() {
        const submitBtn = this.form.querySelector('button[type="submit"]');
        submitBtn.disabled = false;
        submitBtn.textContent = 'Отправлено!';
        submitBtn.classList.add('success');
        
        setTimeout(() => {
            submitBtn.textContent = 'Отправить';
            submitBtn.classList.remove('success');
        }, 3000);
        
        // Показать сообщение об успехе
        this.showMessage('Сообщение успешно отправлено!', 'success');
    }
    
    showMessage(text, type = 'info') {
        const message = document.createElement('div');
        message.className = `message message-${type}`;
        message.textContent = text;
        
        this.form.appendChild(message);
        
        utils.animateIn(message);
        
        setTimeout(() => {
            message.remove();
        }, 5000);
    }
}

// Intersection Observer для анимаций при прокрутке
class ScrollAnimations {
    constructor() {
        this.init();
    }
    
    init() {
        const observerOptions = {
            threshold: 0.1,
            rootMargin: '0px 0px -50px 0px'
        };
        
        const observer = new IntersectionObserver((entries) => {
            entries.forEach(entry => {
                if (entry.isIntersecting) {
                    this.animateElement(entry.target);
                    observer.unobserve(entry.target);
                }
            });
        }, observerOptions);
        
        // Наблюдение за элементами
        elements.featureCards.forEach(card => {
            observer.observe(card);
        });
        
        // Hero анимация
        if (elements.heroTitle) {
            utils.animateIn(elements.heroTitle);
        }
        
        if (elements.heroActions) {
            setTimeout(() => {
                utils.animateIn(elements.heroActions);
            }, 300);
        }
    }
    
    animateElement(element) {
        utils.animateIn(element);
        
        // Pulse анимация для карточек функций
        if (element.classList.contains('feature-card')) {
            setTimeout(() => {
                element.classList.add('pulse');
                setTimeout(() => {
                    element.classList.remove('pulse');
                }, 2000);
            }, 1000);
        }
    }
}

// Инициализация приложения
class App {
    constructor() {
        this.modules = {};
        this.init();
    }
    
    init() {
        // Проверка готовности DOM
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', () => this.start());
        } else {
            this.start();
        }
    }
    
    start() {
        console.log('🚀 Демо веб-разработки запущено');
        
        // Инициализация модулей
        this.modules.navigation = new Navigation();
        
        if (elements.canvas) {
            this.modules.canvas = new CanvasDemo(elements.canvas);
        }
        
        if (elements.contactForm) {
            this.modules.form = new FormValidator(elements.contactForm);
        }
        
        this.modules.scrollAnimations = new ScrollAnimations();
        
        // Мониторинг производительности
        this.initPerformanceMonitoring();
        
        // Регистрация Service Worker
        this.registerServiceWorker();
    }
    
    initPerformanceMonitoring() {
        // Performance страницы при загрузке
        window.addEventListener('load', () => {
            const perfData = performance.getEntriesByType('navigation')[0];
            const loadTime = perfData.loadEventEnd - perfData.loadEventStart;
            
            console.log(`⚡ Страница загружена за ${loadTime}ms`);
            
            // Предупреждение о медленной загрузке
            if (loadTime > 3000) {
                console.warn('⚠️ Обнаружена медленная загрузка');
            }
        });
        
        // Largest Contentful Paint
        new PerformanceObserver((list) => {
            const entries = list.getEntries();
            const lastEntry = entries[entries.length - 1];
            console.log(`🎨 Largest Contentful Paint: ${lastEntry.startTime}ms`);
        }).observe({ entryTypes: ['largest-contentful-paint'] });
    }
    
    async registerServiceWorker() {
        if ('serviceWorker' in navigator) {
            try {
                const registration = await navigator.serviceWorker.register('/sw.js');
                console.log('✅ Service Worker зарегистрирован:', registration.scope);
            } catch (error) {
                console.log('❌ Ошибка регистрации Service Worker:', error);
            }
        }
    }
}

// Запуск приложения
const app = new App();

// Экспорт для модульной системы
if (typeof module !== 'undefined' && module.exports) {
    module.exports = { App, Navigation, CanvasDemo, FormValidator, ScrollAnimations, utils };
}

4. Service Worker для офлайн-функциональности

// sw.js - Service Worker для кеширования и офлайн-функциональности

const CACHE_NAME = 'webdev-demo-v1';
const urlsToCache = [
    '/',
    '/index.html',
    '/styles.css',
    '/script.js',
    '/logo.svg',
    '/favicon.svg'
];

// Установка Service Worker
self.addEventListener('install', (event) => {
    console.log('🔧 Service Worker installing...');
    
    event.waitUntil(
        caches.open(CACHE_NAME)
            .then((cache) => {
                console.log('📦 Caching app shell');
                return cache.addAll(urlsToCache);
            })
            .then(() => {
                console.log('✅ App shell cached successfully');
                return self.skipWaiting();
            })
    );
});

// Активация Service Worker
self.addEventListener('activate', (event) => {
    console.log('🔄 Service Worker activating...');
    
    event.waitUntil(
        caches.keys().then((cacheNames) => {
            return Promise.all(
                cacheNames.map((cacheName) => {
                    if (cacheName !== CACHE_NAME) {
                        console.log('🗑️ Deleting old cache:', cacheName);
                        return caches.delete(cacheName);
                    }
                })
            );
        }).then(() => {
            console.log('✅ Service Worker activated');
            return self.clients.claim();
        })
    );
});

// Обработка запросов - сначала из сети, затем кеш
self.addEventListener('fetch', (event) => {
    const { request } = event;
    const url = new URL(request.url);
    
    // Только для HTTP/HTTPS-запросов
    if (url.protocol !== 'http:' && url.protocol !== 'https:') {
        return;
    }
    
    event.respondWith(
        caches.match(request)
            .then((response) => {
                // Cache Hit - вернуть закешированный ответ
                if (response) {
                    console.log('📋 Serving from cache:', request.url);
                    return response;
                }
                
                // Запрос из сети
                console.log('🌐 Fetching from network:', request.url);
                return fetch(request)
                    .then((response) => {
                        // Проверка валидности ответа
                        if (!response || response.status !== 200 || response.type !== 'basic') {
                            return response;
                        }
                        
                        // Клонирование ответа для кеширования
                        const responseToCache = response.clone();
                        
                        // Кеширование успешных ответов
                        if (shouldCache(request)) {
                            caches.open(CACHE_NAME)
                                .then((cache) => {
                                    console.log('💾 Caching new resource:', request.url);
                                    cache.put(request, responseToCache);
                                });
                        }
                        
                        return response;
                    })
                    .catch(() => {
                        // Сеть недоступна - попытаемся получить из кеша
                        console.log('❌ Network failed, trying cache for:', request.url);
                        return caches.match(request);
                    });
            })
    );
});

// Вспомогательная функция для определения, нужно ли кешировать запрос
function shouldCache(request) {
    const url = new URL(request.url);
    
    // Не кешируем API-запросы и внешние ресурсы
    if (url.pathname.startsWith('/api/') || url.origin !== self.location.origin) {
        return false;
    }
    
    // Кешируем статические файлы
    const staticExtensions = ['.css', '.js', '.svg', '.png', '.jpg', '.jpeg', '.webp'];
    const hasStaticExtension = staticExtensions.some(ext => url.pathname.endsWith(ext));
    
    return hasStaticExtension || url.pathname === '/';
}

// Фоновая синхронизация для офлайн-операций
self.addEventListener('sync', (event) => {
    if (event.tag === 'background-sync') {
        console.log('🔄 Background sync triggered');
        
        event.waitUntil(
            // Здесь могут синхронизироваться офлайн-операции
            new Promise((resolve) => {
                setTimeout(() => {
                    console.log('✅ Background sync completed');
                    resolve();
                }, 1000);
            })
        );
    }
});

// Push-уведомления
self.addEventListener('push', (event) => {
    console.log('📨 Push message received');
    
    const options = {
        body: event.data.text(),
        icon: '/logo.svg',
        badge: '/favicon.svg',
        vibrate: [100, 50, 100],
        data: {
            dateOfArrival: Date.now(),
            primaryKey: 1
        },
        actions: [
            {
                action: 'explore',
                title: 'Explore',
                icon: '/images/checkmark.png'
            },
            {
                action: 'close',
                title: 'Close',
                icon: '/images/xmark.png'
            }
        ]
    };
    
    event.waitUntil(
        self.registration.showNotification('WebDev Demo', options)
    );
});

// Обработка клика по уведомлению
self.addEventListener('notificationclick', (event) => {
    console.log('🔔 Notification clicked');
    
    event.notification.close();
    
    if (event.action === 'explore') {
        event.waitUntil(
            clients.openWindow('/')
        );
    }
});

Обзор HTML5 семантических элементов

ЭлементОписаниеИспользование
headerШапка страницыЛоготип, навигация
navНавигацияМеню, ссылки
mainОсновное содержимоеПервичный контент
sectionРазделТематические блоки
articleСтатьяСамостоятельные материалы
asideБоковая панельВспомогательная информация
footerПодвал страницыКопирайт, ссылки

Техники CSS3 макетирования

Flexbox

.container {
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
}

.item {
    flex: 1 1 300px;
    margin: 1rem;
}

Grid

.grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: 2rem;
}

Container Queries

@container (min-width: 400px) {
    .card {
        display: grid;
        grid-template-columns: 1fr 2fr;
    }
}

Возможности JavaScript ES6+

Arrow Functions

const add = (a, b) => a + b;
const users = data.map(item => item.name);

Template Literals

const message = `Hallo ${name}, du hast ${count} Nachrichten`;

Destructuring

const {name, email} = user;
const [first, second] = array;

Spread/Rest

const newArray = [...oldArray, newItem];
const sum = (...numbers) => numbers.reduce((a, b) => a + b);

Async/Await

async function fetchData() {
    try {
        const response = await fetch('/api/data');
        const data = await response.json();
        return data;
    } catch (error) {
        console.error('Error:', error);
    }
}

Адаптивный дизайн. Точки перелома

/* Мобильный подход в первую очередь */
/* Небольшие телефоны */
@media (min-width: 320px) { }

/* Большие телефоны */
@media (min-width: 480px) { }

/* Планшеты */
@media (min-width: 768px) { }

/* Десктоп */
@media (min-width: 1024px) { }

/* Большой десктоп */
@media (min-width: 1200px) { }

Методы манипуляции DOM

МетодОписаниеПример
querySelectorВыбрать элементdocument.querySelector('.class')
createElementСоздать элементdocument.createElement('div')
appendChildДобавить элементparent.appendChild(child)
classListУправлять классамиelement.classList.add('active')
addEventListenerПривязать событиеelement.addEventListener('click', handler)
setAttributeУстановить атрибутelement.setAttribute('data-id', '123')

CSS3 анимации

Transitions

.button {
    transition: all 0.3s ease-in-out;
}

.button:hover {
    transform: translateY(-2px);
    box-shadow: 0 4px 8px rgba(0,0,0,0.2);
}

Keyframes

@keyframes fadeIn {
    from { opacity: 0; transform: translateY(20px); }
    to { opacity: 1; transform: translateY(0); }
}

.element {
    animation: fadeIn 0.6s ease-out;
}

Оптимизация производительности веб-приложений

Lazy Loading

// Images
const images = document.querySelectorAll('img[data-src]');
const imageObserver = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
        if (entry.isIntersecting) {
            const img = entry.target;
            img.src = img.dataset.src;
            img.removeAttribute('data-src');
            imageObserver.unobserve(img);
        }
    });
});

images.forEach(img => imageObserver.observe(img));

Code Splitting

// Dynamic Import
const loadModule = async () => {
    const module = await import('./heavy-module.js');
    module.doSomething();
};

Лучшие практики доступности

Семантическая разметка

<nav aria-label="Hauptnavigation">
    <ul>
        <li><a href="#" aria-current="page">Home</a></li>
        <li><a href="#">About</a></li>
    </ul>
</nav>

Атрибуты ARIA

<button aria-expanded="false" aria-controls="menu">
    Menü
</button>
<div id="menu" hidden>
    <!-- Menu content -->
</div>

Навигация с клавиатуры

.focusable:focus {
    outline: 2px solid #3b82f6;
    outline-offset: 2px;
}

Преимущества и недостатки

Преимущества современной веб-разработки

  • Кроссплатформенность: один код для всех устройств
  • Адаптивность: оптимальный вид на любом экране
  • Интерактивность: богатый пользовательский опыт
  • SEO-оптимизация: хорошие позиции в поисковых системах
  • Доступность: поддержка для пользователей с ограничениями

Недостатки

  • Сложность: много технологий к изучению
  • Совместимость браузеров: разная поддержка возможностей
  • Производительность: требуется постоянная оптимизация
  • Безопасность: необходимы защитные меры

Типичные вопросы на экзамене

  1. В чём различие между HTML, CSS и JavaScript? HTML структурирует содержимое, CSS определяет внешний вид, JavaScript добавляет интерактивность.

  2. Объясните адаптивный дизайн! Это подстройка интерфейса под разные размеры экранов с помощью Media Queries и гибких макетов.

  3. Что такое DOM и как его изменять? DOM это программируемое представление HTML-документа. Изменение происходит через JavaScript методы типа querySelector, createElement, appendChild.

  4. Когда применять Flexbox, а когда Grid? Flexbox используется для одномерных макетов (строки или колонки), Grid для двумерных макетов.

Основные источники информации

  1. https://developer.mozilla.org/de/docs/Web
  2. https://www.w3.org/TR/html5/
  3. https://www.w3.org/Style/CSS/

Рекомендуемая литература: Веб-разработка

Keine Bücher für Kategorie "web-development" gefunden.

Назад к блогу
Share:

Похожие статьи