← HTML & CSS/div и span: контейнеры#12 из 383← ПредыдущийСледующий →+10 XP
Полезно по теме:Гайд: старт в frontendПрактика: DOM и событияТермин: DOMМаршрут: старт с нуля

div и span: контейнеры

Семантические теги отлично описывают смысл контента. Но что делать когда тебе нужен просто контейнер для стилей или группировки без смысловой нагрузки? Для этого существуют <div> и <span> — нейтральные контейнеры без семантики.

div — блочный контейнер

<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> используют когда нет подходящего семантического тега, но нужно:

  • Сгруппировать элементы для CSS-стилей
  • Создать блок для JavaScript-манипуляций
  • Обернуть элементы для Flexbox или Grid-вёрстки
  • span — строчный контейнер

    <span> — строчный (inline) элемент. Он не начинается с новой строки, остаётся в потоке текста:

    <p>
      Цена: <span class="old-price">9 990 ₽</span>
      <span class="new-price">7 990 ₽</span>
      <span class="discount">-20%</span>
    </p>

    <span> используют для:

  • Выделения части текста цветом/стилем
  • Оборачивания слов для JavaScript
  • Иконок и значков внутри текста
  • Разница: блочные vs строчные элементы

    Блочные (div, p, h1, section):

  • Начинают с новой строки
  • Занимают всю доступную ширину
  • Можно задавать width, height, margin, padding
  • Строчные (span, a, strong, em):

  • Остаются в потоке текста
  • Занимают только нужное место
  • Нельзя задавать width и height напрямую
  • <!-- div переносит на новую строку -->
    <div>Блок 1</div><div>Блок 2</div>
    <!-- Результат: два блока, каждый на своей строке -->
    
    <!-- span остаётся в тексте -->
    <span>Часть 1</span><span>Часть 2</span>
    <!-- Результат: "Часть 1Часть 2" в одну строку -->

    Когда div/span, а когда семантические теги

    Используй семантические теги когда они есть:

    <!-- Плохо — 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> и <span> — нейтральные контейнеры без семантики.

    div — блочный контейнер

    <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> используют когда нет подходящего семантического тега, но нужно:

  • Сгруппировать элементы для CSS-стилей
  • Создать блок для JavaScript-манипуляций
  • Обернуть элементы для Flexbox или Grid-вёрстки
  • span — строчный контейнер

    <span> — строчный (inline) элемент. Он не начинается с новой строки, остаётся в потоке текста:

    <p>
      Цена: <span class="old-price">9 990 ₽</span>
      <span class="new-price">7 990 ₽</span>
      <span class="discount">-20%</span>
    </p>

    <span> используют для:

  • Выделения части текста цветом/стилем
  • Оборачивания слов для JavaScript
  • Иконок и значков внутри текста
  • Разница: блочные vs строчные элементы

    Блочные (div, p, h1, section):

  • Начинают с новой строки
  • Занимают всю доступную ширину
  • Можно задавать width, height, margin, padding
  • Строчные (span, a, strong, em):

  • Остаются в потоке текста
  • Занимают только нужное место
  • Нельзя задавать width и height напрямую
  • <!-- div переносит на новую строку -->
    <div>Блок 1</div><div>Блок 2</div>
    <!-- Результат: два блока, каждый на своей строке -->
    
    <!-- span остаётся в тексте -->
    <span>Часть 1</span><span>Часть 2</span>
    <!-- Результат: "Часть 1Часть 2" в одну строку -->

    Когда div/span, а когда семантические теги

    Используй семантические теги когда они есть:

    <!-- Плохо — 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.

    Загружаем среду выполнения...
    Загружаем AI-помощника...