🔥 Zapisy zamknięte, ale możesz pobrać Roadmapę .NET i dołączyć do listy oczekujących — Pobierz i dołącz do Listy VIP →

Anatomia znacznika HTML — Składnia, Atrybuty i Struktura Drzewa

Kąt widzenia: precyzyjne rozumienie składni HTML — elementy, atrybuty, zagnieżdżanie, void elements i DOM — z przykładami z kontekstu .NET (karty produktów, pola formularzy)
Przykład przewodni: karta produktu w sklepie i pole logowania


Element vs znacznik (tag) — ważna różnica

Te dwa słowa są często używane zamiennie, ale mają precyzyjne znaczenia:

Znacznik (tag) to pojedynczy fragment składni: <div>, </div>, <img>. Jest otoczony nawiasami kątowymi < i >.

Element to całość: znacznik otwierający + treść + znacznik zamykający.

<h1>Klawiatura mechaniczna</h1>
<!-- ↑ znacznik  ↑ treść         ↑ znacznik zamykający -->
<!-- całość to jeden element h1 -->

Analogia: w C# klasa ma nagłówek (public class Product) i ciało ({ ... }). Razem tworzą całą klasę. Tak samo <h1> i </h1> razem z treścią tworzą element.


Struktura elementu

Elementy z treścią (zawartością)

<p>Klawiatura mechaniczna Cherry MX Red, 10-keyless, podświetlenie RGB.</p>

Budowa:

  • <p> — znacznik otwierający (ang. opening tag)
  • tekst między nimi — treść (ang. content), może to być tekst lub inne elementy
  • </p> — znacznik zamykający (ang. closing tag) — ukośnik przed nazwą

Void elements — elementy bez treści

Niektóre elementy z definicji nie mają treści ani dzieci. Nigdy. Są to tzw. void elements:

<img src="/images/keyboard.jpg" alt="Klawiatura mechaniczna">
<br>
<hr>
<input type="text" name="search">
<meta charset="UTF-8">
<link rel="stylesheet" href="styles.css">

Nie mają znacznika zamykającego. Pisanie </img> lub <img /> to HTML5 toleruje (dla kompatybilności z XHTML), ale <img> to poprawna i zalecana forma.

Kompletna lista void elements: area, base, br, col, embed, hr, img, input, link, meta, param, source, track, wbr.

Różnica od XML/XHTML: w XML każdy element musi być zamknięty — <br/> lub <br></br>. W HTML5 void elements nie mają zamknięcia.


Atrybuty — dane o elemencie

Atrybuty dają dodatkowe informacje o elemencie. Umieszcza się je wewnątrz znacznika otwierającego, jako pary nazwa="wartość":

<img src="/images/keyboard.jpg" alt="Klawiatura mechaniczna" width="400" height="300">

Rozkład:

  • src — atrybut, wartość "/images/keyboard.jpg" — źródło obrazu
  • alt — atrybut, wartość "Klawiatura mechaniczna" — tekst alternatywny (dostępność, SEO)
  • width i height — wymiary w pikselach

Kilka zasad o atrybutach

Kolejność atrybutów nie ma znaczenia — przeglądarka traktuje je tak samo niezależnie od kolejności.

Wartości w cudzysłowach — technicznie HTML5 dopuszcza brak cudzysłowów dla prostych wartości, ale konwencja (i wymóg w Razor) to zawsze cudzysłowy:

<!-- Technicznie OK, ale zły styl -->
<img src=/images/keyboard.jpg alt=Klawiatura width=400>

<!-- Poprawnie -->
<img src="/images/keyboard.jpg" alt="Klawiatura" width="400">

Atrybuty logiczne (boolean) — nie mają wartości. Sama obecność atrybutu oznacza true:

<!-- Pole zaznaczone domyślnie -->
<input type="checkbox" name="newsletter" checked>

<!-- Pole wyłączone -->
<input type="text" name="promocode" disabled>

<!-- Pole wymagane -->
<input type="email" name="email" required>

checked, disabled, required, readonly, selected — to boolean attributes. checked="false" nie wyłącza checkboxa — żeby wyłączyć, usuń atrybut checked w ogóle.

Atrybuty globalne — działają na każdym elemencie

Kilka atrybutów jest dostępnych dla każdego elementu HTML:

<!-- id — unikalny identyfikator na stronie -->
<div id="product-42">...</div>

<!-- class — przypisanie do grupy elementów (CSS, JavaScript) -->
<div class="product-card featured">...</div>

<!-- style — style inline (unikaj w produkcji) -->
<p style="color: red;">Ostrzeżenie</p>

<!-- data-* — własne dane dla JavaScript -->
<div class="product-card" data-product-id="42" data-category="keyboards">...</div>

<!-- hidden — ukrywa element -->
<div hidden>Ten tekst jest niewidoczny</div>

<!-- title — tooltip po najechaniu -->
<abbr title="Graphical User Interface">GUI</abbr>

<!-- lang — język treści elementu -->
<p lang="en">This paragraph is in English.</p>

Atrybut data-* jest szczególnie ważny: pozwala osadzić dowolne dane w HTML, które JavaScript może odczytać bez dodatkowych żądań do serwera. W Razor możesz go generować z modelu:

<!-- Razor -->
<div class="product-card" data-product-id="@product.Id" data-in-stock="@product.IsInStock.ToString().ToLower()">
<!-- HTML output -->
<div class="product-card" data-product-id="42" data-in-stock="true">

Zagnieżdżanie — relacja rodzic–dziecko

HTML tworzy drzewo przez zagnieżdżanie elementów. Element zewnętrzny to rodzic, wewnętrzny to dziecko.

<div class="product-card">           <!-- rodzic -->
    <div class="product-image">      <!-- dziecko div, rodzic img -->
        <img src="/img/kb.jpg" alt="Klawiatura">  <!-- wnuk -->
    </div>
    <div class="product-info">       <!-- dziecko div -->
        <h2 class="product-name">Klawiatura mechaniczna</h2>  <!-- wnuk -->
        <p class="product-desc">Cherry MX Red, podświetlenie RGB.</p>
        <span class="price">299,00 zł</span>
    </div>
    <div class="product-actions">
        <a href="/products/42" class="btn btn-primary">Szczegóły</a>
        <button class="btn btn-secondary" data-product-id="42">Do koszyka</button>
    </div>
</div>

Kluczowa zasada: elementy muszą być prawidłowo zagnieżdżone — zamykasz dziecko przed rodzicem:

<!-- ❌ Błąd — overlapping elements -->
<p>Klawiatura <strong>mechaniczna</p></strong>

<!-- ✅ Poprawnie -->
<p>Klawiatura <strong>mechaniczna</strong></p>

Przeglądarka naprawi błąd zagnieżdżenia (HTML jest tolerancyjny), ale DOM może wyglądać inaczej niż zamierzałeś, co prowadzi do trudnych do debugowania problemów CSS.


Elementy blokowe vs inline

Każdy element HTML ma domyślny typ wyświetlania:

Block elements (blokowe): zajmują całą dostępną szerokość, zaczynają się w nowej linii. Przykłady: div, p, h1h6, ul, ol, section, header, footer, form.

Inline elements (liniowe): zajmują tylko tyle miejsca, ile potrzebuje ich treść. Nie łamią linii. Przykłady: span, a, strong, em, img, input, label, button.

<!-- Block — każdy div od nowej linii, pełna szerokość -->
<div>Produkt 1</div>
<div>Produkt 2</div>

<!-- Inline — span obok siebie w tej samej linii -->
Cena: <span class="old-price">349 zł</span> <span class="new-price">299 zł</span>

Ta właściwość jest definiowana przez CSS (wartość display), nie HTML — ale domyślne zachowanie elementów wynika z ich semantyki.


Komentarze w HTML

<!-- To jest komentarz HTML — przeglądarka go ignoruje -->

<!-- Komentarz wieloliniowy:
     Sekcja produktów — wyświetlana tylko zalogowanym użytkownikom
     Warunek sprawdzany po stronie serwera (Razor)
-->

Komentarze HTML są widoczne w źródle strony — widzi je każdy, kto wybierze “Pokaż źródło” w przeglądarce. Nigdy nie umieszczaj w komentarzach HTML wrażliwych informacji (kluczy API, haseł, wewnętrznych nazw endpointów). To jest błąd bezpieczeństwa.

