Creating a Video.js Social Plugin

August 5, 2014 8:56 pm

In this tutorial we’ll be walking through a simple sharing plugin similar to the Sublime Video share button and overlay.

demoOfOurPlugin
A preview of our plugin.

You can view a demo here: http://brianpkelley.github.io/video-js-4-plugins/demos/socialOverlay.html

We’ll be using:

  • Javascript
  • CSS
  • FontAwesome 4.1.0

The Plugin Setup

When creating a plugin file for Video.js it is a best practice to enclose your functions inside a closure.

(function() {
    'use strict';

})();

This allows all of code to be accessible without cluttering up the global namespace, keeping everything nice and tidy.

videojs.plugin

The videojs.plugin function passes your plugin function an options object which we’ll set up later in this tutorial, and its initial scope is the player that is initializing the plugin, so this references the player object.

We’ll be creating a function that will call the constructor for our plugin and passing it to the videojs.plugin function.

(function() {
    'use strict';
    // Plugin components will go here

    /******************************************/
    /*          The Plugin Function           */
    /******************************************/
    // This function will be called by video.js when it loops through all of the registered plugins.
    var pluginFn = function( options ) {

    };

    videojs.plugin( 'sharingPlugin', pluginFn );
})();

Video.js Components

The video.js components are the building blocks for almost every UI object in a player. Some examples are:

  • Big Play Button
  • Play / Pause Toggle
  • The Control Bar
  • The Video.js Poster Image

As you can see, it is quite the flexible base class that provides heaps of core functionality from showing and hiding to adding and removing children, events, and plenty more. For a complete look at it, check out the component documentation provided by the Video.js team.

We’re first going to create the button used to open the overlay. To do this, we’ll be using a child class of videojs.Component convienently named videojs.Button

(function() {
    'use strict';

    /******************************************/
    /*            The Share Button            */
    /******************************************/
    // Create the button
    videojs.ShareButton = videojs.Button.extend({
        init: function( player, options ) {
            // Initialize the button using the default constructor
            videojs.Button.call( this, player, options );           
        }
    });

    // Set the text for the button
    videojs.ShareButton.prototype.buttonText = 'Share Video';

    // These are the defaults for this class.
    videojs.ShareButton.prototype.options_ = {};

    // videojs.Button uses this function to build the class name.
    videojs.ShareButton.prototype.buildCSSClass = function() {
        // Add our className to the returned className
        return 'vjs-share-button ' + videojs.Button.prototype.buildCSSClass.call(this);
    };

    // videojs.Button already sets up the onclick event handler, we just need to overwrite the function
    videojs.ShareButton.prototype.onClick = function( e ) {
        // Add specific click actions here.
    };


    /******************************************/
    /*          The Plugin Function           */
    /******************************************/
    // This function will be called by video.js when it loops through all of the registered plugins.
    var pluginFn = function( options ) {

    };

    videojs.plugin( 'sharingPlugin', pluginFn );
})();

The above code has a lot going on.

  • The extend method of videojs.Component creates a child class of itself, so the ShareButton family tree would resemble
    Component -> Button -> ShareButton
  • The init function initializes our plugin, and in our case, we just need to use the default videojs.Button init method.
  • videojs.ShareButton.prototype.buttonText is the label for the new button. This will be hidden to normal users, but is used by screen readers and other types of non visual browsers.
  • videojs.ShareButton.prototype.options_ are the class variable defaults. The videojs.Component consturctor merges the options object you pass it with the defult options specified.
  • videojs.ShareButton.prototype.buildCSSClass constructs the className string used by the createEl method. This is where we add our specialized classes and call the videojs.Button buildCSSClass method to get core classNames used by the ancestor classes.
  • videojs.ShareButton.prototype.onClick is defined in the base videojs.Button class which also sets up the event handling for click and touch events.

The videojs.Component consturctor does some heavy lifting for you, it calls your objects createEl method which creates and returns the HTML structure of you component in the form of this.el_. Generally buttons have a main root element as this.el_ which uses the css ::before psuedo element for the text and contains an element that is hidden that contains the text of the button. This gives you an HTML structure which should look like this.

videojsButtonStructure
The standard structure of a videojs.Button

Let’s Add the Button

Now that we have a component to use, we can add it to the player. To accomplish this we modify our plugin function and first instantiate the ShareButton object and then use the videojs.Componenet method addChild.

(function() {
    'use strict';

    /******************************************/
    /*            The Share Button            */
    /******************************************/
    // Create the button
    videojs.ShareButton = videojs.Button.extend({
        init: function( player, options ) {
            // Initialize the button using the default constructor
            videojs.Button.call( this, player, options );           
        }
    });

    // Set the text for the button
    videojs.ShareButton.prototype.buttonText = 'Share Video';

    // These are the defaults for this class.
    videojs.ShareButton.prototype.options_ = {};

    // videojs.Button uses this function to build the class name.
    videojs.ShareButton.prototype.buildCSSClass = function() {
        // Add our className to the returned className
        return 'vjs-share-button ' + videojs.Button.prototype.buildCSSClass.call(this);
    };

    // videojs.Button already sets up the onclick event handler, we just need to overwrite the function
    videojs.ShareButton.prototype.onClick = function( e ) {
        // Add specific click actions here.
    };


    /******************************************/
    /*          The Plugin Function           */
    /******************************************/
    // This function will be called by video.js when it loops through all of the registered plugins.
    var pluginFn = function( options ) {
        // We need to pass off the options to the button.
        var shareComponent = new videojs.ShareButton( this, options );

        // Now lets add it to the player.
        var shareButton = this.addChild( shareComponent );
    };

    videojs.plugin( 'sharingPlugin', pluginFn );
})();

