← Назад к вопросам

Как определить какие теги можно вкладывать?

2.2 Middle🔥 181 комментариев
#Soft Skills и рабочие процессы

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Вложение HTML-тегов: правила и проверка

В HTML существуют строгие правила о том, какие теги можно вкладывать внутрь других. Это называется "content model" каждого элемента. Рассмотрим способы проверки допустимости вложений.

1. W3C Спецификация — источник истины

Каждый HTML-тег имеет определённый "content model":

// Примеры правил:
// <p> может содержать только inline элементы, НЕ может содержать <div>, <p>
// <div> может содержать block и inline элементы
// <ul> может содержать только <li> (или <script>, <template>)
// <button> может содержать только phrasing content, НЕ может содержать <div>

// Базовая типизация контента
const contentModels = {
  "p": "phrasing",           // только inline
  "div": "flow",             // block и inline
  "ul": "listItem",          // только <li>
  "button": "phrasing",      // только inline
  "form": "flow",            // block и inline
  "table": "tableContent",   // только <tr>/<caption>/<colgroup>
  "thead": "tableSection",   // только <tr>
  "tr": "tableCell",         // только <td>/<th>
  "select": "option",        // только <option>/<optgroup>
  "datalist": "option"       // только <option>
};

2. Программная проверка в JavaScript

Создай утилиту для валидации вложений:

// Функция проверки допустимости вложения
function isValidChild(parentTag, childTag) {
  const rules = {
    "p": { forbidden: ["address", "article", "aside", "blockquote", "details", "div", "dl", "fieldset", "figcaption", "figure", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "header", "hgroup", "hr", "main", "nav", "ol", "pre", "section", "table", "ul"] },
    "ul": { allowed: ["li", "script", "template"] },
    "ol": { allowed: ["li", "script", "template"] },
    "table": { allowed: ["caption", "colgroup", "tbody", "tfoot", "thead"] },
    "thead": { allowed: ["tr"] },
    "tbody": { allowed: ["tr"] },
    "tr": { allowed: ["td", "th"] },
    "button": { forbidden: ["a", "button", "form", "input", "select", "textarea"] },
    "label": { forbidden: ["label"] }
  };

  const rule = rules[parentTag];
  if (!rule) return true; // Неизвестный тег, разрешаем

  if (rule.allowed) {
    return rule.allowed.includes(childTag);
  }
  if (rule.forbidden) {
    return !rule.forbidden.includes(childTag);
  }
  return true;
}

// Использование
console.log(isValidChild("p", "div"));      // false - неправильно
console.log(isValidChild("div", "p"));      // true - правильно
console.log(isValidChild("ul", "li"));      // true - правильно
console.log(isValidChild("ul", "div"));     // false - неправильно
console.log(isValidChild("button", "a"));   // false - неправильно

3. Валидация DOM при вставке элемента

function validateAndInsertElement(parentElement, childElement) {
  const parentTag = parentElement.tagName.toLowerCase();
  const childTag = childElement.tagName.toLowerCase();

  if (!isValidChild(parentTag, childTag)) {
    console.warn(`Warning: <${childTag}> cannot be placed inside <${parentTag}>`);
    
    // Вариант 1: Отказать
    throw new Error(`Invalid nesting: <${childTag}> in <${parentTag}>`);
    
    // Вариант 2: Авто-исправить (вставить после родителя)
    // parentElement.parentElement.insertBefore(childElement, parentElement.nextSibling);
  }

  parentElement.appendChild(childElement);
}

// Пример с React
import { useEffect } from "react";

function validateReactChildren(tag, children) {
  // Проверка типов для React компонентов
  React.Children.forEach(children, child => {
    if (!React.isValidElement(child)) return;
    
    const childTag = child.type?.toLowerCase?.();
    if (!isValidChild(tag, childTag)) {
      console.error(`Invalid children: <${childTag}> inside <${tag}>`);
    }
  });
}

4. TypeScript типизация допустимых детей

// Строгая типизация в TypeScript
type FlowContent = "p" | "div" | "section" | "article" | "ul" | "ol" | "form";
type PhrasingContent = "span" | "a" | "strong" | "em" | "button" | "input";
type ListItemContent = "li";

interface ContentModel {
  p: PhrasingContent[];
  div: FlowContent[];
  ul: ListItemContent[];
  button: PhrasingContent[];
}

// Компонент с типизованными детьми
interface DivProps {
  children: React.ReactElement<{ type: FlowContent }>;
}

function StrictDiv({ children }: DivProps) {
  // TypeScript проверит типы на компиляции
  return <div>{children}</div>;
}

5. Инструменты для проверки

// Браузер автоматически перемещает некорректные элементы
// Пример: если вставить <div> в <p>, браузер автоматически закроет <p>
const p = document.createElement("p");
const div = document.createElement("div");
p.appendChild(div); // Браузер автоматически переместит div после p

console.log(p.innerHTML); // "" - div был удалён!

// Проверить свежесть через DOM API
function checkValidation(element) {
  const parent = element.parentElement;
  if (!parent) return true;
  
  // После вставки проверить остался ли элемент на месте
  const originalParent = element.parentElement;
  return originalParent === parent;
}

Рекомендация

Используй комбинацию:

  1. Изучи W3C спецификацию для основных правил
  2. Реализуй isValidChild() функцию в проекте
  3. Используй TypeScript для типизации
  4. Доверяй браузеру — он сам исправит некорректные вложения
  5. В React проверяй детей через React.Children.forEach()