W Razor masz komentarze Razor (@* ... *@), które NIE trafiają do wyjścia HTML:

@* Ten komentarz jest przetwarzany przez Razor — nie pojawi się w HTML *@
<!-- Ten komentarz POJAWI SIĘ w HTML wysłanym do przeglądarki -->

Znaki specjalne — HTML entities

Niektóre znaki mają specjalne znaczenie w HTML (<, >, &, ") i nie mogą być użyte bezpośrednio w treści. Używamy encji HTML:

ZnakEncjaUżycie
<&lt;Wyświetlenie nawiasu (np. w tekście o kodzie)
>&gt;j.w.
&&amp;Ampersand w tekście (np. “R&D”)
"&quot;Cudzysłów wewnątrz atrybutu
©&copy;Symbol copyright
&euro;Symbol euro
&nbsp;Twarda spacja (non-breaking space)
<p>Warunek: <code>if (price &lt; 100 &amp;&amp; isOnSale)</code></p>
<p>&copy; 2026 Dev-Hobby. Wszelkie prawa zastrzeżone.</p>
<footer>Cena: 299&nbsp;zł</footer>  <!-- &nbsp; zapobiega łamaniu "299 zł" -->

W Razor — Razor automatycznie enkoduje HTML w wyrażeniach @. Jeśli masz w modelu "<script>", to @Model.Name wyświetli &lt;script&gt; — bezpiecznie. Tylko @Html.Raw() wyłącza enkodowanie (i jest niebezpieczne, jeśli dane pochodzą od użytkownika).


Jak czytać HTML w DevTools

Najważniejsze narzędzie do debugowania widoków: F12 → zakładka Elements (Chrome) lub Inspector (Firefox).

DevTools pokazuje live DOM — nie surowy HTML z odpowiedzi, ale aktualny stan drzewa po modyfikacjach przez JavaScript. To jest źródło prawdy o tym, co przeglądarka faktycznie widzi.

Praktyczne użycie:

  • Kliknij prawym na element → Inspect → wiesz dokładnie jaki HTML odpowiada temu co widzisz
  • W panelu Elements możesz edytować atrybuty i treść na żywo — bez przeładowania
  • Zakładka Network → żądanie strony → Response — to surowy HTML z serwera (przed JavaScript)

Podsumowanie

PojęcieDefinicja
Tag<div> lub </div> — jeden ogranicznik
ElementKompletna struktura: <div>treść</div>
Void elementElement bez treści i zamknięcia: <img>, <input>
AtrybutPara nazwa="wartość" wewnątrz tagu otwierającego
Boolean attributeAtrybut bez wartości: disabled, required, checked
DOMDrzewo obiektów w pamięci przeglądarki zbudowane z HTML

Rozumienie tych elementarnych konceptów jest podstawą poprawnego pisania Razor Views i debugowania problemów z renderowaniem widoków ASP.NET Core.


Następny post: Semantyczne tagi HTML5 — struktura strony i znaczenie dla SEO i dostępności — które tagi używać zamiast <div> do tworzenia struktury strony i dlaczego ma to znaczenie dla wyszukiwarek i dostępności.

👨‍💻
Mariusz Jurczenko
Senior .NET Developer · 10+ lat doświadczenia komercyjnego

Programista .NET z doświadczeniem komercyjnym w firmach takich jak NFZ, Kamsoft, Diagnostyka, Hermes Reply Polska czy Etisoft Smart Solutions. Twórca kursów, z których skorzystało już ponad 11 000 osób w Strefie Kursów i ponad 1 000 kursantów na dev-hobby.pl.

Specjalizacja: Clean Code, Clean Architecture i uczenie programowania tak, żeby dało się je naprawdę zrozumieć — nie wykuć.

🚀 Co dalej?

Zobacz to w praktyce na wideo i pobierz darmową roadmapę, żeby ułożyć naukę w spójną ścieżkę do pierwszej pracy.

2 comments

Dodaj komentarz

czytanie to początek

Zamień wiedzę w umiejętności

Pobierz darmową Roadmapę .NET i ułóż takie tematy jak ten w spójną ścieżkę do pierwszej pracy.

Pobieram roadmapę →