Creating the Overlay

Here is our first custom component. We need to define the components HTML structure, we’ll be using just a simple overlay background and container. Generally, the root element and the content element (where children components are added) are the same element. In our caes, the root element needs to be the overlay, while future children (Social Logos) need to be added to the container. To acheive this behavior we need to assign the container to this.contentEl_ while which ever element we return from createEl (the overlay) will automatically become this.el_.

(function() {
    'use strict';

    /******************************************/
    /*            The Share Button            */
    /******************************************/
    // Create the button
    videojs.ShareButton = videojs.Button.extend({
        init: function( player, options ) {
            // Initialize the button using the default constructor
            videojs.Button.call( this, player, options );           
        }
    });

    // Set the text for the button
    videojs.ShareButton.prototype.buttonText = 'Share Video';

    // These are the defaults for this class.
    videojs.ShareButton.prototype.options_ = {};

    // videojs.Button uses this function to build the class name.
    videojs.ShareButton.prototype.buildCSSClass = function() {
        // Add our className to the returned className
        return 'vjs-share-button ' + videojs.Button.prototype.buildCSSClass.call(this);
    };

    // videojs.Button already sets up the onclick event handler, we just need to overwrite the function
    videojs.ShareButton.prototype.onClick = function( e ) {
        // Add specific click actions here.
    };


    /******************************************/
    /*           The Share Overlay            */
    /******************************************/
    // Create the overlay and container for the links
    videojs.ShareContainer = videojs.Component.extend({
        init: function( player, options ) {
            // call the parent constructor
            videojs.Component.call( this, player, options );
        }
    });

    // These are the child objects of this component.  Add or remove to show/hide from the overlay
    videojs.ShareContainer.prototype.options_ = {
        children: {}
    };

    // This function will be called by the videojs.Component constructor. 
    videojs.ShareContainer.prototype.createEl = function( tagName, props ) {
        // Create the elements

        // The black and blury overlay
        var overlay = videojs.createEl( 'div', {
            className: 'vjs-sharing-overlay'
        });

        // The container for the links/logos of the social sites we wish to offer
        var container = videojs.createEl( 'div', {
            className: 'vjs-sharing-container'
        });

        // this.contentEl is the element that children (links/logos) are added to.
        this.contentEl_ = container;

        overlay.appendChild( this.contentEl_ );

        // This will become "this.el_"
        return overlay;
    };


    /******************************************/
    /*          The Plugin Function           */
    /******************************************/    
    // This function will be called by video.js when it loops through all of the registered plugins.
    var pluginFn = function( options ) {
        // We need to pass off the options to the button.
        var shareComponent = new videojs.ShareButton( this, options );

        // Now lets add it to the player.
        var shareButton = this.addChild( shareComponent );
    };

    videojs.plugin( 'sharingPlugin', pluginFn );
})();

Launching and Hiding the Overlay

We begin by overwriting the standard videojs.Button.prototype.onClick method with our own videojs.ShareButton.prototype.onClick. In this function we will create an overlay object, add it to the player, and tell it to show itself as well as assign a one time event listener for a click event on the window that will tell the overlay to close.

(function() {
    'use strict';

    /******************************************/
    /*            The Share Button            */
    /******************************************/
    // Create the button
    videojs.ShareButton = videojs.Button.extend({
        init: function( player, options ) {
            // Initialize the button using the default constructor
            videojs.Button.call( this, player, options );           
        }
    });

    // Set the text for the button
    videojs.ShareButton.prototype.buttonText = 'Share Video';

    // These are the defaults for this class.
    videojs.ShareButton.prototype.options_ = {};

    // videojs.Button uses this function to build the class name.
    videojs.ShareButton.prototype.buildCSSClass = function() {
        // Add our className to the returned className
        return 'vjs-share-button ' + videojs.Button.prototype.buildCSSClass.call(this);
    };

    // videojs.Button already sets up the onclick event handler, we just need to overwrite the callback
    videojs.ShareButton.prototype.onClick = function( e ) {
        // We need to stop this event before it bubbles up to "window" for our event listener below.
        e.stopImmediatePropagation();


        // There are a few ways to accomplish opening the overlay
        // I chose to create and destroy the overlay to demonstrate
        // the dispose method.  Creating the component as a child is
        // the direction I would choose.
        this.overlay_ = new videojs.ShareContainer( this.player_, this.options_ );

        // We need to add an event listener to close the overlay when the user clicks outside the overlay / player.
        // Because we are destroying the overlay, we only need to listen to this once.
        videojs.one( window, 'click', videojs.bind( this, this.windowClick ) );

        // Add the overlay to the player
        this.player_.addChild( this.overlay_ );

        // Call the show method to apply effects and display the overlay
        this.overlay_.show();

        // Pause the video
        this.player_.pause();
    };

    videojs.ShareButton.prototype.windowClick = function( e ) {
        // Remove it from the parent (player)
        this.player_.removeChild( this.overlay_ );

        // Now remove it from memory.
        // The dispose method removes the element and component.
        this.overlay_.dispose();

        // We no longer use this variable so release it.
        delete this.overlay_;
    };


    /******************************************/
    /*           The Share Overlay            */
    /******************************************/
    // Create the overlay and container for the links
    videojs.ShareContainer = videojs.Component.extend({
        init: function( player, options ) {
            // call the parent constructor
            videojs.Component.call( this, player, options );
        }
    });

    // These are the child objects of this component.  Add or remove to show/hide from the overlay
    videojs.ShareContainer.prototype.options_ = {
        children: {}
    };

    // This function will be called by the videojs.Component constructor. 
    videojs.ShareContainer.prototype.createEl = function( tagName, props ) {
        // Create the elements

        // The black and blury overlay
        var overlay = videojs.createEl( 'div', {
            className: 'vjs-sharing-overlay'
        });

        // The container for the links/logos of the social sites we wish to offer
        var container = videojs.createEl( 'div', {
            className: 'vjs-sharing-container'
        });

        // this.contentEl is the element that children (links/logos) are added to.
        this.contentEl_ = container;

        overlay.appendChild( this.contentEl_ );

        // This will become "this.el_"
        return overlay;
    };


    /******************************************/
    /*          The Plugin Function           */
    /******************************************/    
    // This function will be called by video.js when it loops through all of the registered plugins.
    var pluginFn = function( options ) {
        // We need to pass off the options to the button.
        var shareComponent = new videojs.ShareButton( this, options );

        // Now lets add it to the player.
        var shareButton = this.addChild( shareComponent );
    };

    videojs.plugin( 'sharingPlugin', pluginFn );
})();

