Skip to content

Creating a Sweet Content Ticker for WordPress

TL:DR:
When I realised this post was getting a bit long I just published a plugin version of this code on GitHub which you can access by following the link below.

https://github.com/bambattajb/wp-sweet-ticker

If notice any bugs/have issues/have feature requests please add them to the GitHub repository. It may be added to the WordPress plugin directory if it’s proves to be useful and is rich enough in features.

If you’re interested in reading about how it came about, read on…

Recently my agency had a need to develop a simple content ticker that rotates rows of content set by the content administrator. Content tickers are a bit more scarce now than they used to be. The old days of the ‘Marquee’ tag are long gone and it’s just not cool to have scrolling content anymore.

Or is it? Now with portable devices and different screen widths in some cases, with very little space could a ticker occupying one row save space as well as getting the point across? As a small agency that deal with a lot of publishers finding space for things is always a problem.

So, we decided to go ahead and I am going to share the results with you.

The first part will focus on the set up, bootstrap and the admin facing functionality. The next one will focus on displaying the ticker on the front end.

The Solution

We are building a ticker that has the ability to overflow the container so that it only occupies one narrow row. There are 2 instances in which this row will rotate.

  1. If the content in the row is overflowing, it will scroll left until it reaches the end of the content, then display then next row
  2. If the content in the row is not overflowing, it will wait (nth) seconds, then display the next row

Both instances give the user the time to read the content before moving on.

On the admin side, we are adding the ability to add and remove rows on-the-fly. The rows are sortable and each row is editable.

Setting up

We’re going to write code both on the client and admin sides, so let’s set that up.

./sweet-ticker
  --sweet-ticker.php 

  ./client
    --sweet-ticker-client.php 
    ./js
      --sweet-ticker-client.js 
    ./css
      --sweet-ticker-client.css 

  ./admin
    --sweet-ticker-admin.php 
    ./js
      --sweet-ticker-admin.js 
    ./css
      --sweet-ticker-admin.css 

So let’s do our usual bootstrapping to get all this to work.
Assuming we’re using functions.php

include_once('sweet-ticker/sweet-ticker.php');

And in ./sweet-ticker/sweet-ticker.php

<?php
include_once('client/sweet-ticker-client.php');
include_once('admin/sweet-ticker-admin.php');
 
/* 
 * We're going to be using Ajax so we need to ensure
 * ajaxurl is defined on the client side for when we
 * need to call the server
 */
add_action('wp_head','sweet_ticker_ajaxurl', 1);
function sweet_ticker_ajaxurl() {
    ?>
    <script type="text/javascript">
        var ajaxurl = '<?php echo admin_url('admin-ajax.php'); ?>';
    </script>
<?php
}

Make sure we are getting our relevant scripts and styles for both client and admin. On the admin side we’re going to be using some of jQuery UI. Sortable and Dialog. WordPress already has these libraries built in so we just need to enqueue them.
In ./sweet-ticker/admin/sweet-ticker-admin.php

<?php
/*
 * Create an admin menu page
 */
add_action( 'admin_menu', function() {
    add_menu_page(
        'Ticker',     // Page Title
        'Ticker',     // Menu Title
        'manage_options', // Capability
        'sweet-ticker', // Menu Slug
        'sweet_ticker_menu_page', // Callback Function
        '', // Icon URL
        6 // Position
    );
});
 
/*
 * Load the admin scripts
 */
function sweet_ticker_enqueue_admin_scripts() {
    wp_enqueue_script( 'jquery-ui' );
    wp_enqueue_script( 'jquery-ui-sortable' );
    wp_enqueue_script( 'jquery-ui-dialog' );
    wp_enqueue_script( 'sweet-ticker-admin-script', get_template_directory_uri() . '/sweet-ticker/admin/js/sweet-ticker-admin.js', array( 'jquery' ), '1.0.0', false );
}
 
/*
 * We only want to load these scripts if we're in the sweet-ticker admin page
 */
if(isset($_GET['page']) && $_GET['page']=='sweet-ticker') {
    add_action( 'admin_enqueue_scripts', 'sweet_ticker_enqueue_admin_scripts' );
}
 
/*
 * Load the admin styles
 * I'm just using jQuery Pepper Grinder theme 
 */
