Lists
{{LearnSidebar}}
{{PreviousMenuNext("Learn_web_development/Core/Structuring_content/Emphasis_and_importance", "Learn_web_development/Core/Structuring_content/Structuring_documents", "Learn_web_development/Core/Structuring_content")}}
Now let’s turn our attention to lists. Lists are everywhere in life—from your shopping list to the list of directions you subconsciously follow to get to your house every day, to the lists of instructions you are following in these tutorials! It may not surprise you that HTML has a convenient set of elements that allows us to define different types of list. On the web, we have three types of lists: unordered, ordered, and description lists. This lesson shows you how to use the different types.
Prerequisites: | Basic HTML familiarity, as covered in Basic HTML Syntax. |
---|---|
Learning outcomes: |
|
Unordered lists
Unordered lists are used to mark up lists of items for which the order of the items doesn’t matter. Let’s take a shopping list as an example:
milk
eggs
bread
hummus
Every unordered list starts off with a {{htmlelement("ul")}}
element—this wraps around all the list items:
<ul>
milk
eggs
bread
hummus
</ul>
The last step is to wrap each list item in a {{htmlelement("li")}}
(list item) element:
<ul>
<li>milk</li>
<li>eggs</li>
<li>bread</li>
<li>hummus</li>
</ul>
Active learning: Marking up an unordered list
Try editing the live sample below to create your very own HTML unordered list.
<h2>Live output</h2>
<div class="output" style="min-height: 50px;"></div>
<h2>Editable code</h2>
<p class="a11y-label">
Press Esc to move focus away from the code area (Tab inserts a tab character).
</p>
<textarea id="code" class="input" style="min-height: 100px; width: 95%">
milk
eggs
bread
hummus
</textarea>
<div class="playable-buttons">
<input id="reset" type="button" value="Reset" />
<input id="solution" type="button" value="Show solution" />
</div>
html {
font-family: sans-serif;
}
h2 {
font-size: 16px;
}
.a11y-label {
margin: 0;
text-align: right;
font-size: 0.7rem;
width: 98%;
}
body {
margin: 10px;
background: #f5f9fa;
}
const textarea = document.getElementById("code");
const reset = document.getElementById("reset");
const solution = document.getElementById("solution");
const output = document.querySelector(".output");
const code = textarea.value;
let userEntry = textarea.value;
function updateCode() {
output.innerHTML = textarea.value;
}
const htmlSolution =
"<ul>\n<li>milk</li>\n<li>eggs</li>\n<li>bread</li>\n<li>hummus</li>\n</ul>";
let solutionEntry = htmlSolution;
reset.addEventListener("click", () => {
textarea.value = code;
userEntry = textarea.value;
solutionEntry = htmlSolution;
solution.value = "Show solution";
updateCode();
});
solution.addEventListener("click", () => {
if (solution.value === "Show solution") {
textarea.value = solutionEntry;
solution.value = "Hide solution";
} else {
textarea.value = userEntry;
solution.value = "Show solution";
}
updateCode();
});
textarea.addEventListener("input", updateCode);
window.addEventListener("load", updateCode);
// stop tab key tabbing out of textarea and
// make it write a tab at the caret position instead
textarea.onkeydown = (e) => {
if (e.code === "Tab") {
e.preventDefault();
insertAtCaret("\t");
}
if (e.code === "Escape") {
textarea.blur();
}
};
function insertAtCaret(text) {
const scrollPos = textarea.scrollTop;
let caretPos = textarea.selectionStart;
const front = textarea.value.substring(0, caretPos);
const back = textarea.value.substring(
textarea.selectionEnd,
textarea.value.length,
);
textarea.value = front + text + back;
caretPos += text.length;
textarea.selectionStart = caretPos;
textarea.selectionEnd = caretPos;
textarea.focus();
textarea.scrollTop = scrollPos;
}
// Update the saved userCode every time the user updates the text area code
textarea.onkeyup = () => {
// We only want to save the state when the user code is being shown,
// not the solution, so that solution is not saved over the user code
if (solution.value === "Show solution") {
userEntry = textarea.value;
} else {
solutionEntry = textarea.value;
}
updateCode();
};
{{ EmbedLiveSample('Active_learning_Marking_up_an_unordered_list', 700, 400, "", "") }}
Ordered
Ordered lists are lists in which the order of the items does matter. Let’s take a set of directions as an example:
Drive to the end of the road
Turn right
Go straight across the first two roundabouts
Turn left at the third roundabout
The school is on your right, 300 meters up the road
The markup structure is the same as for unordered lists, except that you have to wrap the list items in an {{htmlelement("ol")}}
element, rather than <ul>
:
<ol>
<li>Drive to the end of the road</li>
<li>Turn right</li>
<li>Go straight across the first two roundabouts</li>
<li>Turn left at the third roundabout</li>
<li>The school is on your right, 300 meters up the road</li>
</ol>
Active learning: Marking up an ordered list
Try editing the live sample below to create your very own HTML ordered list.
<h2>Live output</h2>
<div class="output" style="min-height: 50px;"></div>
<h2>Editable code</h2>
<p class="a11y-label">
Press Esc to move focus away from the code area (Tab inserts a tab character).
</p>
<textarea id="code" class="input" style="min-height: 200px; width: 95%">
Drive to the end of the road
Turn right
Go straight across the first two roundabouts
Turn left at the third roundabout
The school is on your right, 300 meters up the road
</textarea>
<div class="playable-buttons">
<input id="reset" type="button" value="Reset" />
<input id="solution" type="button" value="Show solution" />
</div>
html {
font-family: sans-serif;
}
h2 {
font-size: 16px;
}
.a11y-label {
margin: 0;
text-align: right;
font-size: 0.7rem;
width: 98%;
}
body {
margin: 10px;
background: #f5f9fa;
}
const textarea = document.getElementById("code");
const reset = document.getElementById("reset");
const solution = document.getElementById("solution");
const output = document.querySelector(".output");
const code = textarea.value;
let userEntry = textarea.value;
function updateCode() {
output.innerHTML = textarea.value;
}
const htmlSolution =
"<ol>\n<li>Drive to the end of the road</li>\n<li>Turn right</li>\n<li>Go straight across the first two roundabouts</li>\n<li>Turn left at the third roundabout</li>\n<li>The school is on your right, 300 meters up the road</li>\n</ol>";
let solutionEntry = htmlSolution;
reset.addEventListener("click", () => {
textarea.value = code;
userEntry = textarea.value;
solutionEntry = htmlSolution;
solution.value = "Show solution";
updateCode();
});
solution.addEventListener("click", () => {
if (solution.value === "Show solution") {
textarea.value = solutionEntry;
solution.value = "Hide solution";
} else {
textarea.value = userEntry;
solution.value = "Show solution";
}
updateCode();
});
textarea.addEventListener("input", updateCode);
window.addEventListener("load", updateCode);
// stop tab key tabbing out of textarea and
// make it write a tab at the caret position instead
textarea.onkeydown = (e) => {
if (e.code === "Tab") {
e.preventDefault();
insertAtCaret("\t");
}
if (e.code === "Escape") {
textarea.blur();
}
};
function insertAtCaret(text) {
const scrollPos = textarea.scrollTop;
let caretPos = textarea.selectionStart;
const front = textarea.value.substring(0, caretPos);
const back = textarea.value.substring(
textarea.selectionEnd,
textarea.value.length,
);
textarea.value = front + text + back;
caretPos += text.length;
textarea.selectionStart = caretPos;
textarea.selectionEnd = caretPos;
textarea.focus();
textarea.scrollTop = scrollPos;
}
// Update the saved userCode every time the user updates the text area code
textarea.onkeyup = () => {
// We only want to save the state when the user code is being shown,
// not the solution, so that solution is not saved over the user code
if (solution.value === "Show solution") {
userEntry = textarea.value;
} else {
solutionEntry = textarea.value;
}
updateCode();
};
{{ EmbedLiveSample('Active_learning_Marking_up_an_ordered_list', 700, 500, "", "") }}
Active learning: Marking up our recipe page
So at this point in the article, you have all the information you need to mark up our recipe page example. You can choose to either save a local copy of our text-start.html starting file and do the work there or do it in the editable example below. Doing it locally will probably be better, as then you’ll get to save the work you are doing, whereas if you fill it in to the editable example, it will be lost the next time you open the page. Both have pros and cons.
<h2>Live output</h2>
<div class="output" style="min-height: 50px;"></div>
<h2>Editable code</h2>
<p class="a11y-label">
Press Esc to move focus away from the code area (Tab inserts a tab character).
</p>
<textarea id="code" class="input" style="min-height: 200px; width: 95%">
Quick hummus recipe
This recipe makes quick, tasty hummus, with no messing. It has been adapted from a number of different recipes that I have read over the years.
Hummus is a delicious thick paste used heavily in Greek and Middle Eastern dishes. It is very tasty with salad, grilled meats and pitta breads.
Ingredients
1 can (400g) of chick peas (garbanzo beans)
175g of tahini
6 sundried tomatoes
Half a red pepper
A pinch of cayenne pepper
1 clove of garlic
A dash of olive oil
Instructions
Remove the skin from the garlic, and chop coarsely
Remove all the seeds and stalk from the pepper, and chop coarsely
Add all the ingredients into a food processor
Process all the ingredients into a paste
If you want a coarse "chunky" hummus, process it for a short time
If you want a smooth hummus, process it for a longer time
For a different flavor, you could try blending in a small measure of lemon and coriander, chili pepper, lime and chipotle, harissa and mint, or spinach and feta cheese. Experiment and see what works for you.
Storage
Refrigerate the finished hummus in a sealed container. You should be able to use it for about a week after you've made it. If it starts to become fizzy, you should definitely discard it.
Hummus is suitable for freezing; you should thaw it and use it within a couple of months.
</textarea>
<div class="playable-buttons">
<input id="reset" type="button" value="Reset" />
<input id="solution" type="button" value="Show solution" />
</div>
html {
font-family: sans-serif;
}
h2 {
font-size: 16px;
}
.a11y-label {
margin: 0;
text-align: right;
font-size: 0.7rem;
width: 98%;
}
body {
margin: 10px;
background: #f5f9fa;
}
const textarea = document.getElementById("code");
const reset = document.getElementById("reset");
const solution = document.getElementById("solution");
const output = document.querySelector(".output");
const code = textarea.value;
let userEntry = textarea.value;
function updateCode() {
output.innerHTML = textarea.value;
}
const htmlSolution =
'<h1>Quick hummus recipe</h1>\n\n<p>This recipe makes quick, tasty hummus, with no messing. It has been adapted from a number of different recipes that I have read over the years.</p>\n\n<p>Hummus is a delicious thick paste used heavily in Greek and Middle Eastern dishes. It is very tasty with salad, grilled meats and pitta breads.</p>\n\n<h2>Ingredients</h2>\n\n<ul>\n<li>1 can (400g) of chick peas (garbanzo beans)</li>\n<li>175g of tahini</li>\n<li>6 sundried tomatoes</li>\n<li>Half a red pepper</li>\n<li>A pinch of cayenne pepper</li>\n<li>1 clove of garlic</li>\n<li>A dash of olive oil</li>\n</ul>\n\n<h2>Instructions</h2>\n\n<ol>\n<li>Remove the skin from the garlic, and chop coarsely.</li>\n<li>Remove all the seeds and stalk from the pepper, and chop coarsely.</li>\n<li>Add all the ingredients into a food processor.</li>\n<li>Process all the ingredients into a paste.</li>\n<li>If you want a coarse "chunky" hummus, process it for a short time.</li>\n<li>If you want a smooth hummus, process it for a longer time.</li>\n</ol>\n\n<p>For a different flavor, you could try blending in a small measure of lemon and coriander, chili pepper, lime and chipotle, harissa and mint, or spinach and feta cheese. Experiment and see what works for you.</p>\n\n<h2>Storage</h2>\n\n<p>Refrigerate the finished hummus in a sealed container. You should be able to use it for about a week after you\'ve made it. If it starts to become fizzy, you should definitely discard it.</p>\n\n<p>Hummus is suitable for freezing; you should thaw it and use it within a couple of months.</p>';
let solutionEntry = htmlSolution;
reset.addEventListener("click", () => {
textarea.value = code;
userEntry = textarea.value;
solutionEntry = htmlSolution;
solution.value = "Show solution";
updateCode();
});
solution.addEventListener("click", () => {
if (solution.value === "Show solution") {
textarea.value = solutionEntry;
solution.value = "Hide solution";
} else {
textarea.value = userEntry;
solution.value = "Show solution";
}
updateCode();
});
textarea.addEventListener("input", updateCode);
window.addEventListener("load", updateCode);
// stop tab key tabbing out of textarea and
// make it write a tab at the caret position instead
textarea.onkeydown = (e) => {
if (e.code === "Tab") {
e.preventDefault();
insertAtCaret("\t");
}
if (e.code === "Escape") {
textarea.blur();
}
};
function insertAtCaret(text) {
const scrollPos = textarea.scrollTop;
let caretPos = textarea.selectionStart;
const front = textarea.value.substring(0, caretPos);
const back = textarea.value.substring(
textarea.selectionEnd,
textarea.value.length,
);
textarea.value = front + text + back;
caretPos += text.length;
textarea.selectionStart = caretPos;
textarea.selectionEnd = caretPos;
textarea.focus();
textarea.scrollTop = scrollPos;
}
// Update the saved userCode every time the user updates the text area code
textarea.onkeyup = () => {
// We only want to save the state when the user code is being shown,
// not the solution, so that solution is not saved over the user code
if (solution.value === "Show solution") {
userEntry = textarea.value;
} else {
solutionEntry = textarea.value;
}
updateCode();
};
{{ EmbedLiveSample('Active_learning_Marking_up_our_recipe_page', 900, 620, "", "") }}
If you get stuck, you can always press the Show solution button, or check out our text-complete.html example on our GitHub repo.
Nesting lists
It is perfectly OK to nest one list inside another one. You might want to have some sub-bullets sitting below a top-level bullet. Let’s take the second list from our recipe example:
<ol>
<li>Remove the skin from the garlic, and chop coarsely.</li>
<li>Remove all the seeds and stalk from the pepper, and chop coarsely.</li>
<li>Add all the ingredients into a food processor.</li>
<li>Process all the ingredients into a paste.</li>
<li>If you want a coarse "chunky" hummus, process it for a short time.</li>
<li>If you want a smooth hummus, process it for a longer time.</li>
</ol>
Since the last two bullets are very closely related to the one before them (they read like sub-instructions or choices that fit below that bullet), it might make sense to nest them inside their own unordered list and put that list inside the current fourth bullet. This would look like so:
<ol>
<li>Remove the skin from the garlic, and chop coarsely.</li>
<li>Remove all the seeds and stalk from the pepper, and chop coarsely.</li>
<li>Add all the ingredients into a food processor.</li>
<li>
Process all the ingredients into a paste.
<ul>
<li>
If you want a coarse "chunky" hummus, process it for a short time.
</li>
<li>If you want a smooth hummus, process it for a longer time.</li>
</ul>
</li>
</ol>
Try going back to the previous active learning example and updating the second list like this.
Description lists
The purpose of description lists is to mark up a set of items and their associated descriptions, such as terms and definitions, or questions and answers. Let’s look at an example of a set of terms and definitions:
soliloquy
In drama, where a character speaks to themselves, representing their inner thoughts or feelings and in the process relaying them to the audience (but not to other characters.)
monologue
In drama, where a character speaks their thoughts out loud to share them with the audience and any other characters present.
aside
In drama, where a character shares a comment only with the audience for humorous or dramatic effect. This is usually a feeling, thought or piece of additional background information
Description lists use a different wrapper than the other list types — {{htmlelement("dl")}}
; in addition each term is wrapped in a {{htmlelement("dt")}}
(description term) element, and each description is wrapped in a {{htmlelement("dd")}}
(description definition) element.
Description list example
Let’s finish marking up our example:
<dl>
<dt>soliloquy</dt>
<dd>
In drama, where a character speaks to themselves, representing their inner
thoughts or feelings and in the process relaying them to the audience (but
not to other characters.)
</dd>
<dt>monologue</dt>
<dd>
In drama, where a character speaks their thoughts out loud to share them
with the audience and any other characters present.
</dd>
<dt>aside</dt>
<dd>
In drama, where a character shares a comment only with the audience for
humorous or dramatic effect. This is usually a feeling, thought, or piece of
additional background information.
</dd>
</dl>
The browser default styles will display description lists with the descriptions indented somewhat from the terms.
{{EmbedLiveSample('Description_list_example', '100%', '285px')}}
Multiple descriptions for one term
Note that it is permitted to have a single term with multiple descriptions, for example:
<dl>
<dt>aside</dt>
<dd>
In drama, where a character shares a comment only with the audience for
humorous or dramatic effect. This is usually a feeling, thought, or piece of
additional background information.
</dd>
<dd>
In writing, a section of content that is related to the current topic, but
doesn't fit directly into the main flow of content so is presented nearby
(often in a box off to the side.)
</dd>
</dl>
{{EmbedLiveSample('Multiple_descriptions_for_one_term', '100%', '193px')}}
Active learning: Marking up a set of definitions
It’s time to try your hand at description lists; add elements to the raw text in the Input field so that it appears as a description list in the Output field. You could try using your own terms and descriptions if you like.
If you make a mistake, you can always reset it using the Reset button. If you get really stuck, press the Show solution button to see the answer.
<h2>Live output</h2>
<div class="output" style="min-height: 50px;"></div>
<h2>Editable code</h2>
<p class="a11y-label">
Press Esc to move focus away from the code area (Tab inserts a tab character).
</p>
<textarea id="code" class="input" style="min-height: 100px; width: 95%">
Bacon
The glue that binds the world together.
Eggs
The glue that binds the cake together.
Coffee
The drink that gets the world running in the morning.
A light brown color.
</textarea>
<div class="playable-buttons">
<input id="reset" type="button" value="Reset" />
<input id="solution" type="button" value="Show solution" />
</div>
html {
font-family: sans-serif;
}
h2 {
font-size: 16px;
}
.a11y-label {
margin: 0;
text-align: right;
font-size: 0.7rem;
width: 98%;
}
body {
margin: 10px;
background: #f5f9fa;
}
const textarea = document.getElementById("code");
const reset = document.getElementById("reset");
const solution = document.getElementById("solution");
const output = document.querySelector(".output");
const code = textarea.value;
let userEntry = textarea.value;
function updateCode() {
output.innerHTML = textarea.value;
}
const htmlSolution =
"<dl>\n <dt>Bacon</dt>\n <dd>The glue that binds the world together.</dd>\n <dt>Eggs</dt>\n <dd>The glue that binds the cake together.</dd>\n <dt>Coffee</dt>\n <dd>The drink that gets the world running in the morning.</dd>\n <dd>A light brown color.</dd>\n</dl>";
let solutionEntry = htmlSolution;
reset.addEventListener("click", () => {
textarea.value = code;
userEntry = textarea.value;
solutionEntry = htmlSolution;
solution.value = "Show solution";
updateCode();
});
solution.addEventListener("click", () => {
if (solution.value === "Show solution") {
textarea.value = solutionEntry;
solution.value = "Hide solution";
} else {
textarea.value = userEntry;
solution.value = "Show solution";
}
updateCode();
});
textarea.addEventListener("input", updateCode);
window.addEventListener("load", updateCode);
// stop tab key tabbing out of textarea and
// make it write a tab at the caret position instead
textarea.onkeydown = (e) => {
if (e.code === "Tab") {
e.preventDefault();
insertAtCaret("\t");
}
if (e.code === "Escape") {
textarea.blur();
}
};
function insertAtCaret(text) {
const scrollPos = textarea.scrollTop;
let caretPos = textarea.selectionStart;
const front = textarea.value.substring(0, caretPos);
const back = textarea.value.substring(
textarea.selectionEnd,
textarea.value.length,
);
textarea.value = front + text + back;
caretPos += text.length;
textarea.selectionStart = caretPos;
textarea.selectionEnd = caretPos;
textarea.focus();
textarea.scrollTop = scrollPos;
}
// Update the saved userCode every time the user updates the text area code
textarea.onkeyup = () => {
// We only want to save the state when the user code is being shown,
// not the solution, so that solution is not saved over the user code
if (solution.value === "Show solution") {
userEntry = textarea.value;
} else {
solutionEntry = textarea.value;
}
updateCode();
};
{{ EmbedLiveSample('Active_learning_Marking_up_a_set_of_definitions', 700, 350) }}
Test your skills!
You’ve reached the end of this set of three articles about basic HTML semantic elements, but can you remember the most important information? You can find some further tests to verify that you’ve retained this information before you move on — see Test your skills: HTML text basics.
Summary
That’s it for lists. Next we’ll move on to a higher-level discussion. We’ve shown how to implement some individual page features, but what about structuring a whole HTML page? Structuring documents is next.
{{PreviousMenuNext("Learn_web_development/Core/Structuring_content/Emphasis_and_importance", "Learn_web_development/Core/Structuring_content/Structuring_documents", "Learn_web_development/Core/Structuring_content")}}