Creating the Custom Social Buttons

Now we just need some social icons to click! We’ll be creating a child of videojs.Button as a parent object for each type or brand of icon. We’ve set up this class to use a text and icon option, with the icon being optional and falling back to the text after being lowercased.

    /******************************************/
    /*            The Social Icons            */
    /******************************************/    
    // This is the base class for the share items.  Each Icon can be passed a "Name" and an icon class.
    videojs.OverlaySocialButton = videojs.Button.extend({
        init: function( player, options ) {
            videojs.Button.call(this, player, options );
        }
    });

    videojs.OverlaySocialButton.prototype.createEl = function(type, props){
        var el;

        // Add standard Aria and Tabindex info
        props = vjs.obj.merge({
            className: this.buildCSSClass(),
            'role': 'button',
            'aria-live': 'polite', // let the screen reader user know that the text of the button may change
            tabIndex: 0
        }, props);

        // 'i' is needed for the Font-Awesome icons
        el = vjs.Component.prototype.createEl.call(this, 'i', props);

        // if innerHTML hasn't been overridden (bigPlayButton), add content elements
        if (!props.innerHTML) {
            this.contentEl_ = vjs.createEl('div', {
                className: 'vjs-control-content'
            });

            this.controlText_ = vjs.createEl('span', {
                className: 'vjs-control-text',
                innerHTML: this.options_.text || 'Need Text'
            });

            this.contentEl_.appendChild(this.controlText_);
            el.appendChild(this.contentEl_);
        }

        return el;
    };

    videojs.OverlaySocialButton.prototype.buildCSSClass = function() {
        // We do not use the videojs.Button.prototype.buildCSSClass because we are not creating a typical component.
        return 'vjs-share-icon fa fa-' + this.options_.icon + '-square fa-5x';
    };

Now we have our base class we just need to extend it and add in the specialized onclick functions (I’ll be leaving these empty to allow you choose which method best suits yourself)

