Nitish Dayal, Software & Applications Developer - Contact
Last Commit Date: May 12, 2017
When the HTML page is loaded in a browser, it displays a video player with controls for playing/pausing the video, scrolling through the video progress, skipping/ rewinding, changing the volume, and adjusting the playback speed. None of these controls currently work. Write the necessary JavaScript code to bring functionality to this page.
NOTE: While my answer probably appears significantly different from the official answer key, it's
really not. The changes worth noting are how I assigned event handlers to the skip buttons and
the volume & playback speed sliders; I defined arrow functions as event handlers directly within
the .addEventListener()
method as opposed to writing a function expression and passing in the name
of that function to be the event handler. Everything else is basically the same, just using ES6
where possible and avoiding repetition of writing const
or let
because, once again, I'm lazy
(and, once again, you should be too).
We begin by declaring constants and defining them as references to the HTML elements we want to add some functionality to: the player, the video, the current video progress & progress bar, the play/pause button, the skip & rewind buttons, and the volume & playback speed sliders. Then, we define our event handler functions: one for toggling video playback/pause, one for updating the play/pause button to display the correct symbol, one to update the progress bar to reflect the current video position, and one to change the current video position.
Attach four event listeners to the variable referencing the viewer
class (the video): a 'click'
event listener which will call upon the playback/pause toggle function as the event handler, a
'play' event listener which will call upon the function which updates the play/pause symbol, a
'pause' event listener which will call upon the same function as the 'play' event listener,
and a 'timeupdate' event listener which will call upon the function that updates the progress bar.
We now have a video that a user can click on to play/pause the video, a play/pause icon that will
update in respect to whether the video is playing or is paused, and a progress bar that will reflect
the current video position.
Add an event listener to the variable referencing the play/pause button that will listen for the 'click' event and call on the playback/pause toggle function.
To allow the user to manually change the video position by dragging the progress bar or clicking on a point
in the progress bar, we'll need to keep track of whether the user has their mouse button clicked. If the user
isn't pressing a mouse button down, they're just hovering over the progress bar and we don't need to make
any changes. Declare a variable that will be in charge of a boolean
value, and define it as false
.
Attach four event listeners to the variable referencing the current video progress: a 'mousedown' event
listener which will change the value of the variable containing the boolean
value to true
, a 'mouseup'
event listener which will change that value back to false
, a 'mousemove' event listener which will call
the function responsible for changing the video position IF the variable holding the boolean
value
is true
, and a 'click' event listener which will always call that same function.
Iterate through the variable referencing the skip/rewind buttons and attach an event listener to each
button for the 'click' event, and define an event handler function that will increment the currentTime
property of the video based on the numerical value associated with the data-skip
property of that button.
Iterate through the variable referencing the volume & playback speed sliders and attach two event listeners: a 'change' event listener that will update the property value of the video element corresponding with the 'name' property of the slider to reflect the 'value' property of the slider, and a 'mousemove' event listener that will do the same.
Steps:
- Declare seven constants that will be defined as references to the following HTML elements:
- HTML media element (video) with class
player
- Child of
player
class element with classviewer
(Example assuming first constant is namedplayer
:player.querySelector('.viewer')
) - Child of
player
element with classprogress
- Child of
player
element with classprogress__filled
<-- TWO lower dashes - Child of
player
element with classtoggle
- ALL children of
player
element with adata-skip
property - ALL children of
player
element with classplayer__slider
<-- TWO lower dashes
const player = document.querySelector('.player'),
video = player.querySelector('.viewer'),
progress = player.querySelector('.progress'),
progressBar = player.querySelector('.progress__filled'),
toggle = player.querySelector('.toggle'),
skipButtons = player.querySelectorAll('[data-skip]'),
ranges = player.querySelectorAll('.player__slider')
- Declare four
let
variables that will be defined as arrow functions to be used as event handlers:
-
togglePlay
variable defined as an arrow function that will call on either the.play()
or.pause()
method of the HTMLElement with classviewer
, depending on whether thepaused
property of that element istrue
orfalse
.let togglePlay = () => (video[video.paused ? 'play' : 'pause']())
-
updateButton
variable defined as an arrow function that will update thetextContent
property of the HTMLElement with classtoggle
to be '►' or '❚ ❚' depending on theboolean
value of theviewer
HTMLElement'spaused
property.let updateButton = () => toggle.textContent = video.paused ? '►' : '❚ ❚';
-
handleProgress
variable defined as an arrow function that will set theflexBasis
style attribute of theprogressBar
HTMLElement to reflect the current video progress.let handleProgress = () => progressBar.style.flexBasis = `$(video.currentTime/video.duration) * 100}%`
-
scrub
variable defined as an arrow function which accepts an event parameter, and updates thecurrentTime
property of theviewer
HTMLElement to reflect the position of the progress bar.let scrub = (e) => video.currentTime = ((e.offsetX / progress.offsetWidth) * video.duration)
- Attach four event listeners to the
viewer
HTMLElement:
- A 'click' event listener which will call the
togglePlay
function as an event handler. - A 'play' event listener which will call the
updateButton
function as an event handler. - A 'pause' event listener which will call the
updateButton
function as an event handler. - A 'timeupdate
event listener which will call the
handleProgress` function as an event handler.
video.addEventListener('click', togglePlay)
video.addEventListener('play', updateButton)
video.addEventListener('pause', updateButton)
video.addEventListener('timeupdate', handleProgress)
- Attach an event listener to the
toggle
HTMLElement (in this example, the variable name istoggle
as well) which will call thetogglePlay
function as an event handler.
toggle.addEventListener('click', togglePlay)
- Declare a
let
variablemousedown
and define it asfalse
.
let mousedown = false
- Attach four event listeners to the
progress
HTMLElement:
- A 'click' event listener which will call the
scrub
function as an event handler. - A 'mousemove' event listener which will call the
scrub
function as an event handler IFmousedown
istrue
. - A 'mousedown' event listener with the event handler defined as an arrow function that
will set
mousedown
to true. - A 'mouseup
event listener with the _event handler_ defined as an _arrow function_ that will set
mousedown` to false.
progress.addEventListener('click', scrub)
progress.addEventListener('mousemove', (e) => mousedown && scrub(e))
progress.addEventListener('mousedown', () => mousedown = true)
progress.addEventListener('mouseup', () => mousedown = false)
-
Iterate through the
skipButtons
variable that contains a NodeList, an array-like object, and attach a 'click' event listener to each button with the event handler defined as an arrow function that increments thecurrentTime
property of theviewer
HTMLElement with thedata-skip
attribute of that button.skipButtons.forEach(button => { button.addEventListener( //HTML attributes are string values. We want a number, so we parse the value into a `float` 'click', () => video.currentTime += parseFloat(button.dataset.skip))
})
8. Iterate through the `ranges` variable that contains a _NodeList, an array-like object_, and attach two _event listeners_
to each item:
- A 'change' _event listener_ with the _event handler_ defined as an _arrow function_ that updates a property on the
`viewer` HTMLElement _corresponding with the `name` attribute of the given NodeList item_ to the `value` attribute of the
given item.
- A 'mousemove' _event listener_ that does the same thing.
```JavaScript
ranges.forEach(range => {
range.addEventListener('change', () => video[range.name] = range.value)
range.addEventListener('mousemove', () => video[range.name] = range.value)
})
- Wrap all that up in a nice litte IIFE (immediately invoked function expression) to avoid polluting the global namespace (getting tired of reading that yet?)
Another day, another challenge, another success! Congratulations! <3