Семантические теги отлично описывают смысл контента. Но что делать когда тебе нужен просто контейнер для стилей или группировки без смысловой нагрузки? Для этого существуют <div> и <span> — нейтральные контейнеры без семантики.
<div> (division) — блочный элемент. Он занимает всю ширину родителя и начинается с новой строки:
<div class="card">
<img src="product.jpg" alt="Товар" />
<h3>Nike Air Max</h3>
<p class="price">7 990 ₽</p>
<button>Купить</button>
</div><div> используют когда нет подходящего семантического тега, но нужно:
<span> — строчный (inline) элемент. Он не начинается с новой строки, остаётся в потоке текста:
<p>
Цена: <span class="old-price">9 990 ₽</span>
<span class="new-price">7 990 ₽</span>
<span class="discount">-20%</span>
</p><span> используют для:
Блочные (div, p, h1, section):
Строчные (span, a, strong, em):
<!-- div переносит на новую строку -->
<div>Блок 1</div><div>Блок 2</div>
<!-- Результат: два блока, каждый на своей строке -->
<!-- span остаётся в тексте -->
<span>Часть 1</span><span>Часть 2</span>
<!-- Результат: "Часть 1Часть 2" в одну строку -->Используй семантические теги когда они есть:
<!-- Плохо — div без смысла -->
<div class="header">
<div class="nav">...</div>
</div>
<!-- Хорошо — семантика -->
<header>
<nav>...</nav>
</header>Используй div/span когда нет подходящего тега:
<!-- Карточка товара — нет специального тега, div уместен -->
<div class="product-card">
<img src="..." alt="..." />
<div class="product-info">
<!-- Выделение скидки в тексте — span уместен -->
<span class="badge badge-sale">-30%</span>
<h3>Nike Air Max</h3>
</div>
</div><!-- Можно: блок внутри блока -->
<div>
<div>OK</div>
</div>
<!-- Можно: строчный внутри блока -->
<div>
<span>OK</span>
</div>
<!-- Нельзя: блок внутри строчного -->
<span>
<div>ПЛОХО!</div> <!-- Невалидный HTML -->
</span>
<!-- Нельзя: p внутри span -->
<span>
<p>ПЛОХО!</p> <!-- Невалидный HTML -->
</span>Ошибка 1: div-soup вместо семантики
<!-- Плохой HTML — всё в div -->
<div id="page">
<div id="header">
<div id="logo">Логотип</div>
<div id="menu">...</div>
</div>
<div id="content">...</div>
<div id="footer">...</div>
</div>Ошибка 2: span для блочных вещей
<!-- Span нельзя использовать как блочный контейнер -->
<span class="card">
<h3>Заголовок</h3> <!-- Невалидно -->
</span>Ошибка 3: Избыточные div
<!-- Лишний div — div ничего не добавляет -->
<section>
<div>
<h2>Заголовок</h2>
<p>Текст</p>
</div>
</section>
<!-- Проще -->
<section>
<h2>Заголовок</h2>
<p>Текст</p>
</section>В React каждый компонент возвращает JSX, который обычно оборачивается в <div> или <> (Fragment). React Fragment (пустые теги <></>) позволяет группировать без лишнего div. Понимание разницы блочных и строчных элементов критично для правильного применения Flexbox и Grid.
Разница между div (блочный) и span (строчный)
// Создаём div и span, сравниваем их поведение
const div = document.createElement('div')
div.textContent = 'Я блочный элемент'
div.style.background = '#eef'
const span = document.createElement('span')
span.textContent = 'Я строчный'
span.style.background = '#fee'
// display по умолчанию
console.log('div display по умолчанию: block (занимает всю строку)')
console.log('span display по умолчанию: inline (остаётся в тексте)')
// Карточка товара — реальный пример div
const card = document.createElement('div')
card.className = 'product-card'
const title = document.createElement('h3')
title.textContent = 'Nike Air Max 90'
// Строка с ценами — span для выделения частей
const priceRow = document.createElement('p')
const oldPrice = document.createElement('span')
oldPrice.textContent = '9 990 ₽'
oldPrice.className = 'old-price'
const newPrice = document.createElement('span')
newPrice.textContent = '7 990 ₽'
newPrice.className = 'new-price'
const discount = document.createElement('span')
discount.textContent = '-20%'
discount.className = 'badge'
priceRow.append(oldPrice, ' → ', newPrice, ' ', discount)
card.append(title, priceRow)
console.log('Карточка товара:', card.tagName)
console.log('Заголовок:', card.querySelector('h3').textContent)
console.log('Старая цена:', card.querySelector('.old-price').textContent)
console.log('Новая цена:', card.querySelector('.new-price').textContent)
console.log('Скидка:', card.querySelector('.badge').textContent)Проверка вложенности — валидные и невалидные комбинации
// Правила вложенности элементов
const rules = [
{ parent: 'div', child: 'div', valid: true, note: 'блок внутри блока' },
{ parent: 'div', child: 'span', valid: true, note: 'строчный внутри блока' },
{ parent: 'div', child: 'p', valid: true, note: 'абзац внутри блока' },
{ parent: 'span', child: 'span', valid: true, note: 'строчный внутри строчного' },
{ parent: 'span', child: 'div', valid: false, note: 'НЕЛЬЗЯ: блок внутри строчного' },
{ parent: 'span', child: 'p', valid: false, note: 'НЕЛЬЗЯ: блок внутри строчного' },
{ parent: 'p', child: 'span', valid: true, note: 'строчный внутри абзаца' },
{ parent: 'p', child: 'div', valid: false, note: 'НЕЛЬЗЯ: блок внутри абзаца' },
]
console.log('Правила вложенности HTML:')
rules.forEach(rule => {
const status = rule.valid ? 'OK ' : 'ОШИБКА'
console.log(status + ' | <' + rule.parent + '><' + rule.child + '> — ' + rule.note)
})
const validCount = rules.filter(r => r.valid).length
const invalidCount = rules.filter(r => !r.valid).length
console.log('Валидных: ' + validCount + ', Невалидных: ' + invalidCount)Семантические теги отлично описывают смысл контента. Но что делать когда тебе нужен просто контейнер для стилей или группировки без смысловой нагрузки? Для этого существуют <div> и <span> — нейтральные контейнеры без семантики.
<div> (division) — блочный элемент. Он занимает всю ширину родителя и начинается с новой строки:
<div class="card">
<img src="product.jpg" alt="Товар" />
<h3>Nike Air Max</h3>
<p class="price">7 990 ₽</p>
<button>Купить</button>
</div><div> используют когда нет подходящего семантического тега, но нужно:
<span> — строчный (inline) элемент. Он не начинается с новой строки, остаётся в потоке текста:
<p>
Цена: <span class="old-price">9 990 ₽</span>
<span class="new-price">7 990 ₽</span>
<span class="discount">-20%</span>
</p><span> используют для:
Блочные (div, p, h1, section):
Строчные (span, a, strong, em):
<!-- div переносит на новую строку -->
<div>Блок 1</div><div>Блок 2</div>
<!-- Результат: два блока, каждый на своей строке -->
<!-- span остаётся в тексте -->
<span>Часть 1</span><span>Часть 2</span>
<!-- Результат: "Часть 1Часть 2" в одну строку -->Используй семантические теги когда они есть:
<!-- Плохо — div без смысла -->
<div class="header">
<div class="nav">...</div>
</div>
<!-- Хорошо — семантика -->
<header>
<nav>...</nav>
</header>Используй div/span когда нет подходящего тега:
<!-- Карточка товара — нет специального тега, div уместен -->
<div class="product-card">
<img src="..." alt="..." />
<div class="product-info">
<!-- Выделение скидки в тексте — span уместен -->
<span class="badge badge-sale">-30%</span>
<h3>Nike Air Max</h3>
</div>
</div><!-- Можно: блок внутри блока -->
<div>
<div>OK</div>
</div>
<!-- Можно: строчный внутри блока -->
<div>
<span>OK</span>
</div>
<!-- Нельзя: блок внутри строчного -->
<span>
<div>ПЛОХО!</div> <!-- Невалидный HTML -->
</span>
<!-- Нельзя: p внутри span -->
<span>
<p>ПЛОХО!</p> <!-- Невалидный HTML -->
</span>Ошибка 1: div-soup вместо семантики
<!-- Плохой HTML — всё в div -->
<div id="page">
<div id="header">
<div id="logo">Логотип</div>
<div id="menu">...</div>
</div>
<div id="content">...</div>
<div id="footer">...</div>
</div>Ошибка 2: span для блочных вещей
<!-- Span нельзя использовать как блочный контейнер -->
<span class="card">
<h3>Заголовок</h3> <!-- Невалидно -->
</span>Ошибка 3: Избыточные div
<!-- Лишний div — div ничего не добавляет -->
<section>
<div>
<h2>Заголовок</h2>
<p>Текст</p>
</div>
</section>
<!-- Проще -->
<section>
<h2>Заголовок</h2>
<p>Текст</p>
</section>В React каждый компонент возвращает JSX, который обычно оборачивается в <div> или <> (Fragment). React Fragment (пустые теги <></>) позволяет группировать без лишнего div. Понимание разницы блочных и строчных элементов критично для правильного применения Flexbox и Grid.
Разница между div (блочный) и span (строчный)
// Создаём div и span, сравниваем их поведение
const div = document.createElement('div')
div.textContent = 'Я блочный элемент'
div.style.background = '#eef'
const span = document.createElement('span')
span.textContent = 'Я строчный'
span.style.background = '#fee'
// display по умолчанию
console.log('div display по умолчанию: block (занимает всю строку)')
console.log('span display по умолчанию: inline (остаётся в тексте)')
// Карточка товара — реальный пример div
const card = document.createElement('div')
card.className = 'product-card'
const title = document.createElement('h3')
title.textContent = 'Nike Air Max 90'
// Строка с ценами — span для выделения частей
const priceRow = document.createElement('p')
const oldPrice = document.createElement('span')
oldPrice.textContent = '9 990 ₽'
oldPrice.className = 'old-price'
const newPrice = document.createElement('span')
newPrice.textContent = '7 990 ₽'
newPrice.className = 'new-price'
const discount = document.createElement('span')
discount.textContent = '-20%'
discount.className = 'badge'
priceRow.append(oldPrice, ' → ', newPrice, ' ', discount)
card.append(title, priceRow)
console.log('Карточка товара:', card.tagName)
console.log('Заголовок:', card.querySelector('h3').textContent)
console.log('Старая цена:', card.querySelector('.old-price').textContent)
console.log('Новая цена:', card.querySelector('.new-price').textContent)
console.log('Скидка:', card.querySelector('.badge').textContent)Проверка вложенности — валидные и невалидные комбинации
// Правила вложенности элементов
const rules = [
{ parent: 'div', child: 'div', valid: true, note: 'блок внутри блока' },
{ parent: 'div', child: 'span', valid: true, note: 'строчный внутри блока' },
{ parent: 'div', child: 'p', valid: true, note: 'абзац внутри блока' },
{ parent: 'span', child: 'span', valid: true, note: 'строчный внутри строчного' },
{ parent: 'span', child: 'div', valid: false, note: 'НЕЛЬЗЯ: блок внутри строчного' },
{ parent: 'span', child: 'p', valid: false, note: 'НЕЛЬЗЯ: блок внутри строчного' },
{ parent: 'p', child: 'span', valid: true, note: 'строчный внутри абзаца' },
{ parent: 'p', child: 'div', valid: false, note: 'НЕЛЬЗЯ: блок внутри абзаца' },
]
console.log('Правила вложенности HTML:')
rules.forEach(rule => {
const status = rule.valid ? 'OK ' : 'ОШИБКА'
console.log(status + ' | <' + rule.parent + '><' + rule.child + '> — ' + rule.note)
})
const validCount = rules.filter(r => r.valid).length
const invalidCount = rules.filter(r => !r.valid).length
console.log('Валидных: ' + validCount + ', Невалидных: ' + invalidCount)Напиши HTML-карточку поста в социальной сети. Структура: div.post-card > div.post-header (span.username "@roadtojs" + span.date "5 марта 2024") + p.post-text (текст поста) + div.post-stats (три span: .likes "❤ 142", .comments "💬 28", .reposts "🔁 15"). Используй div для блочных групп и span для строчных элементов внутри текста.
div — для блочных контейнеров (post-header, post-stats). p — для текстового абзаца. span — для строчных элементов внутри блока (username, date, likes и т.д.). Нельзя вкладывать блочные элементы (div, p) внутри span.