(function() {
    'use strict';

    /******************************************/
    /*            The Share Button            */
    /******************************************/
    // Create the button
    videojs.ShareButton = videojs.Button.extend({
        init: function( player, options ) {
            // Initialize the button using the default constructor
            videojs.Button.call( this, player, options );           
        }
    });

    // Set the text for the button
    videojs.ShareButton.prototype.buttonText = 'Share Video';

    // These are the defaults for this class.
    videojs.ShareButton.prototype.options_ = {};

    // videojs.Button uses this function to build the class name.
    videojs.ShareButton.prototype.buildCSSClass = function() {
        // Add our className to the returned className
        return 'vjs-share-button ' + videojs.Button.prototype.buildCSSClass.call(this);
    };

    // videojs.Button already sets up the onclick event handler, we just need to overwrite the callback
    videojs.ShareButton.prototype.onClick = function( e ) {
        // We need to stop this event before it bubbles up to "window" for our event listener below.
        e.stopImmediatePropagation();


        // There are a few ways to accomplish opening the overlay
        // I chose to create and destroy the overlay to demonstrate
        // the dispose method.  Creating the component as a child is
        // the direction I would choose.
        this.overlay_ = new videojs.ShareContainer( this.player_, this.options_ );

        // We need to add an event listener to close the overlay when the user clicks outside the overlay / player.
        // Because we are destroying the overlay, we only need to listen to this once.
        videojs.one( window, 'click', videojs.bind( this, this.windowClick ) );

        // Add the overlay to the player
        this.player_.addChild( this.overlay_ );

        // Call the show method to apply effects and display the overlay
        this.overlay_.show();

        // Pause the video
        this.player_.pause();
    };

    videojs.ShareButton.prototype.windowClick = function( e ) {
        // Remove it from the parent (player)
        this.player_.removeChild( this.overlay_ );

        // Now remove it from memory.
        // The dispose method removes the element and component.
        this.overlay_.dispose();

        // We no longer use this variable so release it.
        delete this.overlay_;
    };


    /******************************************/
    /*           The Share Overlay            */
    /******************************************/
    // Create the overlay and container for the links
    videojs.ShareContainer = videojs.Component.extend({
        init: function( player, options ) {
            // call the parent constructor
            videojs.Component.call( this, player, options );
        }
    });

    // These are the child objects of this component.  Add or remove to show/hide from the overlay
    videojs.ShareContainer.prototype.options_ = {
        children: {}
    };

    // This function will be called by the videojs.Component constructor. 
    videojs.ShareContainer.prototype.createEl = function( tagName, props ) {
        // Create the elements

        // The black and blury overlay
        var overlay = videojs.createEl( 'div', {
            className: 'vjs-sharing-overlay'
        });

        // The container for the links/logos of the social sites we wish to offer
        var container = videojs.createEl( 'div', {
            className: 'vjs-sharing-container'
        });

        // this.contentEl is the element that children (links/logos) are added to.
        this.contentEl_ = container;

        overlay.appendChild( this.contentEl_ );

        // This will become "this.el_"
        return overlay;
    };

    /******************************************/
    /*            The Social Icons            */
    /******************************************/    
    // This is the base class for the share items.  Each Icon can be passed a "Name" and an icon class.
    videojs.OverlaySocialButton = videojs.Button.extend({
        init: function( player, options ) {
            videojs.Button.call(this, player, options );
        }
    });


    videojs.OverlaySocialButton.prototype.createEl = function(type, props){
        var el;

        // Add standard Aria and Tabindex info
        props = vjs.obj.merge({
            className: this.buildCSSClass(),
            'role': 'button',
            'aria-live': 'polite', // let the screen reader user know that the text of the button may change
            tabIndex: 0
        }, props);

        // 'i' is needed for the Font-Awesome icons
        el = vjs.Component.prototype.createEl.call(this, 'i', props);

        // if innerHTML hasn't been overridden (bigPlayButton), add content elements
        if (!props.innerHTML) {
            this.contentEl_ = vjs.createEl('div', {
                className: 'vjs-control-content'
            });

            this.controlText_ = vjs.createEl('span', {
                className: 'vjs-control-text',
                innerHTML: this.options_.text || 'Need Text'
            });

            this.contentEl_.appendChild(this.controlText_);
            el.appendChild(this.contentEl_);
        }

        return el;
    };

    videojs.OverlaySocialButton.prototype.buildCSSClass = function() {
        // We do not use the videojs.Button.prototype.buildCSSClass because we are not creating a typical component.
        return 'vjs-share-icon fa fa-' + ( this.options_.icon || this.options_.text.toLowerCase() ) + '-square fa-5x';
    };


    // These are the indvidual buttons for each type of share.
    // Twitter  
    videojs.TwitterShare = videojs.OverlaySocialButton.extend({
        init: function( player, options ) {
            videojs.OverlaySocialButton.call( this, player, options );
        }
    });
    videojs.TwitterShare.prototype.options_ = { text: 'Twitter' };
    videojs.TwitterShare.prototype.onClick = function() {
        // Do Share action here
    };  

    // Facebook 
    videojs.FacebookShare = videojs.OverlaySocialButton.extend({
        init: function( player, options ) {
            videojs.OverlaySocialButton.call( this, player, options );
        }
    });
    videojs.FacebookShare.prototype.options_ = { text: 'Facebook' };
    videojs.FacebookShare.prototype.onClick = function() {
        // Do Share action here
    };  

    // Pinterest
    videojs.PinterestShare = videojs.OverlaySocialButton.extend({
        init: function( player, options ) {
            videojs.OverlaySocialButton.call( this, player, options );
        }
    });
    videojs.PinterestShare.prototype.options_ = { text: 'Pinterest' };
    videojs.PinterestShare.prototype.onClick = function() {
        // Do Share action here
    };

    // Google Plus
    videojs.GooglePlusShare = videojs.OverlaySocialButton.extend({
        init: function( player, options ) {
            videojs.OverlaySocialButton.call( this, player, options );
        }
    });
    videojs.GooglePlusShare.prototype.options_ = { icon: 'google-plus', text: 'Google+' };
    videojs.GooglePlusShare.prototype.onClick = function() {
        // Do Share action here
    };  

    // LinkedIn
    videojs.LinkedInShare = videojs.OverlaySocialButton.extend({
        init: function( player, options ) {
            videojs.OverlaySocialButton.call( this, player, options );
        }
    });
    videojs.LinkedInShare.prototype.options_ = { text: 'LinkedIn' };
    videojs.LinkedInShare.prototype.onClick = function() {
        // Do Share action here
    };

    /******************************************/
    /*          The Plugin Function           */
    /******************************************/    
    // This function will be called by video.js when it loops through all of the registered plugins.
    var pluginFn = function( options ) {
        // We need to pass off the options to the button.
        var shareComponent = new videojs.ShareButton( this, options );

        // Now lets add it to the player.
        var shareButton = this.addChild( shareComponent );
    };

    videojs.plugin( 'sharingPlugin', pluginFn );
})();

Adding the Icons to the Overlay

Next we specify which social logos we would like displayed. The videojs.ShareContainer.prototype.options_.children object contains the name of the social logo buttons we will create in the coming steps. Durring a components initialization process, these will be automatically created and appended to this.contentEl_.

