Hi!
I need to add a HD button to control bar. Reading documentation I've found that I can do following this steps:
var MyVideo = videojs( videoObjectId , { 'preload' : "auto", 'controls' : true, 'width': 320, 'height': 180});
var HDButton = MyVideo.controlBar.addChild(
MyVideo.controlBar.createEl(
null,
{
className : 'vjs-hd-button vjs-menu-button vjs-control',
text : 'HD',
role : 'button',
'aria-live' : 'polite',
tabIndex : 0
}
);
However, the player doesn't show me this option in the Control Bar.
¿What I'm doing wrong?
Thanks!
The videojs.controlbar.addChild function requires a component (an object inhereting from subObj) but you are passing it an html element. The difference is that components are javascript wrappers for the html elements and use createEl to create their html.
Try something like:
MyVideo.controlBar.addChild('button', {
'el':videojs.createEl('button', { className: 'vjs-hd-button vjs-control',
'role': 'button'})
});
I usually end up subclassing the button directly and adding the component to a custom build and then compiling it all together-(if you don't add it to the build, you have to work the videojs-dev.js. Or you can just use the dev version and minify it by non-closure means. Here's my usual approach:
vjs.HDButton = vjs.Button .extend({
init:function(player, options) {
vjs.Button.call(this, player, options);
}
});
vjs.HDButton .prototype.className =" vjs-hd-button vjs-control ";
vjs.HDButton .prototype.buildCSSClass = function() {
return this.className + vjs.Button.prototype.buildCSSClass.call(this);
};
vjs.HDButton .prototype.onClick = function() {
/*
do whatever here - if you're swapping to an HD source be sure to remove the video element's source tags first
*/
};
Though if you don't feel like rolling components just to add a child button to an existing component:
var hdButtonEl = vjs.createEl('button', { className: 'vjs-hd-button vjs-control',
'role': 'button'
});
var hdButton = new vjs.Button(this.player_, {
'el':editButtonEl //createEl wont be called if el is provided in the options
});
this.on(hdButton, 'click', vjs.bind(this, function() {
/* do whatever... */
}));
Thanks for helping out, @Spaceninja7u!
i copied the Spaceninja7u code in witch he extends the button when i execute the page and clik on video nothing happens , what's wrong ??
@zbexworld do you have any errors in your JS console?
no that's strange no error , all i have is the videojs dev version , then a my cutom script witch adds a centered replay button at the end (the code is below )
var player = videojs('video_1',{"controls": true, "autoplay": false, "preload": "auto"},function(){
var player = this;
var replayBtn = $('',{"class": "vjs-replayButton",text:"replay"});
var overLayer = $('
this.on('ended',function(){
var player = this;
this.posterImage.show();
$('.vjs-play-control').on('click',function(){
player.posterImage.hide();
replayBtn.parent().css('display','none');
});
replayBtn.parent().css('display', 'block').end().on('click',function(){
player.posterImage.hide();
$(this).parent().css('display','none');
player.play();
});
});
});
and this is working (no problem) , i added the custom button script in a sperate js file
like it's shown below
vjs.HDButton = vjs.Button .extend({
init:function(player, options) {
vjs.Button.call(this, player, options);
}
});
vjs.HDButton .prototype.className =" vjs-hd-button vjs-control ";
vjs.HDButton .prototype.buildCSSClass = function() {
return this.className + vjs.Button.prototype.buildCSSClass.call(this);
};
vjs.HDButton .prototype.onClick = function() {
/*
do whatever here - if you're swapping to an HD source be sure to remove the video element's source tags first
*/
};
i checked the dom , no element is added , alo the console no error declared !!!
Did you name the HDButton as a child of the controlBar? I bet its just not getting added. Here's my complete-ish setup object:
var setup = {
'techOrder': ['html5'],
'autoplay': false,
'reportTouchActivity': true,
'controls': true,
'preload': 'auto',
'src': src,
'tracks': [
{
'kind': 'subtitles',
'label': 'English',
'language': 'EN',
'src': 'url to my subtitles .vtt'
}
],
'children': {
// here's a widget that isn't part of the control bar
'splashMenu': {
'topic': 'Splash menu title',
'subtitle': ''
},
'controlBar': {
//'options': { don't make an 'options' object, the object assigned to
//'controlBar' is the options
//'children': { children are now declared inline like, so don't make a children object
'selectBitratePopupMenu': {
//this bit below is specific to my own implementation, the main thing is how this
//button was placed under controlBar
'src': [
{
'src': 'url to my HD video.mp4',
'type': 'video/mp4',
'bitrate': 'high',
'icon': 'bitrate-icon-hd',
'selected': true
},
{
'src': 'url to an lower birthrate mp4 video file',
'type': 'video/mp4',
'bitrate': 'low',
'icon': 'bitrate-icon-sd'
}
]
} //end setup.children.controlBar.options.children.selectBitrateControlButton
} //end setup.children.controlBar
} //end setup.children
};//end setup
Also... are you using grunt to build your own videojs-dev build? if so you'll have to add your file to the array in build/source-loader.js. You also have to add it if you're loading source-loader.js in your own html page. Or you can load it in its own script tag after videojs- but the first way is best.
It looks like you're adding html to the videojs player after its all loaded up, which you can do I guess but there are managed child index variables it uses internally. I would recommend creating all your widgets as components and add them to the setup object like above.
Here's how I created a controlBar button using JQuery (and angular, don't let the angular bits confuse):
vjs.SelectBitratePopupMenu = vjs.Component.extend({
init:function(player, options) {
/** various setup work excluded, hides component if we're not offered multiple video urls */
vjs.Component.prototype.init.call(this, player, options);
player.one('loadeddata', vjs.bind(this, this.updateLabel));
//'resolutionchange' is an event i fired from within a function I extended the player with
player.on('resolutionchange', vjs.bind(this, this.updateLabel));
}
});
vjs.SelectBitratePopupMenu.prototype.defaultSource = null;
vjs.SelectBitratePopupMenu.prototype.createEl = function() {
/*
referenced videosrc and player objects in angular bindings (stuff inside {} brackets) are located in a containing Angularjs directive's scope. ng-click is Angular's click handler.
*/
var ui = '<button title="Select video quality" aria-label="Select video quality" class="btn btn-default vjs-bitrate-button" data-toggle="dropdown" aria-haspopup="true">'
+ '<i class="' + this.defaultSource['icon'] + '"></i>'
+ '</button>'
+ '<ul class="dropdown-menu" role="menu">'
+ '<li ng-repeat="stream in videosrc" ng-class="{active : stream.src == player.cache_.src}">'
+ '<a href="#" ng-click="setVideoStream(stream)" ng-bind-html="stream.label" role="menuitemradio" aria-checked="{stream.src == player.cache_.src}"></a>'
+ '</li>'
+ '</ul>';
/**
Using Bootstrap's dropdown menu
*/
var uiNode = jQuery('<div>').attr('data-toggle', 'dropdown').attr('dropdown', 'dropdown');
uiNode.addClass('btn-group pull-right');
uiNode.addClass('vjs-control');
uiNode.addClass('dropup');
uiNode.html(ui);
/**
since uiNode is JQuery wrapped, we must return uiNode[0] because this function must return
an HTML element, not a JQuery object
*/
return uiNode[0];
};
vjs.SelectBitratePopupMenu.prototype.updateLabel = function() {
/* you can use JQuery to manipulate the label using class selectors as usual */
};
Because I'm using angular I then have to call a $compile on the player's various children inside a player.ready handler:
player.ready(vjs.bind(this, function () {
$compile(player.controlBar.el_)($scope);
$compile(player.splashMenu.el_)($scope);
$timeout(function() {
//kicks off some animations
jQuery(".vjs-splash-menu").addClass("in-on");
});
}));
This is specific to creating a control bar button but it doesn't have to be a controlBar child or even a button.
what i understand from drilling the videojs ui component in my ide is whenever we want to add an element to the player we need first to extend to button class, but what if the element is not a button and outiside the controlbar ( a layer over the video with a centered button to replay the video when it ends).
could give me a roadmap please
Sorry for having over complicated it... You don't have to use grunt if you don't want to add your component into the videojs-dev file, and you certainly don't need to use angular if you want to attach your click handlers the javascript way (or the videojs way).
Here's how I implemented a title screen. The basic steps to add a component that is not a button and is outside the control bar are as follows:
1) create a component that extends vjs.Component (not Button). (when I create buttons using JQuery I just extend vjs.Component anyway)
You need to implement the init function:
vjs.TitleScreen = vjs.Component.extend({
init: function (player, options) {
vjs.Component.call(this, player, options);
/**
A hook to cause the TitleScreen to go away, though I left out the implementation below.
You might also add a listener to make it go away if the user just seeks to some position.
*/
this.on(this.player_, 'firstplay', this.hideTitleScreen);
/*
A hook to make it re-appear after the video has ended. Just use JQuery
selector hooks in this function, if you're referencing 'this' then you'll have
to bind the function.
*/
this.on(this.player_, 'ended', this.showTitleScreen);
/*
using binding it would be:
this.on(this.player_, 'ended', vjs.bind(this, this.showTitleScreen) );
in which case 'showTitleScreen' could reference this.el_ and this.options_
(or any other member of vjs.Component) directly. This also applied to the
hideTiteScreen function referenced above.
*/
}
});
You also need to implement the createEl function (which must return an html node). If you're using JQuery you can do this:
vjs.TitleScreen.prototype.createEl = function() {
var ui = '<div class="row">'
+ '<h4>' + this.options_['h4text'] + '</h4>'
+ '</div>'
+ '<div class="row"><h3>' + this.options_['h3text'] + '</h3></div>'
+ '<div class="row">'
+ '<a href="#" class="btn btn-primary title-play-btn" role="button">'
+ 'Play Video <i class="fa fa-play"></i>'
+ '</a></div>'
var uiNode = jQuery('<div/>')
//add whatever classes you need to the root element of your TitleScreen (or whatever) component.
uiNode.addClass('splash-menu');
uiNode.addClass('in');
uiNode.addClass('container-fluid');
/*
add the markup defined above. if not using jQuery it would have to be
created through vjs.createEl() and HTMLNode.appendChild(), which can
be exhausting and make future changes cumbersome
*/
uiNode.html(ui);
return uiNode[0];
};
But then you need to attach any click handlers later in a player.ready event handler after the player has been created (shown in step 3).
2) Make sure the javascript file where you define your component is loaded into your containing html page after videojs.
3) Add your non-button component as a child to the videojs setup object's children, and give it whatever data it needs to create the labels. Setup your videojs player like so:
jQuery(document).ready( function() {
var playerSetup = {
'techOrder': ['html5'],
'controls': true,
'children': {
/*
Here is where we add the component, note the lowercase first letter of the component name-
its not a typo. "vjs.TitleScreen" must be named as "titleScreen" here:
*/
'titleScreen':{
/* these will end up in the options_ array I used in createEl() */
'h3text':'Here is a title string'.
'h4text':'Here is other text'
}
}// end children
/* I've left out other setup settings like src and tracks, but you should set those here too. */
};// end playerSetup
/*
Create videojs player
*/
var player = videojs("id_of_element_to_hold_videojs", playerSetup, function () {
/* you can do other setup tasks that require the player to already exist, like register
listeners for videojs events. */
});
/*
Here is where you setup click handlers for the TitleScreen component, using
jQuery selectors in the usual way. You could put this in the createEl() function of your component,
but I've shown it here in case its doing something complicated that needs to call other application
logic not defined in the scope of a videojs player. You really don't want createEl doing anything other
than creating your component's HTML.
*/
player.ready(function() {
jQuery(".title-play-btn").on("click", function(event) {
event.preventDefault();
player.play();
});
});
}); //end jQuery's document.ready handler
And that ought to do it...
Hi, but how do i change the order of new button, in other words, i added new button to embed, but i appears in end of control bar, and i want that fullscreen its last.
Already try:
var button = new videojs.EmbedButton(player, {
location: options.location,
el: vjsComponent.prototype.createEl('button', {
className: 'vjs-embed-button vjs-control vjs-button ',
innerHTML: 'Embed'
},{
type: 'button',
title: 'Embed',
'aria-live': 'polite',
tabIndex: 0
})
});
player.controlBar.insertBefore(button, "vjs-fullscreen-control");
But not work!
Thanks!
How to pass a value back end with a vjs-big-play-button class name?
How to add custom button in videojs 5.11.6. i want to add a custom "cc" button
Most helpful comment
How to add custom button in videojs 5.11.6. i want to add a custom "cc" button