Multiple images upload Theme Customizer control – 04

I’ve took my time to get in touch with jQuery, Backbone and the whole company and am pretty much satisfied by the job I’ve made on my multiple image theme customizer control; I’ve packed it into my libraries to increase its usability and the sometimes strange code is mainly due to that.

Queuing properly

Previous version of the control did print its CSS and JS components inline in the control markup. That’s easily removed using the WP_Customize_Control::enqueue method

namespace tad\customizer\controls;

use tad\utils\Script as Script;

if (!class_exists('\WP_Customize_Control')) {
    return null;

class MultiImageControl extends \WP_Customize_Control
    public $type = 'multi-image';
    protected $inputId = '';
    protected $thumbnailsId = '';

    public function __construct($manager, $id, $args = array())
        parent::__construct($manager, $id, $args);
        $this->inputId = $this->type . '-control-' . $this->id;
        $this->thumbnailsId = $this->inputId . '-thumbnails';

    public function enqueue()
        $jsPath = TADLIBS_ASSETS_URL . '/js/multi-image.js';
        wp_enqueue_script('mutli-image-control', Script::suffix($jsPath), array('jquery'));
        $cssPath = TADLIBS_ASSETS_URL . '/css/multi-image.css';
        wp_enqueue_style('mutli-image-control', Script::suffix($cssPath));


Some interactivity

The basic version of the previous script did save the images selected in the Media Uploader but did not show any changes until the whole Theme Customizer page was refreshed: I’ve updated the script to allow some more interactivity

/*global jQuery, document, wp, console*/

jQuery(document).ready(function($) {
    "use strict";
    var file_frame, thumbnails;

    // clicking the upload button will open the media frame
    // and update the input field value
    jQuery('a.multi-images-upload').each(function() {

        var button = jQuery(this),
            inputId ='store');

        // clicking the upload button will open the media manager
        button.on('click', function(evt) {


            // file frame already created, return
            if (file_frame) {

            // create the file frame
            file_frame = ={
                title: jQuery(this).data('uploader_title'),
                button: jQuery(this).data('uploader_button_text'),
                multiple: true,
                library: {
                    type: 'image'

            // get the selected attachments when user confirms selection
            file_frame.on('select', function(evt) {
                var selected = file_frame.state().get('selection').toJSON(),
                    store = jQuery(inputId),
                    urls = [];

                for (var i = selected.length - 1; i >= 0; i--) {

                // triggering change will activate the Save & Close button 

                // update the thumbnails using the new images
                store.trigger('updateThumbnails', {
                    urls: urls
            // open the file frame

    // remove all images when the remove images button is pressed
    jQuery('a.multi-images-remove').each(function() {
        var button = $(this),
            input = jQuery('store'));

        button.on('click', function(evt) {

            // update the hidden input value and save
            // also refresh the thumbnails list
            input.trigger('updateThumbnails', {
                urls: ''

    //update the images when the input value changes
    jQuery('input.multi-images-control-input').each(function() {

        var input = jQuery(this),
            thumbContainer = jQuery('thumbs-container'));

        input.on('updateThumbnails', function(evt, args) {
            var urls = args.urls;
            // remove old images
            // for each image url in the value create and append an image element to the list
            for (var i = urls.length - 1; i >= 0; i--) {
                var li = $('<li/>');
                li.attr('style', 'background-image:url(' + urls[i] + ');');
                li.attr('class', 'thumbnail');
                li.attr('data-src', urls[i]);


I’ve used the data-something attribute extensively in the code to avoid convolute function parameters and the two MultiImageControl methods below will show

public function theButtons()
        <input type="hidden" value="<?php echo $this->value(); ?>" <?php $this->link(); ?> id="<?php echo $this->inputId; ?>" data-thumbs-container="#<?php echo $this->thumbnailsId; ?>" class="multi-images-control-input"/>
        <a href="#" class="button-secondary multi-images-upload" data-store="#<?php echo $this->inputId; ?>">
            <?php echo 'Upload'; ?>
        <a href="#" class="button-secondary multi-images-remove" data-store="#<?php echo $this->inputId; ?>" data-thumbs-container="#<?php echo $this->thumbnailsId; ?>">
           <?php echo 'Remove images'; ?>
public function theUploadedImages($srcs = array())
    <div class="customize-control-content">
        <?php if (is_array($srcs)): ?>
            <ul class="thumbnails" data-store="#<?php echo $this->inputId; ?>" id="<?php echo $this->thumbnailsId; ?>">
                <?php foreach ($srcs as $src): ?>
                    <li class="thumbnail" style="background-image: url(<?php echo $src; ?>);" data-src="<?php echo $src; ?>">
                <?php endforeach; ?> 
        <?php endif; ?>

I will make the list sortable next and make more UI adjustments to make the control, that might be used by uncaring users, as easy to use as possible.

Code on GitHub

Actual code is available online.