Bootstrap Modal Dynamic Youtube Embed, JavaScript

Using Twitter Bootstrap 3.0, I wanted to use its modal window to open YouTube videos.

Rather than manually placing  the HTML for each video’s modal, or creating a line of JavaScript for each separate video, I wanted any YouTube link to automatically pop up embedded in a modal window, without extra code. The modal is a progressive enhancement. The link should work normally without any JavaScript. You could also disable the modal functionality for small devices on a responsive design.

VIEW DEMO ON CODEPEN

Let’s say I have a link on the page in one of the following formats:

In order to generate an iframe embed code, we’ll first need to get the video ID from the URL. Then, put this video ID into the iframe embed format that YouTube gives you under “Share”, and open that in a modal.  I’ve added the data-width and data-height attributes, which will be used to determine the size of the modal window and embed. If these are not set, a default size will be used.

Modal HTML

I’m going to use empty modal HTML that’s placed right before the closing body tag, rather than using an external page. Note the ID, “mediaModal”, which will be used in the JavaScript. This is the basic modal template provided on the official Bootstrap 3.0 docs.

This only needs to be included on the page once, as multiple videos will open in this same modal container:

jQuery – Open all YouTube video links in a modal window

Explanation

First we find all links that begin with http://www.youtube.com, using the attribute-starts-with selector. Then we need to get the video ID from the video URL. I decided to use the plugin on GitHub “jQuery Query Parser” (open source MIT license), to parse out the query string variables and their values. This makes it a little more future-proof, as it will better handle URLs that contain more parameters (HD, rel=0, etc) and malformed links. The minified version of this plugin is pretty small.

The script then goes and inserts the YouTube iframe HTML into the contents of .modal-body inside of #mediaModal. If you use the data-height and data-width, they’re used in the embed, else it defaults to 560×315.

Twitter bootstrap’s modal window is a little limited, compared to some lightboxes. It will not dynamically size itself to fit the content, though you can have it be a percentage width via CSS for responsive sites. In this case, I’d like the window to be just big enough for the video, so that requires calculating a few widths.

The .modal-body and .modal-dialog elements both default to having padding, that could be modified in your CSS. So the script adds the left and right padding of both, to the width of the video, in order to get the final width that the .modal-dialog container should be. This is hooked into the show.bs.modal event.

Then finally, the modal window is opened with modal().

Note: Bad NPObject? Blocked a frame with origin?

If the iframe code that isn’t being inserted into the DOM does not include &wmode=transparent on the src link (it does in the above example), you may start getting these errors:

  • Chrome: Blocked a frame with origin “http://www.youtube.com” from accessing a frame with origin
  • Firefox:  Error bad NPObject

This caused a lot of frustrations, because everything online was saying that this was a cross-domain issue or a browser bug, etc. The video would work in Chrome, but would be black and not work in Firefox. Hopefully that helps if you’re struggling with the same issue! Note that Chrome will still give an origin error from within the frame which will not stop JS processing on your page, and appears to be a bug in Chrome that isn’t fixed yet (per some StackOverflow posts). The video still plays and operates fine.

