Blog (my web musings)

Find out what's interesting me, get tips, advice, and code gems that don't fit elsewhere.


Search Blog


Last year I wrote about my digital signage work and the mechanics of how I displayed news and updates on a web page in a Simple Sweep-in News Scroller (CSS3/JS). The web page was displayed on a large TV screen mounted in a lobby / reception area, with several more visible around the grounds. At the time, I didn't explain that along with a web page of scrolling updates, there were many additional web pages that all displayed on a cycle, with each page redirecting to the next after a few minutes had passed. I used a simple countdown and redirect script, but the same sort of thing could also have been accomplished with a meta refresh tag;

<meta http-equiv="refresh" content="30; URL=another-page.htm">

Although this approach was fine back then, it has since created a few problems with scheduling, because there is no easy way to load in new screens and manage ad-hoc content from one location; The "schedule" is scripted on a screen-to-screen basis with each URL hardcoded into the preceding web page, so this leads the admin on a "paperchase" when editing the display order.

I decided to go back to the drawing board to write a self-contained 'SiteShow' script.

Requirements

  1. Manage all pages from one location (including display order and ad-hoc additions)
  2. Pages must have individual display duration / timings
  3. A way for admins to pause / play / navigate screen content to test new schedules

Fullscreen 'SiteShow' with Controls (no jQuery):

Demo

Displaying URLs as a 'SiteShow'

As luck would have it, I had recently revisited the earlier sweep-in news scroller and furnished it with navigation controls - Sweep-in News Scroller with Controls - and I thought this would be the perfect basis for my latest project, but instead of using the script to reveal a new list item of text into the scroller, I would use it to reveal a new web page into an iFrame.

One main page, containing a list of URLs, and an iFrame in which to display them, satisfies point 1 from my list of requirements. Additionally, having the 'SiteShow' script run from instructions that come from the menu markup would also go part-way to solving point 3;

<ul> 
    <li data-src="daily-news.htm">Daily News</li>
    <li data-src="contact-us.htm">Contact Us</li>
    <li data-src="opening-hours.htm">Opening Hours</li>
</ul>

Note how each web page URL is defined inside a data-src attribute of a list item. With this in mind I took the script from the Sweep-in News Scroller with Controls page and, with a few minor alterations, passed this data-src attribute to the iFrame src within the show() function;

(function(){ 
    var qselector = '#menu ul',
        time = 5000,
        item = document.querySelectorAll(qselector + ' > li'), // each li as menu item
        int = setInterval(next, time), i = 0, go = 1,
        content = document.getElementById('content'),
        pause = document.querySelector(qselector + ' + .controls .pause');

    function show(num){
       i = (num + item.length) % item.length;
       content.src = item[i].getAttribute('data-src');
       }
    function stop(){
       go = 0; clearInterval(int);
       pause.innerHTML = 'Play';
       }
    function start(){
       go = 1; int = setInterval(next, time);
       pause.innerHTML = 'Pause';
       }
    function next(){ show(i+1); }
    function prev(){ show(i-1); }
    pause.addEventListener("click", function(){ go ? stop() : start(); });
    document.querySelector(qselector + ' + .controls .next').addEventListener("click", function(){ next(); stop(); });
    document.querySelector(qselector + ' + .controls .prev').addEventListener("click", function(){ prev(); stop(); });

    content.src = item[0].getAttribute('data-src'); // load 1st item
})();

So, with very little effort, I'd quickly achieved most of what I set out to do. Here's a basic demo of how that looks so far.

Individual 'Slide' duration

The next challenge was to tackle point 2 on my list of requirements and introduce individual timings / durations for each page, so again, I opted for another data attribute on each menu list item - this time called data-sec;

<ul> 
    <li data-sec="5" data-src="daily-news.htm" >Daily News</li>
    <li data-sec="10" data-src="contact-us.htm" >Contact Us</li>
    <li data-sec="12" data-src="opening-hours.htm" >Opening Hours</li>
</ul>

I also revised the script so that it initially set a 'time' variable from the 1st items data-sec attribute and passed it as the time interval to the 'int' variable;

(function(){ 
    var qselector = '#menu ul',
        item = document.querySelectorAll(qselector + ' > li'), // each li as menu item
        time = item[0].getAttribute('data-sec') * 1000,
        int = setInterval(next, time), i = 0, go = 1,
        content = document.getElementById('content'),
        pause = document.querySelector(qselector + ' + .controls .pause');

    function show(num){
       clearInterval(int);
       i = (num + item.length) % item.length;
       time = item[i].getAttribute('data-sec') * 1000;
       int = setInterval(next, time);
       content.src = item[i].getAttribute('data-src');
       }
    function stop(){
       go = 0; clearInterval(int);
       pause.innerHTML = 'Play';
       }
    function start(time){
       go = 1; int = setInterval(next, time); next();
       pause.innerHTML = 'Pause';
       }
    function next(){ show(i+1); }
    function prev(){ show(i-1); }
    pause.addEventListener("click", function(){ go ? stop() : start(time); });
    document.querySelector(qselector + ' + .controls .next').addEventListener("click", function(){ next(); stop(); });
    document.querySelector(qselector + ' + .controls .prev').addEventListener("click", function(){ prev(); stop(); });

    content.src = item[0].getAttribute('data-src'); // load 1st item
})();

Now, because the time interval must change for each slide, it must be cleared first in the show() function, where it can then be reset by a new data-sec attribute value, and set as a new time interval. Here's another basic demo to see the individual timing changes in effect.

Requirement 2 solved! Now to tidy-up requirement 3...

Admin menu

The admin menu was mostly working fine at this stage, in the sense that we can navigate back and forth and pause / play the active 'slide', but the list items don't actually do anything. I decided to add a small loop to turn the list of titles into clickable links;

for (var li = 0; li < item.length; ++li){ 
    item[li].addEventListener("click", function(){ stop(); content.src = this.getAttribute('data-src'); });
    }

Again, I'm using the data-src attribute of the clicked list item and passing it to the iFrame's src.

At this stage, all requirements have been satisfied, but the job isn't quite finished. Visually it looks very dry, and of course there is a small amount of CSS needed to size the iFrame to fullscreen, and make the admin menu look better. That's been done in the final demo, along with a fade-in mask (a technique used in my content filter scripts: single selection, multi-selection) to add a bit of polish to the whole thing.

Grab the complete 'SiteShow' script, HTML markup and CSS from the source of the demo.

Demo

I hope you find the script useful. When projected through a TV in your lobby or reception area it can make for a very eye-catching and informative visitor display.

Caveats

Because this script displays URLs through an iFrame, please bear in mind that if a "break out of frames" script is present in one of the displayed web pages, it will break the SiteShow.

Extras

Learn how to adapt the scripts above to schedule in additional slides at specific times, days or dates. There's a small modification to the JavaScript, plus a few snippets of PHP to insert additional slides whenever you want.