function sweet_ticker_enqueue_admin_styles() {
    wp_enqueue_style('jquery-ui-css',
        'https://code.jquery.com/ui/1.11.4/themes/pepper-grinder/jquery-ui.css',
        false,
        '1.0.0',
        false);
 
    wp_enqueue_style( 'sweet-ticker-admin-style', get_template_directory_uri() . '/sweet-ticker/admin/css/sweet-ticker-admin.css', array('jquery-ui-css'), '1.0.0', 'all' );
}
 
/*
 * We only want to load these styles if we're in the sweet-ticker admin page
 */
if(isset($_GET['page']) && $_GET['page']=='sweet-ticker') {
    add_action( 'admin_enqueue_scripts', 'sweet_ticker_enqueue_admin_styles' );
}
 
function sweet_ticker_menu_page() {
    // Admin Options Here
}

We have created an admin page when, once accessed, will load the JavaScript and CSS files we will be using for the UI.

In ./sweet-ticker/client/sweet-ticker-client.php

<?php
/*
 * Load the client scripts
 */
function sweet_ticker_enqueue_client_scripts() {
 
    /*
     * Only if you do not have jQuery. You can probably leave this commented out
     */
    //wp_register_script( 'jquery' , ("//code.jquery.com/jquery-1.11.3.min.js"), false);
    //wp_enqueue_script( 'jquery' );
 
    wp_enqueue_script( 'sweet-ticker-client-script', get_template_directory_uri() . '/sweet-ticker/client/js/sweet-ticker-client.js', array( 'jquery' ), '1.0.0', true );
}
add_action( 'wp_enqueue_scripts', 'sweet_ticker_enqueue_client_scripts' );
 
/*
 * Load the client styles
 */
function sweet_ticker_enqueue_client_styles() {
    wp_enqueue_style( 'sweet-ticker-client-style', get_template_directory_uri() . '/sweet-ticker/client/css/sweet-ticker-client.css', array(), '1.0.0', 'all' );
}
add_action( 'wp_enqueue_scripts', 'sweet_ticker_enqueue_client_styles' );

We have loaded all of our JavaScript and CSS files on the front-end that we will use for our UI.

Start on the admin side

I’m going to start developing the functionality on the admin side because we can use the rows of content we have to test on the front end. We want to ensure that the content administrator can

  • Add new rows
  • Edit content in each row
  • Sort the rows in order or priority
  • Delete unwanted rows

Let’s start by adding some boilerplate code the page. This will eventually be dynamic when we begin to fetch it from the server.

In ./sweet-ticker/admin/sweet-ticker-admin.php and under function sweet_ticker_menu_page()

<?php
function sweet_ticker_menu_page() { ?>
    <div class="wrap">
        <h1>Sweet Ticker Administration</h1>
 
        <!-- Start UI -->
        <div id="sweet-ticker-admin">
 
            <!-- Dynamic Rows List -->
            <ul class="sweet-ticker-items">
                <li class="ui-state-default">
 
                    <!-- Sortable Handle -->
                    <button class="sweet-ticker-item-sort alignleft"><span class="ui-icon ui-icon-arrow-4-diag"></span></button>
 
                    <!-- Content -->
                    <div class="content">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod</div>
 
                    <!-- Action Buttons -->
                    <button class="sweet-ticker-item-edit alignright"><span class="ui-icon ui-icon-pencil"></span></button>
                    <button class="sweet-ticker-item-remove alignright"><span class="ui-icon ui-icon-trash"></span></button>
                    <div class="clearfix"></div>
                </li>
                <li class="ui-state-default">
                    <button class="sweet-ticker-item-sort alignleft"><span class="ui-icon ui-icon-arrow-4-diag"></span></button>
                    <div class="content">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod</div>
                    <button class="sweet-ticker-item-edit alignright"><span class="ui-icon ui-icon-pencil"></span></button>
                    <button class="sweet-ticker-item-remove alignright"><span class="ui-icon ui-icon-trash"></span></button>
                    <div class="clearfix"></div>
                </li>
                <li class="ui-state-default">
                    <button class="sweet-ticker-item-sort alignleft"><span class="ui-icon ui-icon-arrow-4-diag"></span></button>
                    <div class="content">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod</div>
                    <button class="sweet-ticker-item-edit alignright"><span class="ui-icon ui-icon-pencil"></span></button>
                    <button class="sweet-ticker-item-remove alignright"><span class="ui-icon ui-icon-trash"></span></button>
                    <div class="clearfix"></div>
                </li>
            </ul>
 
            <button class="sweet-ticker-item-add alignright"><span class="ui-icon ui-icon-plus"></span></button>
 
            <div class="clearfix"></div>
        </div>
    </div>
<?php }

We are going to use 2 dialog boxes for this. Append the dialog boxes code to the `div id=”sweet-ticker-admin` element.
./sweet-ticker/admin/sweet-ticker-admin.php.

        <!-- Remove Row Dialog  -->
        <div id="sweet-ticker-dialog-remove-row" class="sweet-ticker-dialog" title="Really delete this row?">
 
            <!-- This is a placeholder div to reference an for clicked row ID later -->
            <div class="dialog-ref"></div>
 
            <p><span class="ui-icon ui-icon-alert" style="float:left; margin:0 7px 20px 0;"></span>This row will be permanently deleted and cannot be recovered. Are you sure?</p>
        </div>
 
        <!-- Edit Row Dialog -->
        <div id="sweet-ticker-dialog-edit-row" class="sweet-ticker-dialog" title="Edit this row">
            <div class="dialog-ref"></div>
            <textarea class="sweet-ticker-content"></textarea>
        </div>

That gives us all of our rows, buttons and dialogs. It just needs a little bit of style.

Add the following code to ./sweet-ticker/admin/css/sweet-ticker-admin.css

.sweet-ticker-admin .wrap {
    position:relative;
}
 
.sweet-ticker-loading {
    position:absolute;
    width:100%;
    height:100%;
    left:0;
    top:0;
    background:white;
    opacity:0.5;
    margin-left:-10px;
}
 
/* Hide Dialogs by Default */
.sweet-ticker-dialog {
    display:none;
}
 
.sweet-ticker-dialog textarea {
    width:100%;
    height:95%;
}
 
ul.sweet-ticker-items li {
    padding:10px;
}
 
ul.sweet-ticker-items .content {
    width:80%;
    float:left;
    padding:0 10px;
}
 
.clearfix {
    clear:both;
}

So that should give you something that resembles something like the following:

Screenshot from 2015-09-18 15:03:30

Adding the actions for the ticker admin management

What we decided to do was save the configuration every time a change is made. To do that we would hook into the specific events that trigger a save. For example, the admin will re-order the rows and that would trigger the save even, as would adding a new row or editing a row.

We need to bind events to our add, sort, edit and delete buttons. I have written a script as if it was a jQuery plugin as I may wrap a WordPress pluging around this code.

I’ll get straight to the code. It’s a little messy. Most of the code is wrapped in jQuery plugin code, leaving just the init part. I have just combined the plugin and the init code into one file. We will tidy this code up in the near future. Here it is.

Add the following to ./sweet-ticker/admin/js/sweet-ticker-admin.js

