Skip to content

Instantly share code, notes, and snippets.

@firestar300
Created March 4, 2021 16:56
Show Gist options
  • Save firestar300/a5d670357811dfaccf791f3c8613ac07 to your computer and use it in GitHub Desktop.
Save firestar300/a5d670357811dfaccf791f3c8613ac07 to your computer and use it in GitHub Desktop.
Transform native YouTube video player into a custom HTML markup
.video {
position: relative;
display: block;
padding-bottom: 56.25%;
margin-bottom: 1rem;
img,
iframe,
&__play {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
img {
object-fit: cover;
}
&__play {
&::before {
@include pseudo__content;
position: absolute;
top: 50%;
left: 50%;
z-index: 10;
width: 88px;
height: 88px;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 130 130'%3E%3Ccircle cx='65' cy='65' r='65' fill='%23fff' opacity='.3'/%3E%3Cpath fill='%23fff' fill-rule='evenodd' d='M45.6 47v36l38.8-18-38.8-18z' clip-rule='evenodd'/%3E%3C/svg%3E");
background-size: 100% 100%;
transform: translate(-50%, -50%);
}
}
}
const $ = jQuery
class AbstractDomElement {
constructor(element, options) {
let oldInstance
// provide an explicit spaceName to prevent conflict after minification
// MaClass.nameSpace = 'MaClass'
this.constructor.nameSpace = this.constructor.nameSpace || this.constructor.name
const nameSpace = this.constructor.nameSpace
// if no spacename beapi, create it - avoid futur test
if (!element.beapi) {
element.beapi = {}
}
oldInstance = element.beapi[nameSpace]
if (oldInstance) {
console.warn(
'[AbstractDomElement] more than 1 class is initialised with the same name space on :',
element,
oldInstance
)
oldInstance._isNewInstance = false
return oldInstance
}
this._element = element
this._settings = $.extend(true, {}, this.constructor.defaults, options)
this._element.beapi[nameSpace] = this
this._isNewInstance = true
}
isNewInstance() {
return this._isNewInstance
}
destroy() {
this._element.beapi[this.constructor.nameSpace] = undefined
return this
}
static init(element, options) {
foreach(element, (el) => {
new this(el, options)
})
return this
}
static hasInstance(element) {
const el = getDomElement(element)
return el && el.beapi && !!el.beapi[this.nameSpace]
}
static getInstance(element) {
const el = getDomElement(element)
return el && el.beapi ? el.beapi[this.nameSpace] : undefined
}
static destroy(element) {
this.foreach(element, (el) => {
if (el.beapi && el.beapi[this.nameSpace]) {
el.beapi[this.nameSpace].destroy()
}
})
return this
}
static foreach(element, callback) {
foreach(element, (el) => {
if (el.beapi && el.beapi[this.nameSpace]) {
callback(el)
}
})
return this
}
static initFromPreset() {
const preset = this.preset
let selector
for (selector in preset) {
this.init(selector, preset[selector])
}
return this
}
static destroyFromPreset() {
const preset = this.preset
let selector
for (selector in preset) {
this.destroy(selector)
}
return this
}
}
// ----
// utils
// ----
function foreach(element, callback) {
const el = getDomElements(element)
let i
for (i = 0; i < el.length; i++) {
if (callback(el[i]) === false) break
}
}
function getDomElements(element) {
return typeof element === 'string' ? document.querySelectorAll(element) : element.length >= 0 ? element : [element]
}
function getDomElement(element) {
return getDomElements(element)[0]
}
// ----
// export
// ----
export default AbstractDomElement
import AbstractDomElement from './AbstractDomElement'
/**
* Video Class
* @author Milan Ricoul
*/
class Video extends AbstractDomElement {
constructor(element, options) {
var instance = super(element, options)
// avoid double init :
if (!instance.isNewInstance()) {
return instance
}
this.displayEmbed = this.displayEmbed.bind(this)
this.init()
}
/**
* Initialization
* @author Milan Ricoul
*/
init() {
const el = this._element
this.wrapper = el
if (el.getAttribute('data-youtube-url')) {
this.embedUrl = el.getAttribute('data-youtube-url')
el.removeAttribute('data-youtube-url')
} else if (el.querySelector('iframe[src*="https://www.youtube.com/embed/"]')) {
this.embedUrl = el.querySelector('iframe[src*="https://www.youtube.com/embed/"]').getAttribute('src')
} else if (el.tagName === 'IFRAME') {
this.embedUrl = el.getAttribute('src')
const parent = el.parentNode
const width = el.getAttribute('width')
this.wrapper = document.createElement('div')
this.wrapper.classList.add('video')
this.wrapper.insertAdjacentHTML(
'afterbegin',
`<img data-sizes="auto" data-src="https://i.ytimg.com/vi/${this.youtubeParser(
this.embedUrl
)}/hqdefault.jpg" class="lazyload"/>`
)
parent.appendChild(this.wrapper)
parent.removeChild(el)
// Apply width of the former iframe
if (typeof width !== 'undefined') {
parent.style.maxWidth = `${width}px`
}
} else {
return false
}
console.log(this._settings)
this.wrapper.insertAdjacentHTML(
'afterbegin',
`
<button type="button" class="video__play btn btn--txt" aria-label="Lire la vidéo">
<span class="sr-only">Lire la vidéo</span>
</button>
`
)
this.wrapper.querySelector('.video__play').addEventListener('click', this.displayEmbed)
}
/**
* Display video embed
* @author Milan Ricoul
*/
displayEmbed() {
this.wrapper.innerHTML = `
<iframe src="${
this.embedUrl.includes('?') ? `${this.embedUrl}&autoplay=1&rel=0` : `${this.embedUrl}?autoplay=1&rel=0`
}" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
`
}
/**
* Get the YouTube id from an url
* @param {String} url YouTube url
* @returns {String}
*/
youtubeParser(url) {
var regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#&?]*).*/
var match = url.match(regExp)
return match && match[7].length === 11 ? match[7] : false
}
}
Video.defaults = {}
Video.preset = {
'.video': {},
'iframe[src*="https://www.youtube.com/embed/"]': {},
}
export default Video
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment