Using WordPress media shortcodes (audio, video, playlist) powered by mediaelement.js in ajax loaded content.

A recent project called for dynamically requesting custom post type content in a modal window. The content was loaded through an ajax request and inserted into the modal window container. That content just so happened to include various combinations of WordPress’s built-in media player through the usage of shortcodes. The single post template, with all the header and footer full script processing worked fine, but loading the content alone in the modal window was resulting in broken, non-functioning players, because the appropriate mediaelement player scripts were not enqueued and subsequent calls to initialize the players never happened.

We don’t want to complicate matters by stuffing the whole page into the modal or forcing the user off to the single page while they’re trying to browse through multiple audio and video files, so here’s how we fixed it.

First things first, loading the actual post content via an ajax request. We wanted the single post template to remain available as a fallback, so a simple conditional check was added to the single post template. Loading the page directly will behave normally, while our ajax request will append a query string variable, which will add a close button instead of the normal site header. We’re using the wonderful Magnific Popup library for our modal window, so we’ll use the standard markup for those close buttons:

<?php 
  if(!isset($_GET['ajax'])):
    get_header();
  else: 
    //these are usually loaded in a modal window
?>
  <button title="Close (Esc)" type="button" class="mfp-close">&times;</button>
<?php endif; ?>

The page from which these pieces of content will be loaded from is itself dynamically populated, so we bind to the container, grab the link that was clicked, append that “ajax” query string we’re using to detect whether or not to display the header in the single post template, stuffing the response into another container, and then displaying said container with Magnific Popup. So far, nothing really overly-complicated…

$('.grid-boxes').on('click','.modal-trigger', function(){
    var post_link = $(this).attr("href") + '?ajax';
    $("#content-popup").addClass('loading').html("loading...");
    $("#content-popup").load(post_link, function() { $('#content-popup').removeClass('loading'); });
    $.magnificPopup.open({
      items: { src: '#content-popup' },
      type: 'inline'
    });
    return false;
});

Now, for normal posts, with regular markup, words, and images, this all works fine. However, if you’re using WordPress’s built in media shortcodes – [playlist], [audio], or [video], you’ll notice that your players do not work! (Neither do Gravity Forms, but that’s a story for another blog post!)

The reason for this seems to be that WordPress, having realized there are no media shortcodes in the page from which you’re making the request, does not enqueue any of the pertinent scripts or styles for the mediaelement players. So, enqueue them properly:

wp_enqueue_style( 'wp-mediaelement' );
wp_enqueue_script( 'wp-playlist' );

Return to the single post template, and we’ll replicate our conditional check in the header, this time in the footer, to inject the necessary script templates for playlists, and calls to reinitialize the player when the content is loaded by our request (but return the normal footer if the single post template is loaded directly):

<?php if(!isset($_GET['ajax'])) { get_footer(); } else { ?>

  <script type="text/html" id="tmpl-wp-playlist-current-item">
    <# if ( data.image ) { #>
    <img src="{{ data.thumb.src }}" alt="" />
    <# } #>
    <div class="wp-playlist-caption">
      <span class="wp-playlist-item-meta wp-playlist-item-title">&#8220;{{ data.title }}&#8221;</span>
      <# if ( data.meta.album ) { #><span class="wp-playlist-item-meta wp-playlist-item-album">{{ data.meta.album }}</span><# } #>
      <# if ( data.meta.artist ) { #><span class="wp-playlist-item-meta wp-playlist-item-artist">{{ data.meta.artist }}</span><# } #>
    </div>
  </script>
  <script type="text/html" id="tmpl-wp-playlist-item">
    <div class="wp-playlist-item">
      <a class="wp-playlist-caption" href="{{ data.src }}">
        {{ data.index ? ( data.index + '. ' ) : '' }}
        <# if ( data.caption ) { #>
          {{ data.caption }}
        <# } else { #>
          <span class="wp-playlist-item-title">&#8220;{{{ data.title }}}&#8221;</span>
          <# if ( data.artists && data.meta.artist ) { #>
          <span class="wp-playlist-item-artist"> &mdash; {{ data.meta.artist }}</span>
          <# } #>
        <# } #>
      </a>
      <# if ( data.meta.length_formatted ) { #>
      <div class="wp-playlist-item-length">{{ data.meta.length_formatted }}</div>
      <# } #>
    </div>
  </script>

  <script type="text/javascript">
  jQuery(document).ready(function($){
    
    //reinit any media playlists
    $('.wp-playlist').each(function(){
      return new WPPlaylistView({ el: this });
    });

    //reinit standalone audios and videos
    //selector and filter string taken out of wp-includes/js/mediaelement/wp-mediaelement.min.js
    $(".wp-audio-shortcode, .wp-video-shortcode").not(".mejs-container").filter(function(){return!$(this).parent().hasClass(".mejs-mediaelement")}).mediaelementplayer();

  });
  </script>
<?php } ?>

While this implementation is inherently a bit fragile – future WordPress core updates might make this obsolete, or just break it – it works for now, and there doesn’t seem to be a lot about it online, so hopefully this helps someone. Thanks for reading, now get out of here.

3 responses to “Using WordPress media shortcodes (audio, video, playlist) powered by mediaelement.js in ajax loaded content.”

  1. Nice. I am using angularjs to output the playlist shortcode in the front end. I have load all the necessary scripts and style. But it is not working. Could you show me how you loaded the content via ajax? Thanks.

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.