{"id":22345773,"url":"https://github.com/gerhynes/custom-video-player","last_synced_at":"2026-04-28T21:31:44.987Z","repository":{"id":106044885,"uuid":"106182771","full_name":"gerhynes/custom-video-player","owner":"gerhynes","description":"A custom HTML5 video player. Built for Wes Bos' JavaScript 30 course. ","archived":false,"fork":false,"pushed_at":"2018-01-13T22:41:00.000Z","size":6,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-06-04T00:15:19.123Z","etag":null,"topics":["custom-video-player","javascript","javascript30"],"latest_commit_sha":null,"homepage":"https://gk-hynes.github.io/custom-video-player/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gerhynes.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-10-08T14:30:14.000Z","updated_at":"2018-01-23T21:50:08.000Z","dependencies_parsed_at":null,"dependency_job_id":"e4c52c9a-955e-425d-94e1-00b1bcc90969","html_url":"https://github.com/gerhynes/custom-video-player","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/gerhynes/custom-video-player","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gerhynes%2Fcustom-video-player","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gerhynes%2Fcustom-video-player/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gerhynes%2Fcustom-video-player/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gerhynes%2Fcustom-video-player/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gerhynes","download_url":"https://codeload.github.com/gerhynes/custom-video-player/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gerhynes%2Fcustom-video-player/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32400842,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-28T19:38:08.556Z","status":"ssl_error","status_checked_at":"2026-04-28T19:37:55.688Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["custom-video-player","javascript","javascript30"],"created_at":"2024-12-04T09:18:39.354Z","updated_at":"2026-04-28T21:31:44.934Z","avatar_url":"https://github.com/gerhynes.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# [Custom HTML5 Video Player](https://gk-hynes.github.io/custom-video-player/)\n\nA custom interface for the standard HTML5 video player. Built for Wes Bos' [JavaScript 30](https://javascript30.com/) course.\n\n[![Screenshot of custom HTML5 video player](https://res.cloudinary.com/gerhynes/image/upload/v1515881605/customVideoPlayer1_tuqcjs.jpg)](https://gk-hynes.github.io/custom-video-player/)\n\n## Notes\n\nThe controls are built in HTML.\n\nThe JavaScript consists of three sections:\n\n* Get the elements\n* Build the functions\n* Hook up the event listeners\n\nGet the player, the video, the progress bar, the progressFilled, the skip buttons and the slider.\n\nCreate a function `togglePlay`. When it is called it uses a ternary operator to call `play()` or `pause()` on the video.\n\n```js\nfunction togglePlay() {\n  const method = video.paused ? \"play\" : \"pause\";\n  video[method]();\n}\n```\n\nAdd click event listeners to both the video and the toggle button.\n\nListen to the video for whenever it is paused and update the toggle button.\n\n```js\nfunction updateButton() {\n  const icon = this.paused ? \"►\" : \"❚ ❚\";\n  toggle.textContent = icon;\n}\nvideo.addEventListener(\"play\", updateButton);\nvideo.addEventListener(\"pause\", updateButton);\n```\n\nMake a function `skip`. Listen for a click on anyhing that has a `data-skip` attribute, i.e. the skip buttons.\n\nTake `this.dataset.skip`, use `parseFloat` to convert it from a string to a number, and then add it to `video.currentTime`.\n\nMake a function, `handleRangeUpdate`, and listen for a change event or a mousemove event on each of the `ranges`.\n\nInside `handleRangeUpdate` set `video[this.name]` to be `this.value` i.e. the value being entered from the sliders.\n\nMake a function `handleProgress`. Set the variable `percent` to `(video.currentTime / video.duration) * 100`.\n\nMake the flex-basis of the progress bar corresponds to its percent completed.\n\n```js\nprogressBar.style.flexBasis = `${percent}%`;\n```\n\nListen for the video to emit an event `timeupdate` and when it happens run `handleProgress`.\n\nMake a function `scrub`, listen for a click on the progress bar and when that happens run `scrub`.\n\nSet a variable `scrubTime` equal to `e.offsetX / progress.offsetWidth * video.duration`. Then update `video.currentTime` to equal `scrubTime`.\n\nSo that `scrub` only runs when the progress bar is dragged, create a variable `mousedown` equal to `false`. When someone mouses down set it to `true` and when they mouse up set it equal to `false`.\n\nListen for a mousemove event on the progrss bar, check if mousedown is `true` and, if it is, run `scrub`.\n\n```js\nprogress.addEventListener(\"mousemove\", e =\u003e mousedown \u0026\u0026 scrub(e));\n```\n\n### Fullscreen mode\n\nThanks to Vince Aggrippino for this fullscreen [solution](https://codepen.io/VAggrippino/pen/vgZdaw).\n\nAdd a button with a class of `fullscreen`.\n\nMake two functions, `toggleFullscreen` and `toggleFullscreenClasses`, and a variable `isFullscreen`, initially set to `false`.\n\nIn `toggleFullscreen`, if `isFullscreen` is `true` check if the `document.exitFullscreen` method is available and run it. Otherwise run the browser-specific equivalent. If `isFullscreen` is `false`, run `player.requestFullscreen` (or the browser-specific equivalent).\n\nIn `toggleFullscreenClasses`, toggle the fullscreen class on the player and set `isFullscreen` to `!isFullscreen`.\n\nListen for a fullscreenchange (and each browswer's implementation of it) and run `toggleFullscreenClasses`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgerhynes%2Fcustom-video-player","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgerhynes%2Fcustom-video-player","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgerhynes%2Fcustom-video-player/lists"}