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
playerclass element with classviewer(Example assuming first constant is namedplayer:player.querySelector('.viewer')) - Child of
playerelement with classprogress - Child of
playerelement with classprogress__filled<-- TWO lower dashes - Child of
playerelement with classtoggle - ALL children of
playerelement with adata-skipproperty - ALL children of
playerelement 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
letvariables that will be defined as arrow functions to be used as event handlers:
-
togglePlayvariable defined as an arrow function that will call on either the.play()or.pause()method of the HTMLElement with classviewer, depending on whether thepausedproperty of that element istrueorfalse.let togglePlay = () => (video[video.paused ? 'play' : 'pause']())
-
updateButtonvariable defined as an arrow function that will update thetextContentproperty of the HTMLElement with classtoggleto be '►' or '❚ ❚' depending on thebooleanvalue of theviewerHTMLElement'spausedproperty.let updateButton = () => toggle.textContent = video.paused ? '►' : '❚ ❚';
-
handleProgressvariable defined as an arrow function that will set theflexBasisstyle attribute of theprogressBarHTMLElement to reflect the current video progress.let handleProgress = () => progressBar.style.flexBasis = `$(video.currentTime/video.duration) * 100}%`
-
scrubvariable defined as an arrow function which accepts an event parameter, and updates thecurrentTimeproperty of theviewerHTMLElement 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
viewerHTMLElement:
- A 'click' event listener which will call the
togglePlayfunction as an event handler. - A 'play' event listener which will call the
updateButtonfunction as an event handler. - A 'pause' event listener which will call the
updateButtonfunction as an event handler. - A 'timeupdate
event listener which will call thehandleProgress` 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
toggleHTMLElement (in this example, the variable name istoggleas well) which will call thetogglePlayfunction as an event handler.
toggle.addEventListener('click', togglePlay)- Declare a
letvariablemousedownand define it asfalse.
let mousedown = false- Attach four event listeners to the
progressHTMLElement:
- A 'click' event listener which will call the
scrubfunction as an event handler. - A 'mousemove' event listener which will call the
scrubfunction as an event handler IFmousedownistrue. - A 'mousedown' event listener with the event handler defined as an arrow function that
will set
mousedownto true. - A 'mouseup
event listener with the _event handler_ defined as an _arrow function_ that will setmousedown` 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
skipButtonsvariable 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 thecurrentTimeproperty of theviewerHTMLElement with thedata-skipattribute 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