feat(curriculum): add HTML video player project to FSD cert (#62866)

Co-authored-by: Jessica Wilkins <67210629+jdwilkin4@users.noreply.github.com>
Co-authored-by: Ilenia <26656284+ilenia-magoni@users.noreply.github.com>
Co-authored-by: Dario <105294544+Dario-DC@users.noreply.github.com>
Co-authored-by: Ilenia M <nethleen@gmail.com>
Co-authored-by: majestic-owl448 <26656284+majestic-owl448@users.noreply.github.com>
This commit is contained in:
Miguel T Rivera
2026-01-02 05:06:41 -08:00
committed by GitHub
parent cbb51972a9
commit 5772ad6a12
17 changed files with 850 additions and 0 deletions
+7
View File
@@ -7816,6 +7816,13 @@
"In these lectures, you will learn how to work with the <code>audio</code> and <code>video</code> elements."
]
},
"workshop-html-video-player": {
"title": "Build an HTML Video Player",
"intro": [
"In this workshop, you'll use HTML to create a basic video player.",
"This project will cover the <code>video</code> element, the video player setup, and more."
]
},
"lab-html-audio-and-video-player": {
"title": "Build an HTML Audio and Video Player",
"intro": [
@@ -0,0 +1,9 @@
---
title: Introduction to the Build an HTML Video Player
block: workshop-html-video-player
superBlock: responsive-web-design-v9
---
## Introduction to the Build an HTML Video Player
In this workshop, you'll use HTML to create a basic video player. This project will cover the `video` element, the video player setup, and more.
@@ -0,0 +1,48 @@
---
id: 68f18724d71db1dda8d6fec7
title: Step 1
challengeType: 0
dashedName: step-1
demoType: onLoad
---
# --description--
In this workshop, you will build an HTML video player. The HTML boilerplate has been provided for you.
Create an `h1` element and give it the text `Working with the HTML Video Element`.
# --hints--
You should have an `h1` element.
```js
assert.exists(document.querySelector('h1'));
```
Your `h1` element should contain the text `Working with the HTML Video Element`.
```js
const h1 = document.querySelector('h1');
assert.strictEqual(h1?.textContent.trim(), 'Working with the HTML Video Element');
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Working with the HTML Video Element</title>
</head>
<body>
--fcc-editable-region--
--fcc-editable-region--
</body>
</html>
```
@@ -0,0 +1,45 @@
---
id: 68f1eb9ad0a7575e4dbc31d5
title: Step 2
challengeType: 0
dashedName: step-2
---
# --description--
Next, create a `video` element below the `h1`. Over the next few steps, you will add the necessary attributes to make the video player functional.
# --hints--
You should have a `video` element.
```js
assert.exists(document.querySelector('video'));
```
The new `video` element should be below the `h1`.
```js
assert.exists(document.querySelector('h1 + video'));
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Working with the HTML Video Element</title>
</head>
<body>
<h1>Working with the HTML Video Element</h1>
--fcc-editable-region--
--fcc-editable-region--
</body>
</html>
```
@@ -0,0 +1,43 @@
---
id: 68f1f371fefd448b4f70b898
title: Step 3
challengeType: 0
dashedName: step-3
---
# --description--
In a previous lesson, you learned about different attributes available to the `video` element. The `width` attribute determines the width of the video in pixels.
Add the `width` attribute to the `video` element with a value of `640`.
# --hints--
Your `video` element should have the attribute `width` with the value `640`.
```js
const video = document.querySelector('video');
assert.strictEqual(video?.getAttribute('width'), '640');
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Working with the HTML Video Element</title>
</head>
<body>
<h1>Working with the HTML Video Element</h1>
--fcc-editable-region--
<video>
--fcc-editable-region--
</video>
</body>
</html>
```
@@ -0,0 +1,45 @@
---
id: 68f1f57723debf9383c75947
title: Step 4
challengeType: 0
dashedName: step-4
---
# --description--
The `loop` attribute will restart the video once playback is completed. Think of an internet meme that repeats playback. Omitting the `loop` attribute will make the video playback once.
The `loop` attribute is a boolean attribute and does not need a value.
Add the `loop` attribute to the `video` element.
# --hints--
Your `video` element should have the `loop` attribute.
```js
const video = document.querySelector('video');
assert.isTrue(video?.hasAttribute('loop'));
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Working with the HTML Video Element</title>
</head>
<body>
<h1>Working with the HTML Video Element</h1>
--fcc-editable-region--
<video width="640">
--fcc-editable-region--
</video>
</body>
</html>
```
@@ -0,0 +1,47 @@
---
id: 68f1f6f97af9749a42260a43
title: Step 5
challengeType: 0
dashedName: step-5
---
# --description--
The `controls` attribute provides playback controls including playback, rewind, and volume control for the `video` element.
The `controls` attribute is a boolean attribute and does not need a value.
Add the `controls` attribute to the `video` element.
Now you should see the video element displayed on the page.
# --hints--
Your `video` element should have the `controls` attribute.
```js
const video = document.querySelector('video');
assert.isTrue(video?.hasAttribute('controls'));
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Working with the HTML Video Element</title>
</head>
<body>
<h1>Working with the HTML Video Element</h1>
--fcc-editable-region--
<video width="640" loop>
--fcc-editable-region--
</video>
</body>
</html>
```
@@ -0,0 +1,45 @@
---
id: 68f1f8c248306da204655117
title: Step 6
challengeType: 0
dashedName: step-6
---
# --description--
The `muted` attribute will silence audio on initial playback. If you have `controls` enabled, the user will be able to unmute audio. Omitting the `muted` attribute will play audio on initial playback.
The `muted` attribute is a boolean attribute and does not need a value.
Add the `muted` attribute to the `video` element.
# --hints--
Your `video` element should have the `muted` attribute.
```js
const video = document.querySelector('video');
assert.isTrue(video?.hasAttribute('muted'));
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Working with the HTML Video Element</title>
</head>
<body>
<h1>Working with the HTML Video Element</h1>
--fcc-editable-region--
<video width="640" loop controls>
--fcc-editable-region--
</video>
</body>
</html>
```
@@ -0,0 +1,48 @@
---
id: 68f1f993460f06a64c033061
title: Step 7
challengeType: 0
dashedName: step-7
---
# --description--
The `poster` attribute is a thumbnail image of the video. Think of the videos you watch on YouTube. It's displayed while the video is downloading. If the attribute is omitted, the first video frame is shown during the download phase.
Now, add the `poster` attribute with the value `https://cdn.freecodecamp.org/curriculum/labs/past-event2.jpg` to your `video` element.
# --hints--
Your `video` element should have the `poster` attribute with the value `https://cdn.freecodecamp.org/curriculum/labs/past-event2.jpg`.
```js
const video = document.querySelector('video');
assert.strictEqual(video?.getAttribute('poster'), 'https://cdn.freecodecamp.org/curriculum/labs/past-event2.jpg');
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Working with the HTML Video Element</title>
</head>
<body>
<h1>Working with the HTML Video Element</h1>
--fcc-editable-region--
<video
width="640"
loop
controls
muted
>
--fcc-editable-region--
</video>
</body>
</html>
```
@@ -0,0 +1,60 @@
---
id: 68f1fac259a4afabb478b960
title: Step 8
challengeType: 0
dashedName: step-8
---
# --description--
You might have noticed you didn't link to the actual video. You will do that in the next phase. When it comes to video file types, there are differences in browser support. To accommodate this, you can use `source` elements inside the `video` element and the browser will select the first compatible `source`.
Here is an example of a `source` element:
```html
<video controls width="250">
<source src="src-url-goes-here" type="video-type-goes-here" />
</video>
```
The `source` element is a void element so it does not have a closing tag.
Add a `source` element inside of your `video` element.
# --hints--
You should have a `source` element inside of your `video` element.
```js
assert.exists(document.querySelector('video > source'));
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Working with the HTML Video Element</title>
</head>
<body>
<h1>Working with the HTML Video Element</h1>
<video
width="640"
loop
controls
muted
poster="https://cdn.freecodecamp.org/curriculum/labs/past-event2.jpg"
>
--fcc-editable-region--
--fcc-editable-region--
</video>
</body>
</html>
```
@@ -0,0 +1,58 @@
---
id: 68f268880219c2485bd71d2a
title: Step 9
challengeType: 0
dashedName: step-9
---
# --description--
To specify the media resource for the video, you will need to add the `src` attribute to the `source` element.
Add the `src` attribute with the value `https://cdn.freecodecamp.org/curriculum/labs/what-is-the-map-method-and-how-does-it-work.mp4`.
# --hints--
Your `video` element should have a `src` attribute.
```js
const source = document.querySelector('source');
assert.isTrue(source?.hasAttribute('src'));
```
Your `source` element should have a `src` attribute with the value `https://cdn.freecodecamp.org/curriculum/labs/what-is-the-map-method-and-how-does-it-work.mp4`.
```js
const source = document.querySelector('source');
assert.strictEqual(source?.getAttribute('src'),
'https://cdn.freecodecamp.org/curriculum/labs/what-is-the-map-method-and-how-does-it-work.mp4');
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Working with the HTML Video Element</title>
</head>
<body>
<h1>Working with the HTML Video Element</h1>
<video
width="640"
loop
controls
muted
poster="https://cdn.freecodecamp.org/curriculum/labs/past-event2.jpg"
>
--fcc-editable-region--
<source>
--fcc-editable-region--
</video>
</body>
</html>
```
@@ -0,0 +1,66 @@
---
id: 68f26a5f79e97c5032c10642
title: Step 10
challengeType: 0
dashedName: step-10
---
# --description--
You have used a video file with an `mp4` file extension, and you need to tell the browser that so it knows how to read the file.
You will use the `type` attribute to specify the `video/mp4` MIME type.
MIME (Multipurpose Internet Mail Extensions) is a standard to describe documents in other forms besides ASCII text, for example, audio, video, and images.
MP4, formally known as MPEG-4 Part 14, is a digital multimedia container format. It is widely used for storing video and audio, but it can also include other data types like subtitles and still images. MP4 files are designed for streaming over the Internet and are compatible with many devices and platforms.
Now, add the `type` attribute and the value `video/mp4`.
# --hints--
Your `source` element should have a `type` attribute.
```js
const source = document.querySelector('source');
assert.isTrue(source?.hasAttribute('type'));
```
Your `source` element should have a `type` attribute with the value `video/mp4`.
```js
const source = document.querySelector('source');
assert.strictEqual(source?.getAttribute('type'), 'video/mp4');
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Working with the HTML Video Element</title>
</head>
<body>
<h1>Working with the HTML Video Element</h1>
<video
width="640"
loop
controls
muted
poster="https://cdn.freecodecamp.org/curriculum/labs/past-event2.jpg"
>
--fcc-editable-region--
<source
src="https://cdn.freecodecamp.org/curriculum/labs/what-is-the-map-method-and-how-does-it-work.mp4"
>
--fcc-editable-region--
</video>
</body>
</html>
```
@@ -0,0 +1,83 @@
---
id: 68f26d52b13e0f5b6e2d2e09
title: Step 11
challengeType: 0
dashedName: step-11
---
# --description--
Another common MIME type is the `video/webm` MIME type.
WebM is an open-source audiovisual media file format developed by Google, primarily designed for web-based media content. It supports video codecs like VP8, VP9, and AV1, and audio codecs such as Vorbis and Opus, making it a popular choice for HTML5 video and audio elements.
Below your first `source` element, add another `source` element and give it a `src` attribute with the value `https://cdn.freecodecamp.org/curriculum/labs/mapmethod.webm` and a `type` attribute with the value `video/webm`.
# --hints--
You should have a second `source` element.
```js
assert.exists(document.querySelector('video > source:nth-of-type(2)'));
```
Your second `source` element should have a `src` attribute.
```js
const source = document.querySelector('source:nth-of-type(2)');
assert.isTrue(source?.hasAttribute('src'));
```
Your second `source` element should have a `src` attribute with a value of `https://cdn.freecodecamp.org/curriculum/labs/mapmethod.webm`.
```js
const source = document.querySelector('source:nth-of-type(2)');
assert.strictEqual(source?.getAttribute('src'), 'https://cdn.freecodecamp.org/curriculum/labs/mapmethod.webm');
```
Your second `source` element should have a `type` attribute.
```js
const source = document.querySelector('source:nth-of-type(2)');
assert.isTrue(source?.hasAttribute('type'));
```
Your second `source` element should have a `type` attribute with a value of `video/webm`.
```js
const source = document.querySelector('source:nth-of-type(2)');
assert.strictEqual(source?.getAttribute('type'), 'video/webm');
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Working with the HTML Video Element</title>
</head>
<body>
<h1>Working with the HTML Video Element</h1>
<video
width="640"
loop
controls
muted
poster="https://cdn.freecodecamp.org/curriculum/labs/past-event2.jpg"
>
<source
src="https://cdn.freecodecamp.org/curriculum/labs/what-is-the-map-method-and-how-does-it-work.mp4"
type="video/mp4"
>
--fcc-editable-region--
--fcc-editable-region--
</video>
</body>
</html>
```
@@ -0,0 +1,87 @@
---
id: 68f2701b2160c765f23b60f8
title: Step 12
challengeType: 0
dashedName: step-12
---
# --description--
Another common MIME type is the `video/ogg` MIME type.
Ogg is a digital multimedia container format designed to provide for efficient streaming and manipulation of digital multimedia. It is maintained by the Xiph.Org Foundation and is free and open, unrestricted by software patents. Its name is derived from "ogging", jargon from the computer game Netrek.
Below your second `source` element, add a third `source` element and give it a `src` attribute with the value `https://cdn.freecodecamp.org/curriculum/labs/mapmethod.ogg` and a `type` attribute with the value `video/ogg`.
# --hints--
You should have a third `source` element.
```js
assert.exists(document.querySelector('video > source:nth-of-type(3)'));
```
Your third `source` element should have a `src` attribute.
```js
const source = document.querySelector('source:nth-of-type(3)');
assert.isTrue(source?.hasAttribute('src'));
```
Your third `source` element should have a `src` attribute with a value of `https://cdn.freecodecamp.org/curriculum/labs/mapmethod.ogg`.
```js
const source = document.querySelector('source:nth-of-type(3)');
assert.strictEqual(source?.getAttribute('src'), 'https://cdn.freecodecamp.org/curriculum/labs/mapmethod.ogg');
```
Your third `source` element should have a `type` attribute.
```js
const source = document.querySelector('source:nth-of-type(3)');
assert.isTrue(source?.hasAttribute('type'));
```
Your third `source` element should have a `type` attribute with a value of `video/ogg`.
```js
const source = document.querySelector('source:nth-of-type(3)');
assert.strictEqual(source?.getAttribute('type'), 'video/ogg');
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Working with the HTML Video Element</title>
</head>
<body>
<h1>Working with the HTML Video Element</h1>
<video
width="640"
loop
controls
muted
poster="https://cdn.freecodecamp.org/curriculum/labs/past-event2.jpg"
>
<source
src="https://cdn.freecodecamp.org/curriculum/labs/what-is-the-map-method-and-how-does-it-work.mp4"
type="video/mp4"
>
<source
src="https://cdn.freecodecamp.org/curriculum/labs/mapmethod.webm"
type="video/webm"
>
--fcc-editable-region--
--fcc-editable-region--
</video>
</body>
</html>
```
@@ -0,0 +1,133 @@
---
id: 68f27139bddf9f6bf2821acf
title: Step 13
challengeType: 0
dashedName: step-13
---
# --description--
The last `source` element you will add will be for the `video/quicktime` MIME type.
QuickTime is an extensible multimedia architecture created by Apple, which supports playing, streaming, encoding, and transcoding a variety of digital media formats. Not as popular as the MP4 format, you may need it for legacy application support.
Below your third `source` element, add a fourth `source` element and give it a `src` attribute with the value `https://cdn.freecodecamp.org/curriculum/labs/mapmethod.mov` and `type` attribute with the value `video/quicktime`.
Congratulations! You completed the HTML Video Player Workshop.
# --hints--
You should have a fourth `source` element.
```js
assert.exists(document.querySelector('video > source:nth-of-type(4)'));
```
Your fourth `source` element should have a `src` attribute.
```js
const source = document.querySelector('source:nth-of-type(4)');
assert.isTrue(source?.hasAttribute('src'));
```
Your fourth `source` element should have a `src` attribute with a value of `https://cdn.freecodecamp.org/curriculum/labs/mapmethod.mov`.
```js
const source = document.querySelector('source:nth-of-type(4)');
assert.strictEqual(source?.getAttribute('src'), 'https://cdn.freecodecamp.org/curriculum/labs/mapmethod.mov');
```
Your fourth `source` element should have a `type` attribute.
```js
const source = document.querySelector('source:nth-of-type(4)');
assert.isTrue(source?.hasAttribute('type'));
```
Your fourth `source` element should have a `type` attribute with a value of `video/quicktime`.
```js
const source = document.querySelector('source:nth-of-type(4)');
assert.strictEqual(source?.getAttribute('type'), 'video/quicktime');
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Working with the HTML Video Element</title>
</head>
<body>
<h1>Working with the HTML Video Element</h1>
<video
width="640"
loop
controls
muted
poster="https://cdn.freecodecamp.org/curriculum/labs/past-event2.jpg"
>
<source
src="https://cdn.freecodecamp.org/curriculum/labs/what-is-the-map-method-and-how-does-it-work.mp4"
type="video/mp4"
>
<source
src="https://cdn.freecodecamp.org/curriculum/labs/mapmethod.webm"
type="video/webm"
>
<source
src="https://cdn.freecodecamp.org/curriculum/labs/mapmethod.ogg"
type="video/ogg"
>
--fcc-editable-region--
--fcc-editable-region--
</video>
</body>
</html>
```
# --solutions--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Working with the HTML Video Element</title>
</head>
<body>
<h1>Working with the HTML Video Element</h1>
<video
width="640"
controls
loop
muted
poster="https://cdn.freecodecamp.org/curriculum/labs/past-event2.jpg"
>
<source
src="https://cdn.freecodecamp.org/curriculum/labs/what-is-the-map-method-and-how-does-it-work.mp4"
type="video/mp4"
>
<source
src="https://cdn.freecodecamp.org/curriculum/labs/mapmethod.webm"
type="video/webm"
>
<source
src="https://cdn.freecodecamp.org/curriculum/labs/mapmethod.ogg"
type="video/ogg"
>
<source
src="https://cdn.freecodecamp.org/curriculum/labs/mapmethod.mov"
type="video/quicktime"
>
</video>
</body>
</html>
```
@@ -0,0 +1,25 @@
{
"name": "Build an HTML Video Player",
"isUpcomingChange": false,
"dashedName": "workshop-html-video-player",
"helpCategory": "HTML-CSS",
"blockLayout": "challenge-grid",
"challengeOrder": [
{ "id": "68f18724d71db1dda8d6fec7", "title": "Step 1" },
{ "id": "68f1eb9ad0a7575e4dbc31d5", "title": "Step 2" },
{ "id": "68f1f371fefd448b4f70b898", "title": "Step 3" },
{ "id": "68f1f57723debf9383c75947", "title": "Step 4" },
{ "id": "68f1f6f97af9749a42260a43", "title": "Step 5" },
{ "id": "68f1f8c248306da204655117", "title": "Step 6" },
{ "id": "68f1f993460f06a64c033061", "title": "Step 7" },
{ "id": "68f1fac259a4afabb478b960", "title": "Step 8" },
{ "id": "68f268880219c2485bd71d2a", "title": "Step 9" },
{ "id": "68f26a5f79e97c5032c10642", "title": "Step 10" },
{ "id": "68f26d52b13e0f5b6e2d2e09", "title": "Step 11" },
{ "id": "68f2701b2160c765f23b60f8", "title": "Step 12" },
{ "id": "68f27139bddf9f6bf2821acf", "title": "Step 13" }
],
"blockLabel": "workshop",
"usesMultifileEditor": true,
"hasEditableBoundaries": true
}
@@ -18,6 +18,7 @@
"lecture-understanding-how-html-affects-seo",
"lab-travel-agency-page",
"lecture-working-with-audio-and-video-elements",
"workshop-html-video-player",
"lab-html-audio-and-video-player",
"lecture-working-with-images-and-svgs",
"workshop-build-a-heart-icon",