Css only carousel dynamic width

Carousels are common features in pages and apps we use every day. As frontend developers we need to build them efficiently and to work across a range of devices and browsers. if we can do it without Javascript then even better!

The main features of a carousel are:
- multiple items in a list
- list has hidden content which can be scrolled into view
- items are the width of the carousel view or are the width of their own content
- we can have links to show each item in the view
- the carousel should work cross device and browser
- touch or mouse operated

So lets start with some simple html:



<div class="carousel">
    <ul>
        <li>
            <img src="image.jpg" />
            <span>Text</span>
        </li>
        <li>
            <img src="image.jpg" />
            <span>Text</span>
        </li>
        <li>
            <img src="image.jpg" />
            <span>Text</span>
        </li>
    </ul>
</div>

and some css to make the items flow using their content size, so multiple items are seen at the same time:

.carousel {
    overflow: auto;
    padding: 10px;
}

.carousel ul {
    display: table;
    table-layout: fixed;
}

.carousel li {
    display: table-cell;
    vertical-align: top;
    padding: 0 10px 0 0;
}

or alternatively if you would like to size the carousel items to fit the view, so only one is visible at one time:

.carousel {
    overflow: auto;
    padding: 10px;
}

.carousel ul {
    white-space: nowrap;
}

.carousel li {
    width: 100%;
    display: inline-block;
    white-space: normal;
    vertical-align: top;
}

.carousel img {
    float: left;
}

.carousel span {
    clear: left;
    float: left;
}

This is a great solution that works down to IE8 and Firefox 3.6. If you would like to support IE7 you should float the items rather than using display inline-block.

Also we would like to add links to jump to certain slides within the content area. This is also achievable without JavaScript, we can use anchor tags. Here is an example of paging/item navigation html:

<div>
    <ul>
        <li><a href="#item1">Item 1</a></li>
        <li><a href="#item2">Item 2</a></li>
        <li><a href="#item3">Item 3</a></li>
    </ul>
</div>

and we also need to update our carousel html to have id's when the anchor tags point to:

<div class="carousel">
    <ul>
        <li id="item1">
            <img src="image.jpg" />
            <span>Text</span>
        </li>
        <li id="item2">
            <img src="image.jpg" />
            <span>Text</span>
        </li>
        <li id="item3">
            <img src="image.jpg" />
            <span>Text</span>
        </li>
    </ul>
</div>

Now when you click the nav items the browser will try to show the item in the scroll area, vertically and horizontally. This works really well except for when the content is already slightly in view. One way to improve the experience is to show the user which slide they have selected is to use the target class:

:target { outline: solid red }

You can see a full working example of it here:
http://jsfiddle.net/kmturley/6yp6Q/5/

Updated example with CSS3 animations:
http://jsfiddle.net/kmturley/fs6wge3f/1/

Simple php templating, keeping your views passive

For some projects you will need to render a page with data values, but not want to include an entire php templating library.

One of the methods you can use is to use php as a templating language itself:

<?php  $item = array('url' => 'projects', 'name' => 'Projects'); ?>
<li><a href="<?php echo $item['url']; ?>"><?php echo $item['name']; ?></a></li>

This is great and one of the most efficient way to render a page quickly. However the more you use this, you find your html and php becoming mixed together. The result is confusing to read, update and maintain.

The second problem with php templates is that you are encouraged to put logic in your views. This is bad because views should not know anything about the data they rely on. Views should be passive and your data should be mapped correctly to match it.

By implementing a template language you can separate your html template views from your php logic, your html templates can then be reused across any platform javascript, php, python etc

If you want a full templating solution I suggest looking at Twig for php. However this can also encourage you to put logic in your views. So for our example we will implement our own single line templating solution which will force you to render your view with the correct data!

Item.html
<li><a href="{{ item.url }}">{{ item.name }}</a></li>

View.php
$item = array('url' => 'projects', 'name' => 'Projects');
$url = 'Item.html';
echo preg_replace('/\{{ item.([^\{]{1,100}?)\}}/e', '$item[$1]', file_get_contents($url));

This uses RegEx to match all instances of {{ item. }} and replace them with the matching object data.

If we would like to use that for multiple pages we can create it into a reusable class:


Item.html
<li><a href="{{ item.url }}">{{ item.name }}</a></li>

View.php

class View {
public function render($model, $template) {
$html = '';
if (gettype($model) == 'array') { foreach ($model as $item) { $html .= $this->replace($item, $template); } }
else { $html .= $this->replace(array('data' => $model), $template); }
return $html;
}
    private function replace($item, $template) {
return preg_replace('/\{{ item.([^\{]{1,100}?)\}}/e', '$item[$1]', $template);
}
}

Load.php

class Loader {
private $templates = array();

public function load($name, $url) {
if (!$this->templates[$name]) { $this->templates[$name] = file_get_contents($url); }
return $this->templates[$name];
}
}


Main.php
$item = array('url' => 'projects', 'name' => 'Projects');


require('Loader.php');
require('View.php');


$loader = new Loader();
$template = $loader->load('Item', 'Item.html');