(function() {
    'use strict';

    /******************************************/
    /*            The Share Button            */
    /******************************************/
    // Create the button
    videojs.ShareButton = videojs.Button.extend({
        init: function( player, options ) {
            // Initialize the button using the default constructor
            videojs.Button.call( this, player, options );           
        }
    });

    // Set the text for the button
    videojs.ShareButton.prototype.buttonText = 'Share Video';

    // These are the defaults for this class.
    videojs.ShareButton.prototype.options_ = {};

    // videojs.Button uses this function to build the class name.
    videojs.ShareButton.prototype.buildCSSClass = function() {
        // Add our className to the returned className
        return 'vjs-share-button ' + videojs.Button.prototype.buildCSSClass.call(this);
    };

    // videojs.Button already sets up the onclick event handler, we just need to overwrite the callback
    videojs.ShareButton.prototype.onClick = function( e ) {
        // We need to stop this event before it bubbles up to "window" for our event listener below.
        e.stopImmediatePropagation();


        // There are a few ways to accomplish opening the overlay
        // I chose to create and destroy the overlay to demonstrate
        // the dispose method.  Creating the component as a child is
        // the direction I would choose.
        this.overlay_ = new videojs.ShareContainer( this.player_, this.options_ );

        // We need to add an event listener to close the overlay when the user clicks outside the overlay / player.
        // Because we are destroying the overlay, we only need to listen to this once.
        videojs.one( window, 'click', videojs.bind( this, this.windowClick ) );

        // Add the overlay to the player
        this.player_.addChild( this.overlay_ );

        // Call the show method to apply effects and display the overlay
        this.overlay_.show();

        // Pause the video
        this.player_.pause();
    };

    videojs.ShareButton.prototype.windowClick = function( e ) {
        // Remove it from the parent (player)
        this.player_.removeChild( this.overlay_ );

        // Now remove it from memory.
        // The dispose method removes the element and component.
        this.overlay_.dispose();

        // We no longer use this variable so release it.
        delete this.overlay_;
    };


    /******************************************/
    /*           The Share Overlay            */
    /******************************************/
    // Create the overlay and container for the links
    videojs.ShareContainer = videojs.Component.extend({
        init: function( player, options ) {
            // call the parent constructor
            videojs.Component.call( this, player, options );
        }
    });

    // These are the child objects of this component.  Add or remove to show/hide from the overlay
    videojs.ShareContainer.prototype.options_ = {
        children: {
            'facebookShare': {},
            'twitterShare': {},
            'pinterestShare': {},
            'googlePlusShare': {},
            'linkedInShare': {}
        }
    };

    // This function will be called by the videojs.Component constructor. 
    videojs.ShareContainer.prototype.createEl = function( tagName, props ) {
        // Create the elements

        // The black and blury overlay
        var overlay = videojs.createEl( 'div', {
            className: 'vjs-sharing-overlay'
        });

        // The container for the links/logos of the social sites we wish to offer
        var container = videojs.createEl( 'div', {
            className: 'vjs-sharing-container'
        });

        // this.contentEl is the element that children (links/logos) are added to.
        this.contentEl_ = container;

        overlay.appendChild( this.contentEl_ );

        // This will become "this.el_"
        return overlay;
    };

    /******************************************/
    /*            The Social Icons            */
    /******************************************/    
    // This is the base class for the share items.  Each Icon can be passed a "Name" and an icon class.
    videojs.OverlaySocialButton = videojs.Button.extend({
        init: function( player, options ) {
            videojs.Button.call(this, player, options );
        }
    });


    videojs.OverlaySocialButton.prototype.createEl = function(type, props){
        var el;

        // Add standard Aria and Tabindex info
        props = vjs.obj.merge({
            className: this.buildCSSClass(),
            'role': 'button',
            'aria-live': 'polite', // let the screen reader user know that the text of the button may change
            tabIndex: 0
        }, props);

        // 'i' is needed for the Font-Awesome icons
        el = vjs.Component.prototype.createEl.call(this, 'i', props);

        // if innerHTML hasn't been overridden (bigPlayButton), add content elements
        if (!props.innerHTML) {
            this.contentEl_ = vjs.createEl('div', {
                className: 'vjs-control-content'
            });

            this.controlText_ = vjs.createEl('span', {
                className: 'vjs-control-text',
                innerHTML: this.options_.text || 'Need Text'
            });

            this.contentEl_.appendChild(this.controlText_);
            el.appendChild(this.contentEl_);
        }

        return el;
    };

    videojs.OverlaySocialButton.prototype.buildCSSClass = function() {
        // We do not use the videojs.Button.prototype.buildCSSClass because we are not creating a typical component.
        return 'vjs-share-icon fa fa-' + ( this.options_.icon || this.options_.text.toLowerCase() ) + '-square fa-5x';
    };


    // These are the indvidual buttons for each type of share.
    // Twitter  
    videojs.TwitterShare = videojs.OverlaySocialButton.extend({
        init: function( player, options ) {
            videojs.OverlaySocialButton.call( this, player, options );
        }
    });
    videojs.TwitterShare.prototype.options_ = { text: 'Twitter' };
    videojs.TwitterShare.prototype.onClick = function() {
        // Do Share action here
    };  

    // Facebook 
    videojs.FacebookShare = videojs.OverlaySocialButton.extend({
        init: function( player, options ) {
            videojs.OverlaySocialButton.call( this, player, options );
        }
    });
    videojs.FacebookShare.prototype.options_ = { text: 'Facebook' };
    videojs.FacebookShare.prototype.onClick = function() {
        // Do Share action here
    };  

    // Pinterest
    videojs.PinterestShare = videojs.OverlaySocialButton.extend({
        init: function( player, options ) {
            videojs.OverlaySocialButton.call( this, player, options );
        }
    });
    videojs.PinterestShare.prototype.options_ = { text: 'Pinterest' };
    videojs.PinterestShare.prototype.onClick = function() {
        // Do Share action here
    };

    // Google Plus
    videojs.GooglePlusShare = videojs.OverlaySocialButton.extend({
        init: function( player, options ) {
            videojs.OverlaySocialButton.call( this, player, options );
        }
    });
    videojs.GooglePlusShare.prototype.options_ = { icon: 'google-plus', text: 'Google+' };
    videojs.GooglePlusShare.prototype.onClick = function() {
        // Do Share action here
    };  

    // LinkedIn
    videojs.LinkedInShare = videojs.OverlaySocialButton.extend({
        init: function( player, options ) {
            videojs.OverlaySocialButton.call( this, player, options );
        }
    });
    videojs.LinkedInShare.prototype.options_ = { text: 'LinkedIn' };
    videojs.LinkedInShare.prototype.onClick = function() {
        // Do Share action here
    };

    /******************************************/
    /*          The Plugin Function           */
    /******************************************/    
    // This function will be called by video.js when it loops through all of the registered plugins.
    var pluginFn = function( options ) {
        // We need to pass off the options to the button.
        var shareComponent = new videojs.ShareButton( this, options );

        // Now lets add it to the player.
        var shareButton = this.addChild( shareComponent );
    };

    videojs.plugin( 'sharingPlugin', pluginFn );
})();