/* Begin Plugin */
(function($){
 
    var SweetTickerAdminLib = function(element, options) {
 
        // Global
        var elem        = $(element),
            obj         = this;
 
        // Global Modal Remove
        var dialog_remove = elem.find( "#sweet-ticker-dialog-remove-row" );
        dialog_remove.dialog({
            autoOpen    : false,
            resizable   : false,
            height      :200,
            buttons     : {
                "OK": function() {
                    $(this).dialog( "close" );
 
                    var row_id      = dialog_remove.find('.dialog-ref').data('id');
 
                    remove_sortable_row(row_id);
                    save();
                },
                Cancel: function() {
                    $(this).dialog( "close" );
                }
            }
        });
 
 
        // Global Modal Edit
        var dialog_edit = elem.find( "#sweet-ticker-dialog-edit-row" );
        dialog_edit.dialog({
            autoOpen    : false,
            resizable   : false,
            height      : 500,
            width       : 500,
            buttons     : {
                "OK": function() {
                    dialog_edit.dialog( "close" );
 
                    var textarea    = dialog_edit.find('textarea.sweet-ticker-content'),
                        row_id      = dialog_edit.find('.dialog-ref').data('id');
 
                    update_sortable_row(textarea, row_id);
                    save();
                },
                Cancel: function() {
                    dialog_edit.dialog( "close" );
                }
            }
        });
 
 
 
 
 
        /**
         *
         * Public Methods
         */
 
        // Bind sortable UI function
        this.bindSortable = function(element) {
            element.sortable({
                handle: 'button.sweet-ticker-item-sort',
                cancel: '',
                stop: function( ) {
                    save();
                }
            });
            element.disableSelection();
 
            // Add New Row
            element.parent().find('button.sweet-ticker-item-add').on('click', function() {
                add_sortable_row(element);
            });
 
            obj.dyn_actions(element);
 
        };
 
        // Refresh sortable UI function
        this.refreshSortable = function(element) {
            element.sortable("refresh");
        };
 
        // Sortable UI actions (edit, delete etc..)
        this.dyn_actions        = function(element) {
            element.find('button.sweet-ticker-item-edit').each(function() {
                $(this).unbind().on('click', function() {
                    dialog_edit.dialog('open');
 
                    // Get current content from row and place in textarea
                    var textarea_el = dialog_edit.find('textarea.sweet-ticker-content'),
                        ref_el      = dialog_edit.find('div.dialog-ref'),
                        row_li      = $(this).parents('li'),
                        content_el  = row_li.find('.content'),
                        content     = content_el.text();
 
                    ref_el.data('id', row_li.index());
                    textarea_el.val(content);
 
                });
            });
            element.find('button.sweet-ticker-item-remove').each(function() {
                $(this).unbind().on('click', function() {
 
                    dialog_remove.dialog('open');
 
                    var ref_el      = dialog_remove.find('div.dialog-ref'),
                        row_li      = $(this).parents('li');
 
                    ref_el.data('id', row_li.index());
 
                });
            });
        };
 
 
        /**
         *
         * Private Methods
         */
 
        // Add sortable row
        var add_sortable_row = function(element) {
            var html = '' +
                '<li class="ui-state-default ui-sortable-handle">' +
                '<button class="sweet-ticker-item-sort alignleft"><span class="ui-icon ui-icon-arrow-4-diag"></span></button>' +
                '<div class="content">No content yet. Click \'Edit\'</div>' +
                '<button class="sweet-ticker-item-edit alignright"><span class="ui-icon ui-icon-pencil"></span></button>' +
                '<button class="sweet-ticker-item-remove alignright"><span class="ui-icon ui-icon-trash"></span></button>' +
                '<div class="clearfix"></div>' +
                '</li>';
 
            element.append(html);
 
            obj.dyn_actions(element);
 
            return false;
        };
 
        // Update sortable row
        var update_sortable_row = function(textarea, index) {
            var new_content     = textarea.val(),
                row_content_el  = elem.find('li:eq('+index+') .content');
 
            row_content_el.text(new_content);
 
            return false;
        };
 
        // Remove sortable row
        var remove_sortable_row = function(index) {
            var row_li = elem.find('li:eq('+index+')');
 
            row_li.remove();
 
            return false;
        };
 
        // Loading
        var loading = function(status) {
 
            var html = '<div class="sweet-ticker-loading">LOADING...</div>';
 
            if(status=='wait') {
                elem.prepend(html);
            } else {
                elem.find('.sweet-ticker-loading').remove();
            }
 
            return false;
        };
 
        // Save sortable to database
        var save = function() {
 
            loading('wait');
 
            // Saving all data into wp_options as an array
            var rows    = [];
            elem.find('.sweet-ticker-items li').each(function() {
                rows.push($(this).find('.content').text());
            });
 
 
            var config  = [];
            elem.find('.sweet-ticker-config').each(function() {
                config.push({
                    'key' : $(this).data('config'),
                    'val' : $(this).val()
                })
            });
 
            var json = {
                    'rows'      : rows,
                    'config'    : config
                },
                data = {
                    'action'        : 'sweet_ticker_save_options',
                    'sweet-ticker'  : json
                };
 
 
            // Finally, save
            $.post(ajaxurl, data, function(response) {
                loading('complete');
            });
 
 
            return false;
        };
 
    };
 
 
    $.fn.stAdmin = function(options) {
        return this.each(function() {
            var element = $(this);
 
            // Return early if this element already has a plugin instance
            if (element.data('init')) return;
 
            // pass options to plugin constructor
            var stAdmin = new SweetTickerAdminLib(this, options);
 
            // Store plugin object in this element's data
            element.data('init', stAdmin);
        });
    };
 
 
})( jQuery );
/* End Plugin */
 
/* Begin Init */
(function( $ ) {
    'use strict';
 
    $(function() {
        var el = $('div#sweet-ticker-admin');
        if(el.length>0) {
 
            // Instantiate stAdmin plugin
            el.stAdmin();
            var stAdmin = el.data('init');
 
            stAdmin.bindSortable(el.find('.sweet-ticker-items'));
 
        }
    });
 
})( jQuery );
/* End Init */

