aboutsummaryrefslogtreecommitdiff
path: root/files/ja/learn/javascript/client-side_web_apis/video_and_audio_apis/index.html
blob: bcc0c856c36b4d8d7bdd453db81bedb105d6d710 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
---
title: 動画と音声の API
slug: Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs
tags:
  - API
  - Audio
  - Beginner
  - CodingScripting
  - Guide
  - JavaScript
  - Learn
  - Video
  - 記事
translation_of: Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs
---
<div>{{LearnSidebar}}</div>

<div>{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Drawing_graphics", "Learn/JavaScript/Client-side_web_APIs/Client-side_storage", "Learn/JavaScript/Client-side_web_APIs")}}</div>

<p class="summary">HTML5 comes with elements for embedding rich media in documents — {{htmlelement("video")}} and {{htmlelement("audio")}} — which in turn come with their own APIs for controlling playback, seeking, etc. This article shows you how to do common tasks such as creating custom playback controls.</p>

<table class="learn-box standard-table">
 <tbody>
  <tr>
   <th scope="row">前提条件:</th>
   <td>JavaScript basics (see <a href="/ja/docs/Learn/JavaScript/First_steps">first steps</a>, <a href="/ja/docs/Learn/JavaScript/Building_blocks">building blocks</a>, <a href="/ja/docs/Learn/JavaScript/Objects">JavaScript objects</a>), the <a href="/ja/docs/Learn/JavaScript/Client-side_web_APIs/Introduction">basics of Client-side APIs</a></td>
  </tr>
  <tr>
   <th scope="row">目的:</th>
   <td>To learn how to use browser APIs to control video and audio playback.</td>
  </tr>
 </tbody>
</table>

<h2 id="HTML5_video_と_audio">HTML5 video と audio</h2>

<p>The {{htmlelement("video")}} and {{htmlelement("audio")}} elements allow us to embed video and audio into web pages. As we showed in <a href="/ja/docs/Learn/HTML/Multimedia_and_embedding/Video_and_audio_content">Video and audio content</a>, a typical implementation looks like this:</p>

<pre class="brush: html notranslate">&lt;video controls&gt;
  &lt;source src="rabbit320.mp4" type="video/mp4"&gt;
  &lt;source src="rabbit320.webm" type="video/webm"&gt;
  &lt;p&gt;Your browser doesn't support HTML5 video. Here is a &lt;a href="rabbit320.mp4"&gt;link to the video&lt;/a&gt; instead.&lt;/p&gt;
&lt;/video&gt;</pre>

<p>This creates a video player inside the browser like so:</p>

<p>{{EmbedGHLiveSample("learning-area/html/multimedia-and-embedding/video-and-audio-content/multiple-video-formats.html", '100%', 380)}}</p>

<p>You can review what all the HTML features do in the article linked above; for our purposes here, the most interesting attribute is {{htmlattrxref("controls", "video")}}, which enables the default set of playback controls. If you don't specify this, you get no playback controls:</p>

<p>{{EmbedGHLiveSample("learning-area/html/multimedia-and-embedding/video-and-audio-content/multiple-video-formats-no-controls.html", '100%', 380)}}</p>

<p>This is not as immediately useful for video playback, but it does have advantages. One big issue with the native browser controls is that they are different in each browser — not very good for cross-browser support! Another big issue is that the native controls in most browsers aren't very keyboard-accessible.</p>

<p>You can solve both these problems by hiding the native controls (by removing the <code>controls</code> attribute), and programming your own with HTML, CSS, and JavaScript. In the next section we'll look at the basic tools we have available to do this.</p>

<h2 id="The_HTMLMediaElement_API">The HTMLMediaElement API</h2>

<p>Part of the HTML5 spec, the {{domxref("HTMLMediaElement")}} API provides features to allow you to control video and audio players programmatically — for example {{domxref("HTMLMediaElement.play()")}}, {{domxref("HTMLMediaElement.pause()")}}, etc. This interface is available to both {{htmlelement("audio")}} and {{htmlelement("video")}} elements, as the features you'll want to implement are nearly identical. Let's go through an example, adding features as we go.</p>

<p>Our finished example will look (and function) something like the following:</p>

<p>{{EmbedGHLiveSample("learning-area/javascript/apis/video-audio/finished/", '100%', 360)}}</p>

<h3 id="Getting_started">Getting started</h3>

<p>To get started with this example, <a href="https://github.com/mdn/learning-area/raw/master/javascript/apis/video-audio/start/media-player-start.zip">download our media-player-start.zip</a> and unzip it into a new directory on your hard drive. If you <a href="https://github.com/mdn/learning-area">downloaded our examples repo</a>, you'll find it in <code>javascript/apis/video-audio/start/</code></p>

<p>At this point, if you load the HTML you should see a perfectly normal HTML5 video player, with the native controls rendered.</p>

<h4 id="Exploring_the_HTML">Exploring the HTML</h4>

<p>Open the HTML index file. You'll see a number of features; the HTML is dominated by the video player and its controls:</p>

<pre class="notranslate">&lt;div class="player"&gt;
  &lt;video controls&gt;
    &lt;source src="video/sintel-short.mp4" type="video/mp4"&gt;
    &lt;source src="video/sintel-short.webm" type="video/webm"&gt;
    &lt;!-- fallback content here --&gt;
  &lt;/video&gt;
  &lt;div class="controls"&gt;
    &lt;button class="play" data-icon="P" aria-label="play pause toggle"&gt;&lt;/button&gt;
    &lt;button class="stop" data-icon="S" aria-label="stop"&gt;&lt;/button&gt;
    &lt;div class="timer"&gt;
      &lt;div&gt;&lt;/div&gt;
      &lt;span aria-label="timer"&gt;00:00&lt;/span&gt;
    &lt;/div&gt;
    &lt;button class="rwd" data-icon="B" aria-label="rewind"&gt;&lt;/button&gt;
    &lt;button class="fwd" data-icon="F" aria-label="fast forward"&gt;&lt;/button&gt;
  &lt;/div&gt;
&lt;/div&gt;
</pre>

<ul>
 <li>The whole player is wrapped in a {{htmlelement("div")}} element, so it can all be styled as one unit if needed.</li>
 <li>The {{htmlelement("video")}} element contains two {{htmlelement("source")}} elements so that different formats can be loaded depending on the browser viewing the site.</li>
 <li>The controls HTML is probably the most interesting:
  <ul>
   <li>We have four {{htmlelement("button")}}s — play/pause, stop, rewind, and fast forward.</li>
   <li>Each <code>&lt;button&gt;</code> has a <code>class</code> name, a <code>data-icon</code> attribute for defining what icon should be shown on each button (we'll show how this works in the below section), and an <code>aria-label</code> attribute to provide an understandable description of each button, since we're not providing a human-readable label inside the tags. The contents of <code>aria-label</code> attributes are read out by screenreaders when their users focus on the elements that contain them.</li>
   <li>There is also a timer {{htmlelement("div")}}, which will report the elapsed time when the video is playing. Just for fun, we are providing two reporting mechanisms — a {{htmlelement("span")}} containing the elapsed time in minutes and seconds, and an extra <code>&lt;div&gt;</code> that we will use to create a horizontal indicator bar that gets longer as the time elapses. To get an idea of what the finished product will look like, <a href="https://mdn.github.io/learning-area/javascript/apis/video-audio/finished/">check out our finished version</a>.</li>
  </ul>
 </li>
</ul>

<h4 id="Exploring_the_CSS">Exploring the CSS</h4>

<p>Now open the CSS file and have a look inside. The CSS for the example is not too complicated, but we'll highlight the most interesting bits here. First of all, notice the <code>.controls</code> styling:</p>

<pre class="brush: css notranslate">.controls {
  visibility: hidden;
  opacity: 0.5;
  width: 400px;
  border-radius: 10px;
  position: absolute;
  bottom: 20px;
  left: 50%;
  margin-left: -200px;
  background-color: black;
  box-shadow: 3px 3px 5px black;
  transition: 1s all;
  display: flex;
}

.player:hover .controls, player:focus .controls {
  opacity: 1;
}
</pre>

<ul>
 <li>We start off with the {{cssxref("visibility")}} of the custom controls set to <code>hidden</code>. In our JavaScript later on, we will set the controls to <code>visible</code>, and remove the <code>controls</code> attribute from the <code>&lt;video&gt;</code> element. This is so that, if the JavaScript doesn't load for some reason, users can still use the video with the native controls.</li>
 <li>We give the controls an {{cssxref("opacity")}} of 0.5 by default, so that they are less distracting when you are trying to watch the video. Only when you are hovering/focusing over the player do the controls appear at full opacity.</li>
 <li>We lay out the buttons inside the control bar using Flexbox ({{cssxref("display")}}: flex), to make things easier.</li>
</ul>

<p>Next, let's look at our button icons:</p>

<pre class="brush: css notranslate">@font-face {
   font-family: 'HeydingsControlsRegular';
   src: url('fonts/heydings_controls-webfont.eot');
   src: url('fonts/heydings_controls-webfont.eot?#iefix') format('embedded-opentype'),
        url('fonts/heydings_controls-webfont.woff') format('woff'),
        url('fonts/heydings_controls-webfont.ttf') format('truetype');
   font-weight: normal;
   font-style: normal;
}

button:before {
  font-family: HeydingsControlsRegular;
  font-size: 20px;
  position: relative;
  content: attr(data-icon);
  color: #aaa;
  text-shadow: 1px 1px 0px black;
}</pre>

<p>First of all, at the top of the CSS we use a {{cssxref("@font-face")}} block to import a custom web font. This is an icon font — all the characters of the alphabet equate to common icons you might want to use in an application.</p>

<p>Next we use generated content to display an icon on each button:</p>

<ul>
 <li>We use the {{cssxref("::before")}} selector to display the content before each {{htmlelement("button")}} element.</li>
 <li>We use the {{cssxref("content")}} property to set the content to be displayed in each case to be equal to the contents of the <code><a href="/ja/docs/Learn/HTML/Howto/Use_data_attributes">data-icon</a></code> attribute. In the case of our play button, <code>data-icon</code> contains a capital "P".</li>
 <li>We apply the custom web font to our buttons using {{cssxref("font-family")}}. In this font, "P" is actually a "play" icon, so therefore the play button has a "play" icon displayed on it.</li>
</ul>

<p>Icon fonts are very cool for many reasons — cutting down on HTTP requests because you don't need to download those icons as image files, great scalability, and the fact that you can use text properties to style them — like {{cssxref("color")}} and {{cssxref("text-shadow")}}.</p>

<p>Last but not least, let's look at the CSS for the timer:</p>

<pre class="brush: css notranslate">.timer {
  line-height: 38px;
  font-size: 10px;
  font-family: monospace;
  text-shadow: 1px 1px 0px black;
  color: white;
  flex: 5;
  position: relative;
}

.timer div {
  position: absolute;
  background-color: rgba(255,255,255,0.2);
  left: 0;
  top: 0;
  width: 0;
  height: 38px;
  z-index: 2;
}

.timer span {
  position: absolute;
  z-index: 3;
  left: 19px;
}</pre>

<ul>
 <li>We set the outer <code>.timer</code> <code>&lt;div&gt;</code> to have flex: 5, so it takes up most of the width of the controls bar. We also give it {{cssxref("position")}}<code>: relative</code>, so that we can position elements inside it conveniently according to it's boundaries, and not the boundaries of the {{htmlelement("body")}} element.</li>
 <li>The inner <code>&lt;div&gt;</code> is absolutely positioned to sit directly on top of the outer <code>&lt;div&gt;</code>. It is also given an initial width of 0, so you can't see it at all. As the video plays, the width will be increased via JavaScript as the video elapses.</li>
 <li>The <code>&lt;span&gt;</code> is also absolutely positioned to sit near the left hand side of the timer bar.</li>
 <li>We also give our inner <code>&lt;div&gt;</code> and <code>&lt;span&gt;</code> the right amount of {{cssxref("z-index")}} so that the timer will be displayed on top, and the inner <code>&lt;div&gt;</code> below that. This way, we make sure we can see all the information — one box is not obscuring another.</li>
</ul>

<h3 id="Implementing_the_JavaScript">Implementing the JavaScript</h3>

<p>We've got a fairly complete HTML and CSS interface already; now we just need to wire up all the buttons to get the controls working.</p>

<ol>
 <li>
  <p>Create a new JavaScript file in the same directory level as your index.html file. Call it <code>custom-player.js</code>.</p>
 </li>
 <li>
  <p>At the top of this file, insert the following code:</p>

  <pre class="brush: js notranslate">const media = document.querySelector('video');
const controls = document.querySelector('.controls');

const play = document.querySelector('.play');
const stop = document.querySelector('.stop');
const rwd = document.querySelector('.rwd');
const fwd = document.querySelector('.fwd');

const timerWrapper = document.querySelector('.timer');
const timer = document.querySelector('.timer span');
const timerBar = document.querySelector('.timer div');
</pre>

  <p>Here we are creating constants to hold references to all the objects we want to manipulate. We have three groups:</p>

  <ul>
   <li>The <code>&lt;video&gt;</code> element, and the controls bar.</li>
   <li>The play/pause, stop, rewind, and fast forward buttons.</li>
   <li>The outer timer wrapper <code>&lt;div&gt;</code>, the digital timer readout <code>&lt;span&gt;</code>, and the inner <code>&lt;div&gt;</code> that gets wider as the time elapses.</li>
  </ul>
 </li>
 <li>
  <p>Next, insert the following at the bottom of your code:</p>

  <pre class="brush: js notranslate">media.removeAttribute('controls');
controls.style.visibility = 'visible';</pre>

  <p>These two lines remove the default browser controls from the video, and make the custom controls visible.</p>
 </li>
</ol>

<h4 id="Playing_and_pausing_the_video">Playing and pausing the video</h4>

<p>Let's implement probably the most important control — the play/pause button.</p>

<ol>
 <li>
  <p>First of all, add the following to the bottom of your code, so that the <code>playPauseMedia()</code> function is invoked when the play button is clicked:</p>

  <pre class="brush: js notranslate">play.addEventListener('click', playPauseMedia);
</pre>
 </li>
 <li>
  <p>Now to define <code>playPauseMedia()</code> — add the following, again at the bottom of your code:</p>

  <pre class="brush: js notranslate">function playPauseMedia() {
  if(media.paused) {
    play.setAttribute('data-icon','u');
    media.play();
  } else {
    play.setAttribute('data-icon','P');
    media.pause();
  }
}</pre>

  <p>Here we use an <code><a href="/ja/docs/Web/JavaScript/Reference/Statements/if...else">if</a></code> statement to check whether the video is paused. The {{domxref("HTMLMediaElement.paused")}} property returns true if the media is paused, which is any time the video is not playing, including when it is set at 0 duration after it first loads. If it is paused, we set the <code>data-icon</code> attribute value on the play button to "u", which is a "paused" icon, and invoke the {{domxref("HTMLMediaElement.play()")}} method to play the media.</p>

  <p>On the second click, the button will be toggled back again — the "play" icon will be shown again, and the video will be paused with {{domxref("HTMLMediaElement.pause()")}}.</p>
 </li>
</ol>

<h4 id="Stopping_the_video">Stopping the video</h4>

<ol>
 <li>
  <p>Next, let's add functionality to handle stopping the video. Add the following <code><a href="/ja/docs/Web/API/EventTarget/addEventListener">addEventListener()</a></code> lines below the previous one you added:</p>

  <pre class="brush: js notranslate">stop.addEventListener('click', stopMedia);
media.addEventListener('ended', stopMedia);
</pre>

  <p>The {{event("click")}} event is obvious — we want to stop the video by running our <code>stopMedia()</code> function when the stop button is clicked. We do however also want to stop the video when it finishes playing — this is marked by the {{event("ended")}} event firing, so we also set up a listener to run the function on that event firing too.</p>
 </li>
 <li>
  <p>Next, let's define <code>stopMedia()</code> — add the following function below <code>playPauseMedia()</code>:</p>

  <pre class="brush: js notranslate">function stopMedia() {
  media.pause();
  media.currentTime = 0;
  play.setAttribute('data-icon','P');
}
</pre>

  <p>there is no <code>stop()</code> method on the HTMLMediaElement API — the equivalent is to <code>pause()</code> the video, and set its {{domxref("HTMLMediaElement.currentTime","currentTime")}} property to 0. Setting <code>currentTime</code> to a value (in seconds) immediately jumps the media to that position.</p>

  <p>All there is left to do after that is to set the displayed icon to the "play" icon. Regardless of whether the video was paused or playing when the stop button is pressed, you want it to be ready to play afterwards.</p>
 </li>
</ol>

<h4 id="Seeking_back_and_forth">Seeking back and forth</h4>

<p>There are many ways that you can implement rewind and fast forward functionality; here we are showing you a relatively complex way of doing it, which doesn't break when the different buttons are pressed in an unexpected order.</p>

<ol>
 <li>
  <p>First of all, add the following two <code><a href="/ja/docs/Web/API/EventTarget/addEventListener">addEventListener()</a></code> lines below the previous ones:</p>

  <pre class="brush: js notranslate">rwd.addEventListener('click', mediaBackward);
fwd.addEventListener('click', mediaForward);
</pre>
 </li>
 <li>
  <p>Now on to the event handler functions — add the following code below your previous functions to define <code>mediaBackward()</code> and <code>mediaForward()</code>:</p>

  <pre class="brush: js notranslate">let intervalFwd;
let intervalRwd;

function mediaBackward() {
  clearInterval(intervalFwd);
  fwd.classList.remove('active');

  if(rwd.classList.contains('active')) {
    rwd.classList.remove('active');
    clearInterval(intervalRwd);
    media.play();
  } else {
    rwd.classList.add('active');
    media.pause();
    intervalRwd = setInterval(windBackward, 200);
  }
}

function mediaForward() {
  clearInterval(intervalRwd);
  rwd.classList.remove('active');

  if(fwd.classList.contains('active')) {
    fwd.classList.remove('active');
    clearInterval(intervalFwd);
    media.play();
  } else {
    fwd.classList.add('active');
    media.pause();
    intervalFwd = setInterval(windForward, 200);
  }
}
</pre>

  <p>You'll notice that first we initialize two variables — <code>intervalFwd</code> and <code>intervalRwd</code> — you'll find out what they are for later on.</p>

  <p>Let's step through <code>mediaBackward()</code> (the functionality for <code>mediaForward()</code> is exactly the same, but in reverse):</p>

  <ol>
   <li>We clear any classes and intervals that are set on the fast forward functionality — we do this because if we press the <code>rwd</code> button after pressing the <code>fwd</code> button, we want to cancel any fast forward functionality and replace it with the rewind functionality. If we tried to do both at one, the player would break.</li>
   <li>We use an <code>if</code> statement to check whether the <code>active</code> class has been set on the <code>rwd</code> button, indicating that it has already been pressed. The {{domxref("Element.classList", "classList")}} is a rather handy property that exists on every element — it contains a list of all the classes set on the element, as well as methods for adding/removing classes, etc. We use the <code>classList.contains()</code> method to check whether the list contains the <code>active</code> class. This returns a boolean <code>true</code>/<code>false</code> result.</li>
   <li>If <code>active</code> has been set on the <code>rwd</code> button, we remove it using <code>classList.remove()</code>, clear the interval that has been set when the button was first pressed (see below for more explanation), and use {{domxref("HTMLMediaElement.play()")}} to cancel the rewind and start the video playing normally.</li>
   <li>If it hasn't yet been set, we add the <code>active</code> class to the <code>rwd</code> button using <code>classList.add()</code>, pause the video using {{domxref("HTMLMediaElement.pause()")}}, then set the <code>intervalRwd</code> variable to equal a {{domxref("WindowOrWorkerGlobalScope.setInterval", "setInterval()")}} call. When invoked, <code>setInterval()</code> creates an active interval, meaning that it runs the function given as the first parameter every x milliseconds, where x is the value of the 2nd parameter. So here we are running the <code>windBackward()</code> function every 200 milliseconds — we'll use this function to wind the video backwards constantly. To stop a {{domxref("WindowOrWorkerGlobalScope.setInterval", "setInterval()")}} running, you have to call {{domxref("WindowOrWorkerGlobalScope.clearInterval", "clearInterval()")}}, giving it the identifying name of the interval to clear, which in this case is the variable name <code>intervalRwd</code> (see the <code>clearInterval()</code> call earlier on in the function).</li>
  </ol>
 </li>
 <li>
  <p>Finally, we need to define the <code>windBackward()</code> and <code>windForward()</code> functions invoked in the <code>setInterval()</code> calls. Add the following below your two previous functions:</p>

  <pre class="brush: js notranslate">function windBackward() {
  if(media.currentTime &lt;= 3) {
    rwd.classList.remove('active');
    clearInterval(intervalRwd);
    stopMedia();
  } else {
    media.currentTime -= 3;
  }
}

function windForward() {
  if(media.currentTime &gt;= media.duration - 3) {
    fwd.classList.remove('active');
    clearInterval(intervalFwd);
    stopMedia();
  } else {
    media.currentTime += 3;
  }
}</pre>

  <p>Again, we'll just run through the first one of these functions as they work almost identically, but in reverse to one another. In <code>windBackward()</code> we do the following — bear in mind that when the interval is active, this function is being run once every 200 milliseconds.</p>

  <ol>
   <li>We start off with an <code>if</code> statement that checks to see whether the current time is less than 3 seconds, i.e., if rewinding by another three seconds would take it back past the start of the video. This would cause strange behaviour, so if this is the case we stop the video playing by calling <code>stopMedia()</code>, remove the <code>active</code> class from the rewind button, and clear the <code>intervalRwd</code> interval to stop the rewind functionality. If we didn't do this last step, the video would just keep rewinding forever.</li>
   <li>If the current time is not within 3 seconds of the start of the video, we simply remove three seconds from the current time by executing <code>media.currentTime -= 3</code>. So in effect, we are rewinding the video by 3 seconds, once every 200 milliseconds.</li>
  </ol>
 </li>
</ol>

<h4 id="Updating_the_elapsed_time">Updating the elapsed time</h4>

<p>The very last piece of our media player to implement is the time elapsed displays. To do this we'll run a function to update the time displays every time the {{event("timeupdate")}} event is fired on the <code>&lt;video&gt;</code> element. The frequency with which this event fires depends on your browser, CPU power, etc (<a href="http://stackoverflow.com/questions/9678177/how-often-does-the-timeupdate-event-fire-for-an-html5-video">see this stackoverflow post</a>).</p>

<p>Add the following <code>addEventListener()</code> line just below the others:</p>

<pre class="brush: js notranslate">media.addEventListener('timeupdate', setTime);</pre>

<p>Now to define the <code>setTime()</code> function. Add the following at the bottom of your file:</p>

<pre class="brush: js notranslate">function setTime() {
  let minutes = Math.floor(media.currentTime / 60);
  let seconds = Math.floor(media.currentTime - minutes * 60);
  let minuteValue;
  let secondValue;

  if (minutes &lt; 10) {
    minuteValue = '0' + minutes;
  } else {
    minuteValue = minutes;
  }

  if (seconds &lt; 10) {
    secondValue = '0' + seconds;
  } else {
    secondValue = seconds;
  }

  let mediaTime = minuteValue + ':' + secondValue;
  timer.textContent = mediaTime;

  let barLength = timerWrapper.clientWidth * (media.currentTime/media.duration);
  timerBar.style.width = barLength + 'px';
}
</pre>

<p>This is a fairly long function, so let's go through it step by step:</p>

<ol>
 <li>First of all, we work out the number of minutes and seconds in the {{domxref("HTMLMediaElement.currentTime")}} value.</li>
 <li>Then we initialize two more variables — <code>minuteValue</code> and <code>secondValue</code>.</li>
 <li>The two <code>if</code> statements work out whether the number of minutes and seconds are less than 10. If so, they add a leading zero to the values, in the same way that a digital clock display works.</li>
 <li>The actual time value to display is set as <code>minuteValue</code> plus a colon character plus <code>secondValue</code>.</li>
 <li>The {{domxref("Node.textContent")}} value of the timer is set to the time value, so it displays in the UI.</li>
 <li>The length we should set the inner <code>&lt;div&gt;</code> to is worked out by first working out the width of the outer <code>&lt;div&gt;</code> (any element's {{domxref("Element.clientWidth", "clientWidth")}} property will contain its length), and then multiplying it by the {{domxref("HTMLMediaElement.currentTime")}} divided by the total {{domxref("HTMLMediaElement.duration")}} of the media.</li>
 <li>We set the width of the inner <code>&lt;div&gt;</code> to equal the calculated bar length, plus "px", so it will be set to that number of pixels.</li>
</ol>

<h4 id="Fixing_play_and_pause">Fixing play and pause</h4>

<p>There is one problem left to fix. If the play/pause or stop buttons are pressed while the rewind or fast forward functionality is active, they just don't work. How can we fix it so that they cancel the <code>rwd</code>/<code>fwd</code> button functionality and play/stop the video as you'd expect? This is fairly easy to fix.</p>

<p>First of all, add the following lines inside the <code>stopMedia()</code> function — anywhere will do:</p>

<pre class="brush: js notranslate">rwd.classList.remove('active');
fwd.classList.remove('active');
clearInterval(intervalRwd);
clearInterval(intervalFwd);
</pre>

<p>Now add the same lines again, at the very start of the <code>playPauseMedia()</code> function (just before the start of the <code>if</code> statement).</p>

<p>At this point, you could delete the equivalent lines from the <code>windBackward()</code> and <code>windForward()</code> functions, as that functionality has been implemented in the <code>stopMedia()</code> function instead.</p>

<p>Note: You could also further improve the efficiency of the code by creating a separate function that runs these lines, then calling that anywhere it is needed, rather than repeating the lines multiple times in the code. But we'll leave that one up to you.</p>

<h2 id="Summary">Summary</h2>

<p>I think we've taught you enough in this article. The {{domxref("HTMLMediaElement")}} API makes a wealth of functionality available for creating simple video and audio players, and that's only the tip of the iceberg. See the "See also" section below for links to more complex and interesting functionality.</p>

<p>Here are some suggestions for ways you could enhance the existing example we've built up:</p>

<ol>
 <li>
  <p>The time display currently breaks if the video is an hour long or more (well, it won't display hours; just minutes and seconds). Can you figure out how to change the example to make it display hours?</p>
 </li>
 <li>
  <p>Because <code>&lt;audio&gt;</code> elements have the same {{domxref("HTMLMediaElement")}} functionality available to them, you could easily get this player to work for an <code>&lt;audio&gt;</code> element too. Try doing so.</p>
 </li>
 <li>
  <p>Can you work out a way to turn the timer inner <code>&lt;div&gt;</code> element into a true seek bar/scrobbler — i.e., when you click somewhere on the bar, it jumps to that relative position in the video playback? As a hint, you can find out the X and Y values of the element's left/right and top/bottom sides via the <code><a href="/ja/docs/Web/API/Element/getBoundingClientRect">getBoundingClientRect()</a></code> method, and you can find the coordinates of a mouse click via the event object of the click event, called on the {{domxref("Document")}} object. For example:</p>

  <pre class="brush: js notranslate">document.onclick = function(e) {
  console.log(e.x) + ',' + console.log(e.y)
}</pre>
 </li>
</ol>

<h2 id="See_also">See also</h2>

<ul>
 <li>{{domxref("HTMLMediaElement")}}</li>
 <li><a href="/ja/docs/Learn/HTML/Multimedia_and_embedding/Video_and_audio_content">Video and audio content</a> — simple guide to <code>&lt;video&gt;</code> and <code>&lt;audio&gt;</code> HTML.</li>
 <li><a href="/ja/docs/Web/Apps/Fundamentals/Audio_and_video_delivery">Audio and video delivery</a> — detailed guide to delivering media inside the browser, with many tips, tricks, and links to further more advanced tutorials.</li>
 <li><a href="/ja/docs/Web/Apps/Fundamentals/Audio_and_video_manipulation">Audio and video manipulation</a> — detailed guide to manipulating audio and video, e.g. with <a href="/ja/docs/Web/API/Canvas_API">Canvas API</a>, <a href="/ja/docs/Web/API/Web_Audio_API">Web Audio API</a>, and more.</li>
 <li>{{htmlelement("video")}} and {{htmlelement("audio")}} reference pages.</li>
 <li><a href="/ja/docs/Web/Media/Formats">Guide to media types and formats on the web</a></li>
</ul>

<p>{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Drawing_graphics", "Learn/JavaScript/Client-side_web_APIs/Client-side_storage", "Learn/JavaScript/Client-side_web_APIs")}}</p>

<h2 id="このモジュール">このモジュール</h2>

<ul>
 <li><a href="/ja/docs/Learn/JavaScript/Client-side_web_APIs/Introduction">Web API の紹介</a></li>
 <li><a href="/ja/docs/Learn/JavaScript/Client-side_web_APIs/Manipulating_documents">ドキュメントの操作</a></li>
 <li><a href="/ja/docs/Learn/JavaScript/Client-side_web_APIs/Fetching_data">サーバからのデータ取得</a></li>
 <li><a href="/ja/docs/Learn/JavaScript/Client-side_web_APIs/Third_party_APIs">サードパーティ API</a></li>
 <li><a href="/ja/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics">グラフィックの描画</a></li>
 <li><a href="/ja/docs/Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs">動画と音声の API</a></li>
 <li><a href="/ja/docs/Learn/JavaScript/Client-side_web_APIs/Client-side_storage">クライアント側ストレージ</a></li>
</ul>