Styling

At this point, we pretty much have a working plugin, albeit an unstyled working plugin. To acheive the blur effect we’ll need to apply some styling to the players root element and code up some pretty quick css.

In order to apply and remove the styles and class names we need on the root element and the display: table we use to center all the icons, we overwrite the default show, hide, and dispose methods.

(function() {
    'use strict';

    /******************************************/
    /*            The Share Button            */
    /******************************************/
    // Create the button
    videojs.ShareButton = videojs.Button.extend({
        init: function( player, options ) {
            // Initialize the button using the default constructor
            videojs.Button.call( this, player, options );           
        }
    });

    // Set the text for the button
    videojs.ShareButton.prototype.buttonText = 'Share Video';

    // These are the defaults for this class.
    videojs.ShareButton.prototype.options_ = {};

    // videojs.Button uses this function to build the class name.
    videojs.ShareButton.prototype.buildCSSClass = function() {
        // Add our className to the returned className
        return 'vjs-share-button ' + videojs.Button.prototype.buildCSSClass.call(this);
    };

    // videojs.Button already sets up the onclick event handler, we just need to overwrite the callback
    videojs.ShareButton.prototype.onClick = function( e ) {
        // We need to stop this event before it bubbles up to "window" for our event listener below.
        e.stopImmediatePropagation();


        // There are a few ways to accomplish opening the overlay
        // I chose to create and destroy the overlay to demonstrate
        // the dispose method.  Creating the component as a child is
        // the direction I would choose.
        this.overlay_ = new videojs.ShareContainer( this.player_, this.options_ );

        // We need to add an event listener to close the overlay when the user clicks outside the overlay / player.
        // Because we are destroying the overlay, we only need to listen to this once.
        videojs.one( window, 'click', videojs.bind( this, this.windowClick ) );

        // Add the overlay to the player
        this.player_.addChild( this.overlay_ );

        // Call the show method to apply effects and display the overlay
        this.overlay_.show();

        // Pause the video
        this.player_.pause();
    };

    videojs.ShareButton.prototype.windowClick = function( e ) {
        // Remove it from the parent (player)
        this.player_.removeChild( this.overlay_ );

        // Now remove it from memory.
        // The dispose method removes the element and component.
        this.overlay_.dispose();

        // We no longer use this variable so release it.
        delete this.overlay_;
    };


    /******************************************/
    /*           The Share Overlay            */
    /******************************************/
    // Create the overlay and container for the links
    videojs.ShareContainer = videojs.Component.extend({
        init: function( player, options ) {
            // call the parent constructor
            videojs.Component.call( this, player, options );
        }
    });

    // These are the child objects of this component.  Add or remove to show/hide from the overlay
    videojs.ShareContainer.prototype.options_ = {
        children: {
            'facebookShare': {},
            'twitterShare': {},
            'pinterestShare': {},
            'googlePlusShare': {},
            'linkedInShare': {}
        }
    };

    // This function will be called by the videojs.Component constructor. 
    videojs.ShareContainer.prototype.createEl = function( tagName, props ) {
        // Create the elements

        // The black and blury overlay
        var overlay = videojs.createEl( 'div', {
            className: 'vjs-sharing-overlay'
        });

        // The container for the links/logos of the social sites we wish to offer
        var container = videojs.createEl( 'div', {
            className: 'vjs-sharing-container'
        });

        // this.contentEl is the element that children (links/logos) are added to.
        this.contentEl_ = container;

        overlay.appendChild( this.contentEl_ );

        // This will become "this.el_"
        return overlay;
    };

    // To enable the video to blur, we must add some classess/styling to the root element.
    videojs.ShareContainer.prototype.show = function() {
        var techEl;
        var playerEl;

        // Do the default show method
        this.el_.style.display = 'table';

        // To get the blur effect, we need to add it to the tech element.
        // But we do not want to waste our time with trying to blur a flash element.
        if ( this.player_.techName != "Flash" ) {
            techEl = this.player_.tech.el();
            playerEl = this.player_.el();

            // We have to set the clip property here because it is dependent on the size of the player
            techEl.style.clip = 'rect(0 0 ' + playerEl.offsetWidth + 'px ' + playerEl.offsetHeight + 'px)';

            // Add our class to blur the video.
            videojs.addClass( techEl, 'vjs-blur' );
        }

        // Hide the controls, using opacity = 0 will use the default transition timing.
        this.player_.controlBar.el().style.opacity = '0';

        this.el_.style.opacity = '1';

    };

    videojs.ShareContainer.prototype.hide = function() {
        var techEl = this.player_.tech.el();

        // Do the default hide method
        videojs.Component.prototype.hide.call( this );

        // This time we don't care if it is flash, html, youtube etc
        techEl.style.clip = '';
        videojs.removeClass( techEl, 'vjs-blur' );

        // Show the controls, using opacity = '' will use the default opacity.
        this.player_.controlBar.el().style.opacity = '';
    };

    videojs.ShareContainer.prototype.dispose = function() {
        // Hide and remove classes from the tech element
        this.hide();

        // Do the default dispose method
        videojs.Component.prototype.dispose.call( this );

    };

    /******************************************/
    /*            The Social Icons            */
    /******************************************/    
    // This is the base class for the share items.  Each Icon can be passed a "Name" and an icon class.
    videojs.OverlaySocialButton = videojs.Button.extend({
        init: function( player, options ) {
            videojs.Button.call(this, player, options );
        }
    });


    videojs.OverlaySocialButton.prototype.createEl = function(type, props){
        var el;

        // Add standard Aria and Tabindex info
        props = vjs.obj.merge({
            className: this.buildCSSClass(),
            'role': 'button',
            'aria-live': 'polite', // let the screen reader user know that the text of the button may change
            tabIndex: 0
        }, props);

        // 'i' is needed for the Font-Awesome icons
        el = vjs.Component.prototype.createEl.call(this, 'i', props);

        // if innerHTML hasn't been overridden (bigPlayButton), add content elements
        if (!props.innerHTML) {
            this.contentEl_ = vjs.createEl('div', {
                className: 'vjs-control-content'
            });

            this.controlText_ = vjs.createEl('span', {
                className: 'vjs-control-text',
                innerHTML: this.options_.text || 'Need Text'
            });

            this.contentEl_.appendChild(this.controlText_);
            el.appendChild(this.contentEl_);
        }

        return el;
    };

    videojs.OverlaySocialButton.prototype.buildCSSClass = function() {
        // We do not use the videojs.Button.prototype.buildCSSClass because we are not creating a typical component.
        return 'vjs-share-icon fa fa-' + ( this.options_.icon || this.options_.text.toLowerCase() ) + '-square fa-5x';
    };


    // These are the indvidual buttons for each type of share.
    // Twitter  
    videojs.TwitterShare = videojs.OverlaySocialButton.extend({
        init: function( player, options ) {
            videojs.OverlaySocialButton.call( this, player, options );
        }
    });
    videojs.TwitterShare.prototype.options_ = { text: 'Twitter' };
    videojs.TwitterShare.prototype.onClick = function() {
        // Do Share action here
    };  

    // Facebook 
    videojs.FacebookShare = videojs.OverlaySocialButton.extend({
        init: function( player, options ) {
            videojs.OverlaySocialButton.call( this, player, options );
        }
    });
    videojs.FacebookShare.prototype.options_ = { text: 'Facebook' };
    videojs.FacebookShare.prototype.onClick = function() {
        // Do Share action here
    };  

    // Pinterest
    videojs.PinterestShare = videojs.OverlaySocialButton.extend({
        init: function( player, options ) {
            videojs.OverlaySocialButton.call( this, player, options );
        }
    });
    videojs.PinterestShare.prototype.options_ = { text: 'Pinterest' };
    videojs.PinterestShare.prototype.onClick = function() {
        // Do Share action here
    };

    // Google Plus
    videojs.GooglePlusShare = videojs.OverlaySocialButton.extend({
        init: function( player, options ) {
            videojs.OverlaySocialButton.call( this, player, options );
        }
    });
    videojs.GooglePlusShare.prototype.options_ = { icon: 'google-plus', text: 'Google+' };
    videojs.GooglePlusShare.prototype.onClick = function() {
        // Do Share action here
    };  

    // LinkedIn
    videojs.LinkedInShare = videojs.OverlaySocialButton.extend({
        init: function( player, options ) {
            videojs.OverlaySocialButton.call( this, player, options );
        }
    });
    videojs.LinkedInShare.prototype.options_ = { text: 'LinkedIn' };
    videojs.LinkedInShare.prototype.onClick = function() {
        // Do Share action here
    };

    /******************************************/
    /*          The Plugin Function           */
    /******************************************/    
    // This function will be called by video.js when it loops through all of the registered plugins.
    var pluginFn = function( options ) {
        // We need to pass off the options to the button.
        var shareComponent = new videojs.ShareButton( this, options );

        // Now lets add it to the player.
        var shareButton = this.addChild( shareComponent );
    };

    videojs.plugin( 'sharingPlugin', pluginFn );
})();