I’ll do my best to explain what we’re doing here from the top to bottom.

Initiation of the dialog boxes

These are near the top. Both dialog boxes (edit and delete) are opened with an ID which is the reference of the row the admin has clicked on so that any interaction within the dialog is associated just to that row. Each dialog box invokes the save() method when OK is clicked and the function has been performed.

Public methods

This area contains all of the methods accessible from the obj. instance. It includes the binding of the sortable function where the admin can resort the rows.

In this.bindSortable() you’ll notice the save() method is called. We’re trying to save the instance to the database on demand.

this.dyn_actions binds the click events to the buttons (edit and delete.

Private methods

This area contains the methods that are only available from inside the object.

add_sortable_row() contains the HTML for a newly created row. It appends the parent element when the sweet-ticker-item-add button is clicked.

update_sortable_row() pushes any changes that the admin has edited to its associated row.

remove_sortable_row() removes a row from the list when the admin clicks the OK inside the #sweet-ticker-dialog-remove-row dialog box.

loading() stops the user from doing anything whilst the admin waits for a response from the server.

save() iterates over the list of items and saves their contents to the database, in the order tbat the admin has chosen. Further down the save() method an Ajax request is being made sending some data to the server. We have not yet created the function that sweet_ticker_save_options is mapped to.

  var json = {
    'rows'      : rows,
    'config'    : config
  },
  data = {
    'action'        : 'sweet_ticker_save_options',
    'sweet-ticker'  : json
  };
 
  // Finally, save
  $.post(ajaxurl, data, function(response) {
    loading('complete');
  });

After adding this code you should see the UI working. You can now sort the items, edit their content, delete them, add a new one. The only thing that is missing is saving.

Saving the data

Now that the JavaScript is ‘trying’ to send a message to the server, let’s create the function to map it to.

In ./sweet-ticker/sweet-ticker-admin.php add the following function somewhere in the file.

function sweet_ticker_save_options() {
 
  /* Check the correct request is being made */
  if(isset($_REQUEST['sweet-ticker'])) {
 
    /* Add the already magically serlialized array to the wp_options table */
    update_option('_sweet_ticker', $_REQUEST['sweet-ticker'], TRUE);
  }
 
  /* Kill the application */
  die();
}
/*
 * This function only needs to be accessible
 * from the admin side
 */
add_action( 'wp_ajax_sweet_ticker_save_options', 'sweet_ticker_save_options');

Remove all the code from between ul class=”sweet-ticker-items in sweet_ticker_menu_page()

  <ul class="sweet-ticker-items">
    // Items should dynamically appear here  
  </ul>

Now everything that we’re going to be saving through our own interaction is going to need to be displayed on the front end. Now that we have all this being saved to the database, let’s add a some code to our sweet ticker admin menu page to display this each time we go in there.

In ./sweet-ticker/admin/sweet-ticker-admin.php in the function sweet_ticker_menu_page().

<?php
    /* Get saved items */
    $_sweet_ticker          = get_option('_sweet_ticker');
 
    /* Default no rows */
    $_sweet_ticker_rows     = false;
 
    /* If there are rows in $_sweet_ticker */
    if($_sweet_ticker) {
        if(!empty($_sweet_ticker['rows'])) {
            $_sweet_ticker_rows = $_sweet_ticker['rows'];
        }
    }
 
    ?>
 
    <div class="wrap">
        <h1>Sweet Ticker Administration</h1>
 
        <!-- Start UI -->
        <div id="sweet-ticker-admin">
 
            <!-- Dynamic Rows List -->
            <ul class="sweet-ticker-items">
                <?php
                if($_sweet_ticker_rows) {
                    foreach($_sweet_ticker['rows'] as $row) { ?>
                        <li class="ui-state-default">
                            <button class="sweet-ticker-item-sort alignleft"><span class="ui-icon ui-icon-arrow-4-diag"></span></button>
                            <div class="content"><?php echo stripslashes($row); ?></div>
                            <button class="sweet-ticker-item-edit alignright"><span class="ui-icon ui-icon-pencil"></span></button>
                            <button class="sweet-ticker-item-remove alignright"><span class="ui-icon ui-icon-trash"></span></button>
                            <div class="clearfix"></div>
                        </li>
                    <?php }
                } else {
                    // No Rows Message?
                } ?>
            </ul>
 
            <button class="sweet-ticker-item-add alignright"><span class="ui-icon ui-icon-plus"></span></button>
 
            <div class="clearfix"></div>
 
            <!-- Remove Row Dialog  -->
            <div id="sweet-ticker-dialog-remove-row" class="sweet-ticker-dialog" title="Really delete this row?">
 
                <!-- This is a placeholder div to reference an for clicked row ID later -->
                <div class="dialog-ref"></div>
 
                <p><span class="ui-icon ui-icon-alert" style="float:left; margin:0 7px 20px 0;"></span>This row will be permanently deleted and cannot be recovered. Are you sure?</p>
            </div>
 
            <!-- Edit Row Dialog -->
            <div id="sweet-ticker-dialog-edit-row" class="sweet-ticker-dialog" title="Edit this row">
                <div class="dialog-ref"></div>
                <textarea class="sweet-ticker-content"></textarea>
            </div>
        </div>
 
    </div>

And there you have it. The basis for the management of a ticker feature in WordPress.

Getting it functioning on the front-end

We decided it would be best to fetch the ticker data from the server and store it in HTML5’s localStorage() API. Then retrieve each ticker item at intervals.

The first thing we need to do is create a function on the server side that gets the ticker items. We’re going to map and Ajax request to this function and as it will return a serialised array, we want to echo the array as stringified JSON.

Open ./client/sweet-ticker-client.php and append with the following code.

<?php
// ... Code before
 
/*
 * Get the rows
 */
function sweet_ticker_get_rows() {
    $data       = get_option('_sweet_ticker');
 
    /*
     * Strip slashes from returned array
     */
    $json       = array_map('stripslashes', $data['rows']);
 
    /*
     * We'll use ticker-req as our request var
     */
    if(isset($_REQUEST['ticker-req'])) {
        echo json_encode($json);
        die();
    }
 
    /*
     * We could just leave a default non Ajax request in here
     */
    return $json;
}
 
/*
 * Map function to Ajax
 */
add_action( 'wp_ajax_sweet_ticker_get_rows',         'sweet_ticker_get_rows');
add_action( 'wp_ajax_nopriv_sweet_ticker_get_rows',  'sweet_ticker_get_rows');

We also need some client side wrapper code to put our ticker items into. We’ll create another function to do this.

<?php
// ... Code before
 
/*
 * Display the HTML code to house the ticker items
 */
function sweet_ticker_display() {
    $html = '<div class="sweet-ticker"><div class="sweet-ticker-inner"><span class="sweet-ticker-content">&nbsp;</span></div></div>';
    echo $html;
}

Because we want to ensure the ticker is on one line and that anything overflowing horizontally will scroll, we’ll add some CSS to handle the default of this. Also, the animation will be handled by CSS transitions rather than JavaScript animation.

In ./client/css/sweet-ticker-client.css

/* No Text Wrap */
.sweet-ticker {
    display: table;
    table-layout: fixed;
    overflow:hidden;
    width:100%;
}
 
.sweet-ticker-inner {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: clip;
    word-break: break-all;
    word-wrap: break-word;
    position:relative;
    top:0;
    opacity:1;
    transition: all .5s;
    -webkit-transition: all .5s;
    -o-transition: all .5s;
    -ms-transition: all .5s;
    -moz-transition: all .5s;
}
 
.sweet-ticker-inner.queue {
    top:-50px;
}
 
.sweet-ticker-inner.out {
    opacity:0;
}

Now we want to use jQuery to fetch the results from the server, store them into localStorage and load each at set intervals from localStorage. Here is my rough jQuery code with comments to explain what each part does.

Add this to ./client/js/sweet-ticker-client.js

(function($) {
    'use strict';
 
    $(function() {
 
        /*
         * Ajax request
         */
        var get_rows_from_server = function(callback) {
 
            /*
             * Send request to the server
             */
            var data = {
                'action'        : 'sweet_ticker_get_rows',
                'ticker-req'    : 1
            };
 
            $.post(ajaxurl, data, function(response) {
                localStorage.setItem('_sweet_ticker', response);
 
                var rows = JSON.parse(response);
 
                callback(rows);
 
            });
        };
 
        var elem    = $('.sweet-ticker'),
            inner   = elem.find('.sweet-ticker-inner'),
            content = elem.find('.sweet-ticker-content'),
            id      = 0;
 
        /*
         * If the element is there...
         */
        if(elem.length>0) {
 
            /*
             * Queue item
             */
            elem.on('next', function() {
 
                /*
                 * Get rows from localStorage if they exist
                 */
                var cached_rows = localStorage.getItem('_sweet_ticker');
 
                /*
                 * Set the element to queuing content
                 */
                inner.addClass('queue');
 
                /*
                 * If row has an ID assigned, plus 1
                 */
                if(inner.attr('data-id')) {
                    id = parseInt(inner.attr('data-id'))+1;
                }
 
                /*
                 * If ID is greater than amount in database
                 * reset to 0
                 */
                if(id>=parseInt(inner.attr('data-count'))) {
                    id=0;
                }
 
 
                if(cached_rows && id!=0) {
 
                    /*
                     * If rows in localStorage
                     * If id=0 do a hard refresh from the server
                     *
                     * Allow the current content to animate away before
                     * executing next content
                     */
                    setTimeout(function() {
                        var rows = JSON.parse(cached_rows);
                        set_next_content(rows, id);
                    },500);
 
                } else {
 
                    /*
                     * Get the rows from the server
                     */
                    get_rows_from_server(function(rows) {
                        set_next_content(rows, id);
                    });
                }
 
 
            });
 
            /*
             * Trigger next initially
             */
            elem.trigger('next');
 
        }
 
        var set_next_content = function(rows, id) {
            content.html(rows[id]);
 
            inner.removeClass('queue');
            inner.attr('data-id',       id);
            inner.attr('data-count',    rows.count);
 
            ticker_container_overflow();
        };
 
        /*
         * Since text-overflow requires a width, set it
         */
        var ticker_container_width = function() {
 
            /* Temporarily remove contents */
            var contents    = content.html();
            content.html('');
 
            var width       = elem.width();
            inner.css({'width':width});
 
            content.html(contents);
 
        };
 
        $(window).resize(function() {
            ticker_container_width();
        });
        ticker_container_width();
 
 
 
        /*
         * Move ticker through overflow
         */
        var ticker_container_overflow = function() {
            var contents_width  = content.width(),
                container_width = inner.width(),
                width_diff      = parseInt(contents_width - container_width);
 
            if(contents_width<container_width) {
                /*
                 * If no need to scroll, wait 5 seconds and change
                 */
                setTimeout(function() {
                    elem.trigger('next');
                }, 5000)
            } else {
 
                /*
                 * Unfortunately, we need to set the CSS transitions like this
                 * I can't think of another way right now.
                 */
                setTimeout(function() {
 
                    content.css({
                        'margin-left' : parseInt('-'+parseInt(width_diff+20)), /* Extra 20px to cover any margins/padding */
                        'transition': 'margin-left '+parseInt(width_diff/20)+'s linear',
                        '-webkit-transition': 'margin-left '+parseInt(width_diff/20)+'s linear',
                        '-o-transition': 'margin-left '+parseInt(width_diff/20)+'s linear',
                        '-ms-transition': 'margin-left '+parseInt(width_diff/20)+'s linear',
                        '-moz-transition': 'margin-left '+parseInt(width_diff/20)+'s linear'
                    });
 
                    /*
                     * Wait for animation to finish before triggering the next queue
                     */
                    setTimeout(function() {
                        elem.trigger('next');
 
                        /*
                         * Reset after scrolling
                         */
                        setTimeout(function() {
                            content.css({
                                'margin-left' : '0',
                                'transition': 'none',
                                '-webkit-transition': 'none',
                                '-o-transition': 'none',
                                '-ms-transition': 'none',
                                '-moz-transition': 'none'
                            });
                        },500);
 
 
                        /*
                         * Don't trigger the event until after scrolling has
                         * finished and the user has enough time to view
                         */
                    }, parseInt((width_diff/20)*1000) + 2000)
 
                },1000);
            }
 
        };
 
    });
 
})( jQuery );

No by adding the function sweet_ticker_display() somewhere in one of your templates it should start revolving your ticker rows.

About 

I run a small web development agency in Salisbury, UK. We provide both front-end/back end solutions and infrastructure management. My specific role is to identify the needs of our clients and providing an online solution that is easy to administer, secure, scalable and maintainable.

 

My agency website is white-fire.co.uk. Contact us if you need a consultant.

Published inJavaScriptWordPress

Be First to Comment

Leave a Reply

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