Comments on this Article

  1. Justin says:

    This is pretty great.. thank you for sharing.

    I have a question though. *I’m a novice with JQuery and JavaScript*

    I have around 20 unique modals (bootstrap 3) on my site. Each modal has a unique youtube video embedded in it. To do this for my site, do you recommend I script this JQuery 20 different times, each with the appropriate corresponding Modal ID?

    Thanks so much..

    Justin

    • Josh says:

      I wrote this script to avoid and replace the type of situation you are describing, where there are 20+ unique modal IDs. I try to stick to the DRY principle (Don’t Repeat Yourself). It only requires the single “Video / Generic Modal” HTML, and then the embed code is dynamically inserted into that. If the modals are only for video (no other content), then this script could be used to replace all of those.

  2. Brian says:

    Do you have a link showing this in action? I think its what I want, but would like to eyeball it. Thanks.

  3. crazy4groovy says:

    “&wmode=transparent” is PURE GOLD! Thanks man!!

  4. Derrick says:

    For a responsive modal that automatically sizes the modal to fit the current display, change these three lines:

    // Variables for iFrame code. Width and height from data attributes, else use default.
    var vidWidth = 560; // default
    var vidHeight = 315; // default

    …to this:

    // Calculate default iFrame embed size based on current window size
    // (these will only be used if data attributes are not specified)
    if ($(window).height() < $(window).width()) {
        var vidHeight = $(window).height() * 0.7;
        var vidWidth = vidHeight * 1.77777;
    } else {
        var vidWidth = $(window).width() * 0.9;
        var vidHeight = vidWidth / 1.77777;
    }
    

    I wanted to submit this to the gist repo, but couldn't find a way to do so.

    • Josh says:

      Great. Thanks for sharing.
      I think it’d be a good idea to add a max width too, so the video does not get too huge/stretched for giant monitors (or else a max-width may be on the modal itself).

  5. Faiz says:

    Thanks a lot. Useful Code 🙂

  6. Gregor Volkmann says:

    Thanks, helped me with my Bandspage! But I don’t like your approach on using the jQuery Parser, I just did:

    var queryStringArray = queryString.split("&");
    var queryVars = new Array();

    for(i = 0; i < queryStringArray.length; i++) {
    queryVars[queryStringArray[i].split("=")[0]] = queryStringArray[i].split("=")[1];
    }

    Results in one less dependency.

    Cheers, Gregor from My Friend The Immigrant

  7. matt says:

    Weird, embedded Gist’s don’t show up in Chrome on Mac…

    Refused to execute script from 'https://gist.github.com/cf4add71f2d3e138cdd3.js?file=' because its MIME type ('text/html') is not executable, and strict MIME type checking is enabled. www.joshuawinn.com/:1
    Refused to execute script from 'https://gist.github.com/0e936461be263d7e7474.js?file=' because its MIME type ('text/html') is not executable, and strict MIME type checking is enabled. www.joshuawinn.com/:1
    Refused to execute script from 'https://gist.github.com/6655039.js?file=' because its MIME type ('text/html') is not executable, and strict MIME type checking is enabled. www.joshuawinn.com/:1

    • Josh says:

      Odd..are you still receiving this error? I saw a few issues about this online related to the nosniff header and RAW.github.com, but this really shouldn’t be happening with GIST.github.com’s embed, unless its a problem with the plugin that displays it. I hope it was a temporary issue.

  8. Tony says:

    Briliiant! Looks like there is a problem when you use the https. It will not load on the modal. Would you know how to fix it?

    • Keith McLaughlin says:

      @Tony, just remove the http:// part from line 8 in the JavaScript code.

      • Keith McLaughlin says:

        @Tony, my last comment was incorrect. Replace line 8 with this:

        $('a[href^="http://www.youtube.com"], [href^="https://www.youtube.com"]').on('click', function(e){

  9. Wolfy says:

    Hi there,
    Thank you so much for this. It’s really nifty. I was trying to get this to work with a vimeo video embed but it doesn’t seem to be working. Here is the code I am using

    $(document).ready(function(){
    // BOOTSTRAP 3.0 – Open Vimeo Video Dynamicaly in Modal Window
    // Modal Window for dynamically opening videos
    $(‘a[href^=”https://www.vimeo.com”]’).on(‘click’, function(e){
    // Store the query string variables and values
    // Uses “jQuery Query Parser” plugin, to allow for various URL formats (could have extra parameters)
    var queryString = $(this).attr(‘href’).slice( $(this).attr(‘href’).indexOf(‘?’) + 1);
    var queryVars = $.parseQuery( queryString );

    // if GET variable “v” exists. This is the Vimeo Video ID
    if ( ‘v’ in queryVars )
    {
    // Prevent opening of external page
    e.preventDefault();

    // Variables for iFrame code. Width and height from data attributes, else use default.
    var vidWidth = 560; // default
    var vidHeight = 315; // default
    if ( $(this).attr(‘data-width’) ) { vidWidth = parseInt($(this).attr(‘data-width’)); }
    if ( $(this).attr(‘data-height’) ) { vidHeight = parseInt($(this).attr(‘data-height’)); }
    var iFrameCode = ”;

    // Replace Modal HTML with iFrame Embed
    $(‘#mediaModal .modal-body’).html(iFrameCode);
    // Set new width of modal window, based on dynamic video content
    $(‘#mediaModal’).on(‘show.bs.modal’, function () {
    // Add video width to left and right padding, to get new width of modal window
    var modalBody = $(this).find(‘.modal-body’);
    var modalDialog = $(this).find(‘.modal-dialog’);
    var newModalWidth = vidWidth + parseInt(modalBody.css(“padding-left”)) + parseInt(modalBody.css(“padding-right”));
    newModalWidth += parseInt(modalDialog.css(“padding-left”)) + parseInt(modalDialog.css(“padding-right”));
    newModalWidth += ‘px’;
    // Set width of modal (Bootstrap 3.0)
    $(this).find(‘.modal-dialog’).css(‘width’, newModalWidth);
    });

    // Open Modal
    $(‘#mediaModal’).modal();
    }
    });

    // Clear modal contents on close.
    // There was mention of videos that kept playing in the background.
    $(‘#mediaModal’).on(‘hidden.bs.modal’, function () {
    $(‘#mediaModal .modal-body’).html(”);
    });

    });

    • Josh says:

      There is no “v” in the query string used in Vimeo videos. This would need to be changed to use the Vimeo URL format, which appears to be a number after vimeo.com. And the embed code would need to change.

  10. Dani says:

    how can this be implemented on images? i set up everything but somehow my image link goes straight to the YouTube page, no modal window.

    • Josh says:

      This isn’t really intended for images. You could check the file extension on the link and do something different if it’s an image.

  11. Weird gentleman says:

    That’s a really cool solution, definitely gonna use it in my next bootstrap projects instead of implementing third party lightbox that supports video. Thanks for sharing!

  12. Sb says:

    how do you disable the modal functionality on small devices?

    • Josh says:

      You could check the window width with $(window).width().

  13. Roland says:

    Hi Josh

    Precisely the idea I was looking for. I would like to know if I could follow the same script in opening up a Contact Form within the modal body, remotely?

  14. Yoshi says:

    I’m very new to this and would like a little more explanation if possible.

    1. Is all of this code on the html page, or do these need to be on seperate files of some kind?

    2. In the jquery portion you wrote

    // REQUIRED: Include “jQuery Query Parser” plugin here or before this point:
    // https://github.com/mattsnider/jquery-plugin-query-parser

    how is this done?

    Thanks.

  15. Yoshi says:

    Thanks for replying but I am still unsure how to actually include the parser. What do I download, where do I put it, or do I copy it and paste it somewhere? 😛 Thanks again.

  16. Yoshi says:

    I finally got it to work! Could someone please tell me how to make the youtube video autoplay in this code? thanks.

    • KWIT LLC says:

      I would also like a way to set autoplay. Adding it as a querystring doesn’t work https://www.youtube.com/watch?v=VIDID?autoplay=1&rel=0&enablejsapi=1&playerapiid=ytplayer"

      • Josh says:

        You have two question marks there in the URL. Try with an ampersand after the video ID?

Comments are closed.