And the CSS

/* We are using the font awesome set. */
@import url('//maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css');  

/* This applies the blur filter to the video/preview etc */
.video-js .vjs-tech.vjs-blur {
    -webkit-transition: .75s all;
    transition: .75s all;

    -webkit-filter: blur(5px);
    filter: blur(5px);
}

/* Basic overlay styles, we are using the table-cell layout hack to center the content */
.video-js .vjs-sharing-overlay {
    background: rgba( 0, 0, 0, 0.6 );
    position: absolute;
    top: 0;
    left: 0;
    opacity: 0;
    -webkit-transition: .75s all;
    transition: .75s all;

    /* Root "Table" element for hack */
    display: table;
    height: 100%;
    width: 100%;
}


/* Icon for our initial button */
.vjs-control.vjs-share-button {
    cursor: pointer;
}
.vjs-control.vjs-share-button:before {
    font-family: FontAwesome;
}

/* The styles for an on-screen button */
.video-js > .vjs-control.vjs-share-button {
    position: absolute;
    top: 1em;
    right: 1em;
}
.video-js > .vjs-control.vjs-share-button:hover:before {
    text-shadow: 0 0 .3em rgba( 255,255,255,0.8);
}

/* The styles for the button on a control bar */
.vjs-control-bar .vjs-control.vjs-share-button:before {
    content: '\f064';
}