$view = new View();
$view->render($item, $loader->load($item, $template);

You can then extend these classes to render loops of templates or save modules and embed them inside other modules. I've created an example of nested module templates loading from csv configuration files at:
https://github.com/kmturley/php-simple-templates

Sharing Server side and Client side templates

Often when creating a module or gadget you will want to render something server-side, then as the module loads with JavaScript, update the html with the new data/logic.

This presents a few questions:

- will the data load once, or refresh?
- is the data already embedded in the page?
- could the data take time to load?
- will we know that before creating our module?

- if data is coming from a javascript ajax request, will the html be empty until it loads?
- should we show a loader icon while this is happening?
- what if the server wants to render the module with some data and not load a feed?
- do we need a server-side template and then a javascript template

So many questions that prevent us from completing our module, and potentially could also force us to make changes to it later down the line as the functionality changes. I've come across this problem many times and come to a great solution.

Sharing server-side and client-side templates
Your html template should be the same for both server-side and client-side which means you need to pick the same library for both or implement the same custom template code. For this example i'm going to be using:

- Jinja for python,
- Twig for php
- Twig.js for JavaScript

A normal html file with div for javascript to attach to, javascript template and data would look something like this:

Main.html

<div id="list1" class="list"></div>


<script id="tmpl" type="text/template">
    <ul>
        {% for item in items %}
            <li><a href="{{ item.url }}">{{ item.name }}</a></li>
        {% endfor %}
    </ul>
</script>

<script>
    var list1 = new List('list1', [{'name': 'Item 1', 'url': 'http://www.google.co.uk'}]);
</script>


This is bad because html is combined with javascript script tags. The key is to separate the two cleanly.

Main.html
<div id="list1" class="list">
    {% include 'html/modules/Item.html' %}
</div>
<script id="tmpl" type="text/template">
    {% include 'html/modules/Item.html' %}
</script>


<script>
    var list1 = new List('list1', [{'name': 'Item 1', 'url': 'http://www.google.co.uk'}]);
</script>



List.html

  <ul>
  {% for item in items %}
    <li><a href="{{ item.url }}">{{ item.name }}</a></li>
  {% endfor %}
  </ul>

Now you can keep three files for each module which makes developing really easy:
List.html
List.css
List.js

Automated modules
Patterns start to emerge from writing our code this way, which allows us to automate processes. Lets output the modules dynamically:

Main.html

{%
    set modules = [
        { 'name': 'List', 'id': 'list1', 'data': [{'name': 'Item 1', 'url': 'http://www.google.co.uk'}] },
        { 'name': 'List', 'id': 'list2', 'data': [{'name': 'List 1', 'url': 'http://www.google.com'}] }
    ]
%}

{% for item in modules -%}

    <div id="{{ item.id }}" class="list">
        {% include 'html/modules/'~item.name~'.html' %}
    </div>
    <script id="tmpl" type="text/template">
        {% include 'html/modules/'~item.name~'.html' %}
    </script>


    <script>
        var {{ item.id }} = new {{ item.name }}('{{ item.id }}', {{ item.data }});
    </script>
{% endfor -%}

By outputting automatically you can keep all of your modules consistent while developing and ensure the all conform to the same structure.

An added benefit is that your backend developer can use you object to create the model for the CMS. He has all of the fields that the view requires to be rendered.

Saves a world of pain too when the data needs to be updated via ajax instead of server side.




Javascript modules with custom events

At some point when writing javascript modules you will want to start passing data between the modules.

1) One method is to hard wire the names of the callbacks but this creates dependancies and is obviously terrible e.g.

function load(data) {
    // do something here
    example.complete(data);
}

2) The second method is to pass through your callback through the chain until it's required e.g.


function load(data, callback) {
    // do something here
    callback(data);
}
function complete(items) {
    console.log('complete', items);
}

load({example:'test'}, complete);

However this is a one to one relationship, e.g. one callback to one function call. So if you want to pass the data through to multiple parts of your application then you would need a parent function which links them all together.

3) The ideal solution is to have a publish/subscribe pattern which allows you to subscribe to an event and receive updates whenever it's called. This can push updated to any number of callbacks and can be added and removed dynamically  giving you full control over your modules and events.

Here's a module that does exactly that, by saving every callback in an object using the event name as a key:


define('Events', {
    events: {},
    addEvent: function(name, callback) {
        if (!this.events[name]) { this.events[name] = []; }
        this.events[name].push(callback);
    },
    removeEvent: function(name, callback) {
        if (this.events[name]) {
            if (callback) {
                for (var i=0; i<this.events[name].length; ++i) {
                    if (this.events[name][i] === callback) { this.events[name].splice(i, 1); return true; }
                }
            }
            else { delete this.events[name]; }
        }
    },
    dispatchEvent: function(name, data) {
        if (this.events[name]) {
            for (var i=0; i<this.events[name].length; ++i) {
                this.events[name][i]({ data: data, target: this, type: name});
            }
        }
    }
});


If every module you create contains these functions you can chain any module to another using the following pattern. e.g.


define('Example', {
    extend: 'Events',
    init: function() {
        this.items = {item: 'testdata'};
    },
    load: function() {
        this.dispatchEvent('complete', this.items);
    }
});



var example = new Example('example1', {param: 'options'});
example.addEvent('complete', function(e) {
    console.log('oncomplete removed', e);
});

example.removeEvent('complete');

example.addEvent('complete', function(e) {
    console.log('oncomplete added again', e);
});
example.load();

Easy to use event creation, deletion and dispatching in 24 lines of code. Here's a working full example using my base module class:
https://github.com/kmturley/js-modules