I'm working on a Wordpress theme for a website that makes heavy use of its default media player. In the process of styling its playlists, I found that a core file injects hardcoded characters that can't be made invisible just by using CSS, so I'm a bit clueless about how to hide/remove them.

These characters are injected on 3 different places on each playlist entry:

  1. those entries are numbered, but the container is a div instead of an ol, so using list-style-type: none is not possible;
  2. they add curly quotes to the song title, but not by using :before and :after;
  3. and they add an em dash before the artist name, also not by using the :before pseudo-class.

On wordpress\wp-includes\js\media.php there's a function called wp_underscore_playlist_templates which declares the following 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"><?php
                /* translators: playlist item title */
                printf( _x( '&#8220;%s&#8221;', 'playlist item title' ), '{{{ data.title }}}' );
            <# if ( data.artists && data.meta.artist ) { #>
            <span class="wp-playlist-item-artist"> &mdash; {{ data.meta.artist }}</span>
            <# } #>
        <# } #>
    <# if ( data.meta.length_formatted ) { #>
    <div class="wp-playlist-item-length">{{ data.meta.length_formatted }}</div>
    <# } #>

Of course I could edit this core file, but I don't want to do that. So what should I do? Add a filter on functions.php? Make a JS file?

2 Answers 2


Inside the shortcode function for playlists, there is this line:

do_action( 'wp_playlist_scripts', $atts['type'], $atts['style'] );

Hooked into that is wp_playlist_scripts() which hooks the templates into the footer:

add_action( 'wp_footer', 'wp_underscore_playlist_templates', 0 );
add_action( 'admin_footer', 'wp_underscore_playlist_templates', 0 );

So if you want to replace the templates, you can hook into wp_playlist_scripts after those hooks have been added (so any priority greater than 10), remove the hooks, then hook in your own templates:

function wpse_296966_hook_new_playlist_templates() {
    // Unhook default templates.
    remove_action( 'wp_footer', 'wp_underscore_playlist_templates', 0 );
    remove_action( 'admin_footer', 'wp_underscore_playlist_templates', 0 );

    // Hook in new templates.
    add_action( 'wp_footer', 'wpse_296966_underscore_playlist_templates', 0 );
    add_action( 'admin_footer', 'wpse_296966_underscore_playlist_templates', 0 );
add_action( 'wp_playlist_scripts', 'wpse_296966_hook_new_playlist_templates', 20 );

Then you just need to copy the wp_underscore_playlist_templates() function, in its entirety, to a new function in your theme/plugin called wpse_296966_underscore_playlist_templates() (or whatever you want, just needs to match the add_action() call. Then make any modifications you want to the function to get the markup you wanted.

I would avoid doing anything too drastic though, since the scripts likely depend on specific classes, and the general structure. But removing these unwanted characters shouldn't be a problem.

  • It worked perfectly. I should only note the function you wrote above is already called wpse_296966_hook_new_playlist_templates(), so the other new function needed a different name to prevent an error.
    – Gui Odai
    Commented Mar 16, 2018 at 9:23
  • I did give it a different name. Commented Mar 16, 2018 at 9:36
  • 1
    True, my bad. I did something wrong, got a conflict error, renamed it and everything went fine.
    – Gui Odai
    Commented Mar 16, 2018 at 9:41

For the title in question "they add curly quotes to the song title, but not by using :before and :after;" and other kind of templates using gettext calls, you can filter the javascript templates like this in your functions.php:

add_filter('gettext_with_context', function($translated, $text, $context, $domain){
    if($context = 'playlist item title' && $text == '&#8220;%s&#8221;') $translated = "%s";
    return $translated;
}, 10, 4);

Remark the filter name, its differ from context _x( calls vs __( calls.

/If someone struggles with same issues

  • Nice - this works for getting rid of the curly quotes if that's all you need to do. I ended up going with the full template override because I also wanted to target the track number ({{ data.index ? ( data.index + '. ' ) : '' }}). Pretty sad that element for the index is not wrapped in a span so we could target it with CSS. Commented Aug 23, 2020 at 0:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.