.video-js > .vjs-control.vjs-share-button:before {
    content: '\f064';
    font-size: 1.5em;
    color: rgba(255,255,255,0.75);
    background: rgba(7,20,30,.7);
    padding: 10px;
    height: 1em;
    line-height: 1em;
    border-radius: 15%;
    width: auto;
}





/* Styling for the icons */
.vjs-sharing-container {
    /* The table-cell of the hack */
    display: table-cell;
    height: 100%;
    width: 100%;

    vertical-align: middle;
    text-align: center;
}

/* Icon body */
.vjs-sharing-container .vjs-share-icon {
    font-size: 7em;
    margin: .2em;
    cursor: pointer;
    position: relative;
}
/* The actual Icon */
.vjs-sharing-container .vjs-share-icon:hover:before {
    color: #fff;
    text-shadow: 0 0 .5em rgba(255,255,255,0.5);
}

/* Show the text that is usually hidden in a videojs.Button */
.vjs-sharing-container .vjs-share-icon .vjs-control-text {
    position: absolute;
    width: 100%;
    font-size: .15em;
    font-weight: 700;
    text-align: center;
    left: 0;
    bottom: -1em;

    clip: initial;
    height: initial;
    margin: 0;
}



/* To show/hide the onscreen button.  Duplicate the showing / hiding of the control bar */

.vjs-has-started.vjs-user-inactive.vjs-playing > .vjs-control.vjs-share-button,
.video-js > .vjs-control.vjs-share-button {
    display: block;
    visibility: hidden;
    opacity: 0;
    -webkit-transition: visibility 1s, opacity 1s;
    -moz-transition: visibility 1s, opacity 1s;
    -o-transition: visibility 1s, opacity 1s;
    transition: visibility 1s, opacity 1s;
}

.vjs-has-started  > .vjs-control.vjs-share-button {
    display: block;
    visibility: visible;
    opacity: 1;
    -webkit-transition: visibility 0.1s, opacity 0.1s;
    -moz-transition: visibility 0.1s, opacity 0.1s;
    -o-transition: visibility 0.1s, opacity 0.1s;
    transition: visibility 0.1s, opacity 0.1s;
}

10 Comments


  • Todd
    October 25, 2014 at 2:32 am
    Permlink

    It’s driving me crazy. How do you change the share button? This uses fa fa-share (http://fortawesome.github.io/Font-Awesome/icon/share/) and I want to alter that. I can’t find the place where that class is called, so I have no idea how to go about this. I found the place to change the sharing buttons, but I can’t narrow this down. Can you give me an idea where to look for this?

    I also want to use this to display embed code and a short link to the video. If you have ideas how to add that to this overlay, throw those out, too. Much appreciated, both for this tutorial (which works quite well) and for any other advice you can give.


    • Todd
      October 27, 2014 at 6:18 pm
      Permlink

      Got it. It’s in the CSS. content: ‘\f064’; is what’s there now. A quick trip over to the FontAwesome CSS (http://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css) will give you the character code to use.

      Next question! How can this icon only appear on rollover? I don’t want the share icon to be there the whole time.


    • Brian Kelley
      January 15, 2015 at 3:29 pm
      Permlink

      Sorry, I must have missed the email notification for this.

      I’ve added in an embed feature for the plugin where I work, though it pretty specialize for our customized player. I’ll see if I can throw together something to help you.


      • sufyan
        August 8, 2015 at 5:53 am
        Permlink

        I want to create field where i give my embed iframe and share link of my video in this share overlay how could i do this


  • Todd
    October 27, 2014 at 6:24 pm
    Permlink

    The icon (along with the control bar at the bottom) goes away after a few seconds of not being rolledover. I should have just waited.

    Anyhow, thanks for this tutorial! I’ll be putting it to good use soon.


  • Greg
    April 23, 2015 at 6:02 pm
    Permlink

    This is nice, why does it require the _dev version of video.js? That’s a much larger file.


    • Brian Kelley
      July 28, 2015 at 3:31 pm
      Permlink

      Thanks!
      The dev version may or may not be required.

      I’ve become accustomed to using the dev version and just use it by default to avoid any headaches associated with tracking down certain methods or variables named “a” and “o” which can be renamed randomly in the next release. I also use a custom build process that minimizes without renaming variables.

      The videojs team has been pretty consistent on only revealing a very select group of core videojs functions. This makes it difficult to create a library agnostic plugin.


  • nerarosa
    August 2, 2015 at 12:45 pm
    Permlink

    Not working with videojs 4.12. how to fix, please???


  • Steven Sheffey
    November 24, 2015 at 7:38 pm
    Permlink

    It seems this tutorial doesn’t do anything after I put in the plugin code, nor does it give any errors. It simple does not work, I’m using video.js 4.10.2. Can anyone shed some light on how to get this to work?


  • Abhishek
    February 23, 2016 at 12:56 pm
    Permlink

    Can anyone tell me how to make it work with videojs 4.12 ? or fix the bug?

Leave a Reply to Todd Cancel reply

Your email address will not be published. Required fields are marked *