Skip to content
wannacode

Text, links, images, tables

In short: The content of an HTML page is made of headings h1–h6, paragraphs, lists, links with href, images with alt, and data tables with caption, th and scope.

Problem

"Milk" leads nowhere

A customer writes that on the bread page the link "Milk" leads to "No such page". On the home page the same menu works. The page is at https://shop.example/bread/white/dobrudzha.

HTML, CSS, JS
index.html

Click "Milk". The sandbox is a single page and does not open links, but the console says what is in href: milk. The other links start with /, and this one does not. Without / the browser does not go to /milk. It builds the address from the address of the page you are on, and gets a different one.

The idea

Full and relative addresses

A link is <a> with the attribute href: where it leads. The address in href (a URL) is either full or relative.

A full address has everything: the scheme (https://), the host (shop.example) and the path (/bread/white/dobrudzha). The path is made of folders separated by /, and the last part is the page.

A relative address is incomplete, and the browser completes it from the address of the page the link is on. The start decides how:

  • milk (nothing in front): from the folder of the page.
  • ../milk: one folder up.
  • /milk: from the root of the site, whatever the page.
  • #price: a place in the same page, the element with id="price".

That is why the menu, which is on every page, uses addresses from the root: /milk leads to the same place from everywhere. You write a full address only for another site.

See it

Watch the address being built

Five links on the same page. Watch which parts of the address at the top the browser keeps and what it adds.

<!-- the address of the page: https://shop.example/bread/white/dobrudzha -->
<a href="milk">Milk</a>
<a href="../milk">Milk</a>
<a href="/milk">Milk</a>
<a href="#price">The price</a>
<a href="https://recipes.example/bread">Recipes</a>
Step by step, in words
  1. The page lives at https://shop.example/bread/white/dobrudzha. The browser splits the address into parts: the scheme, the host and the path. The path is made of folders, and the last part is the page itself.
  2. href="milk" has nothing in front. The browser keeps the folder of the page, /bread/white/, and replaces only the last part. It gets /bread/white/milk: a page that does not exist.
  3. ../ means one folder up. From /bread/white/ the browser goes up to /bread/ and adds milk. It is still not /milk.
  4. A slash in front means from the root of the site. The browser keeps only the scheme and the host. This address works from every page of the shop.
  5. # is a place in the same page. The browser keeps the whole address and adds #price. The page is not loaded again: it scrolls to the element with id="price".
  6. An address with a scheme is full. The browser takes nothing from the page. This is how you link to another site.
  7. The start of href decides: nothing or ../ counts from the folder, / from the root, # stays on the page, https:// is a full address.

The browser does not ask the server whether the address exists. It builds the address by the rules and goes there. That is why a mistake in href does not show in the code, but only when someone clicks the link. In the browser you see the built address when you hold the mouse over a link: bottom left of the window.

Try it

Fix the links

Fix the links: "Milk" and "Cart" must work from every page of the shop (an address from the root: /milk and /cart). "To the price" must jump to the paragraph with the price. "Recipes with this bread" goes to another site: make it open in a new tab without that site getting access to our page.

HTML, CSS, JS

Try it

Fill in

The link has to jump to the price on the same page. What goes in href?

<a href="">To the price</a>
…
<p id="price">Price: €1.80</p>

The idea

Text: outline, paragraphs, lists

The headings <h1> … <h6> are not six font sizes. They are the outline of the page, like the table of contents of a book. <h1> is the title of the page, <h2> are its sections, <h3> are parts of a section. Levels are not skipped: after <h2> comes <h3>, not <h5>. A screen reader shows this outline as a list, and a person jumps from section to section. The size is changed with CSS, without touching the outline.

The rest:

  • <p> is a paragraph. <br> is a new line inside a paragraph (an address, a poem), not a way to make lists.
  • <ul> is a list without order (ingredients), <ol> has an order (steps). Each item is <li>.
  • <dl> is a list of "name and value" pairs: <dt> is the name (Weight), <dd> is the value (650 g).
  • <strong> is important ("Contains gluten."), and <em> is stress that changes the meaning ("in a cloth bag, not in plastic"). If you only want bold looks without meaning, that is CSS.
HTML, CSS, JS
index.html

Try it

Make an outline

A colleague picked the headings by size: the bread name is an <h4> and the offer is an <h1>. Make the outline: the product name is the title of the page, "Ingredients" and "How to keep it" are the sections. The offer is not a heading: make it important text in a paragraph. The ingredients are a list.

HTML, CSS, JS
index.html

The idea

Images

<img src="img/bread.svg" alt="A round white loaf with a crisp crust" width="240" height="240">
  • src is the address of the image. It is built like href.
  • alt is the text instead of the image. A screen reader reads it, a search engine indexes it, and if the image does not load, the browser shows it in its place. Write what the image shows and why it is there.
  • An image that is only decoration, or repeats the text next to it, gets alt="". Then the screen reader skips it. No alt at all is a mistake: the reader does not know it is decoration.
  • An image that is the only content of a link gives the link its name. The alt is where the link leads: "The shop", not "logo".
  • width and height are the size in pixels. While the image is on its way, the browser does not know how big it is and gives it 0 pixels of height. When it arrives, the text below jumps down. With width and height the browser keeps the place in advance, even when CSS makes it smaller.

Try it

Add alt and sizes

The logo is the only thing in the link to the home page: the link must be called "The shop". The photo of the bread shows something: describe it in alt. The gift icon repeats the text next to it. The photo is 240 × 240 pixels: tell the browser.

HTML, CSS, JS
index.html

A screen reader says only "link" for one link. Which line has the mistake?

The idea

Data tables

A table is for data in rows and columns: prices, orders, a timetable. A screen reader reads it as a table: "row 3, column Price, €7.90". That is why it is only for data, and not for laying out a page in columns. CSS does that.

<table>
<caption>Prices today</caption>
<thead>
<tr><th scope="col">Product</th><th scope="col">Price</th></tr>
</thead>
<tbody>
<tr><th scope="row">White cheese</th><td>€7.90</td></tr>
</tbody>
</table>
  • <caption> is the name of the table. It is inside the table, not a heading above it.
  • <tr> is a row. <td> is a data cell, and <th> is a header cell.
  • scope="col" says the header is for the column below it, and scope="row" for the row to its right.
  • <thead> holds the header rows, <tbody> the data, <tfoot> the sum at the bottom.

Try it

The price list

The price list is made of <td> cells and bold text. Make it a data table: the name "Prices today" belongs to the table, the header row is in <thead>, and the products are in <tbody>. The column headers and the product names are header cells; use scope to say what each one is for.

HTML, CSS, JS
index.html
More precisely, if you are curious

More about addresses. ./milk is the same as milk. ?page=2 keeps the path and changes only the query: /bread/white/dobrudzha?page=2. An address that starts with // keeps only the scheme: //cdn.example/x.js is https://cdn.example/x.js. ../ above the root goes nowhere higher: ../../../milk from our page is /milk. In JavaScript this building is new URL('../milk', 'https://shop.example/bread/white/dobrudzha').

target="_blank" and window.opener. Chromium already gives a new tab from target="_blank" window.opener === null, the same as with rel="noopener", and rel="opener" brings the link back. We write rel="noopener" so that it is clear in browsers that do not do it by themselves.

Images for different screens. srcset gives several sizes of one image, and the browser picks one for the screen. loading="lazy" loads the image when it gets close to the screen. Both belong to the CSS module and to speed.

Merged cells. colspan="3" merges three cells in a row, and rowspan="2" two cells in a column. Then scope matters: without it the browser guesses which cells the header is for.

<b>, <i> and <br>. They exist and have a meaning: <b> is text that stands out without being more important (a keyword), <i> is a different voice (a term, the title of a book). For important text there is <strong>, for stress there is <em>.

Exercise

Order 1042

Build the page of order 1042 from scratch. A menu with a link "My orders" to /orders. A heading "Order 1042", and under it "Status: shipped". Then a table "The products in the order" with the columns Product, Quantity, Price, Sum: Dobrudzha bread 2 × €1.80 = €3.60, fresh milk 3% 1 × €2.40 = €2.40, white cheese 1 × €7.90 = €7.90. In front of each name there is a 32 × 32 image (img/bread.svg, img/milk.svg, img/cheese.svg). At the bottom is the row "Total" with €13.90 in the last column.

HTML, CSS, JS
index.html

Test

Check yourself

1. The page is at https://shop.example/bread/white/dobrudzha. Where does <a href="../milk"> lead?
2. The product name is too big as an <h1>. What do you do?
3. Next to the text "A gift with orders over €30" there is a gift icon. What alt does it need?
4. Why write width and height on <img> when the image shows without them?
5. When is <table> the right element?

Made by Ivo Gergov as part of Evo Tech's campaign for accessible programming education ·